diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage index f2a58e7b6a7ac5..c474998e6fd3db 100644 --- a/.ci/Jenkinsfile_coverage +++ b/.ci/Jenkinsfile_coverage @@ -3,7 +3,7 @@ library 'kibana-pipeline-library' kibanaLibrary.load() // load from the Jenkins instance -kibanaPipeline(timeoutMinutes: 180) { +kibanaPipeline(timeoutMinutes: 240) { catchErrors { withEnv([ 'CODE_COVERAGE=1', // Needed for multiple ci scripts, such as remote.ts, test/scripts/*.sh, schema.js, etc. diff --git a/.eslintignore b/.eslintignore index 1f22b6074e76e0..2ed9ecf971ff35 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,46 +1,48 @@ -node_modules -bower_components -/data -/optimize -/build -/target +**/*.js.snap +**/graphql/types.ts /.es -/plugins +/build /built_assets +/data /html_docs -/src/plugins/data/common/es_query/kuery/ast/_generated_/** -/src/plugins/vis_type_timelion/public/_generated_/** -src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data -/src/legacy/ui/public/flot-charts +/optimize +/plugins /test/fixtures/scenarios -/src/legacy/core_plugins/console/public/webpackShims +/x-pack/build +node_modules +target + +!/.eslintrc.js + +# plugin overrides +/src/core/lib/kbn_internal_native_observable /src/legacy/core_plugins/console/public/tests/webpackShims +/src/legacy/core_plugins/console/public/webpackShims +/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken +/src/legacy/ui/public/flot-charts /src/legacy/ui/public/utils/decode_geo_hash.js +/src/plugins/data/common/es_query/kuery/ast/_generated_/** +/src/plugins/vis_type_timelion/public/_generated_/** /src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.* -/src/core/lib/kbn_internal_native_observable -/packages/*/target -/packages/eslint-config-kibana -/packages/kbn-pm/dist -/packages/kbn-plugin-generator/sao_template/template -/packages/kbn-ui-framework/dist -/packages/kbn-ui-framework/doc_site/build -/packages/kbn-ui-framework/generator-kui/*/templates/ -/packages/kbn-test/src/functional_test_runner/__tests__/fixtures/ -/packages/kbn-test/src/functional_test_runner/lib/config/__tests__/fixtures/ -/x-pack/legacy/plugins/maps/public/vendor/** -/x-pack/coverage -/x-pack/build /x-pack/legacy/plugins/**/__tests__/fixtures/** -/packages/kbn-interpreter/src/common/lib/grammar.js +/x-pack/legacy/plugins/apm/e2e/cypress/**/snapshots.js /x-pack/legacy/plugins/canvas/canvas_plugin +/x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts /x-pack/legacy/plugins/canvas/shareable_runtime/build /x-pack/legacy/plugins/canvas/storybook -/x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts /x-pack/legacy/plugins/infra/common/graphql/types.ts /x-pack/legacy/plugins/infra/public/graphql/types.ts /x-pack/legacy/plugins/infra/server/graphql/types.ts -/x-pack/legacy/plugins/apm/e2e/cypress/**/snapshots.js -/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken -**/graphql/types.ts -**/*.js.snap -!/.eslintrc.js +/x-pack/legacy/plugins/maps/public/vendor/** + +# package overrides +/packages/eslint-config-kibana +/packages/kbn-interpreter/src/common/lib/grammar.js +/packages/kbn-plugin-generator/sao_template/template +/packages/kbn-pm/dist +/packages/kbn-test/src/functional_test_runner/__tests__/fixtures/ +/packages/kbn-test/src/functional_test_runner/lib/config/__tests__/fixtures/ +/packages/kbn-ui-framework/dist +/packages/kbn-ui-framework/doc_site/build +/packages/kbn-ui-framework/generator-kui/*/templates/ + diff --git a/.eslintrc.js b/.eslintrc.js index 4e501004c22f9d..c9b41ec711b7f9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -217,6 +217,9 @@ module.exports = { '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)/**/*', @@ -739,6 +742,101 @@ module.exports = { }, }, + /** + * Lists overrides + */ + { + // typescript and javascript for front and back end + files: ['x-pack/plugins/lists/**/*.{js,ts,tsx}'], + plugins: ['eslint-plugin-node'], + env: { + mocha: true, + jest: true, + }, + rules: { + 'accessor-pairs': 'error', + 'array-callback-return': 'error', + 'no-array-constructor': 'error', + complexity: 'error', + 'consistent-return': 'error', + 'func-style': ['error', 'expression'], + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], + 'newlines-between': 'always', + }, + ], + 'sort-imports': [ + 'error', + { + ignoreDeclarationSort: true, + }, + ], + 'node/no-deprecated-api': 'error', + 'no-bitwise': 'error', + 'no-continue': 'error', + 'no-dupe-keys': 'error', + 'no-duplicate-case': 'error', + 'no-duplicate-imports': 'error', + 'no-empty-character-class': 'error', + 'no-empty-pattern': 'error', + 'no-ex-assign': 'error', + 'no-extend-native': 'error', + 'no-extra-bind': 'error', + 'no-extra-boolean-cast': 'error', + 'no-extra-label': 'error', + 'no-func-assign': 'error', + 'no-implicit-globals': 'error', + 'no-implied-eval': 'error', + 'no-invalid-regexp': 'error', + 'no-inner-declarations': 'error', + 'no-lone-blocks': 'error', + 'no-multi-assign': 'error', + 'no-misleading-character-class': 'error', + 'no-new-symbol': 'error', + 'no-obj-calls': 'error', + 'no-param-reassign': ['error', { props: true }], + 'no-process-exit': 'error', + 'no-prototype-builtins': 'error', + 'no-return-await': 'error', + 'no-self-compare': 'error', + 'no-shadow-restricted-names': 'error', + 'no-sparse-arrays': 'error', + 'no-this-before-super': 'error', + 'no-undef': 'error', + 'no-unreachable': 'error', + 'no-unsafe-finally': 'error', + 'no-useless-call': 'error', + 'no-useless-catch': 'error', + 'no-useless-concat': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-escape': 'error', + 'no-useless-rename': 'error', + 'no-useless-return': 'error', + 'no-void': 'error', + 'one-var-declaration-per-line': 'error', + 'prefer-object-spread': 'error', + 'prefer-promise-reject-errors': 'error', + 'prefer-rest-params': 'error', + 'prefer-spread': 'error', + 'prefer-template': 'error', + 'require-atomic-updates': 'error', + 'symbol-description': 'error', + 'vars-on-top': 'error', + '@typescript-eslint/explicit-member-accessibility': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/unified-signatures': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-unused-vars': 'error', + 'no-template-curly-in-string': 'error', + 'sort-keys': 'error', + 'prefer-destructuring': 'error', + }, + }, /** * Alerting Services overrides */ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0d86726dca836c..a97400ee09c0e9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,7 @@ /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app -/src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app +/src/plugins/vis_type_vislib/ @elastic/kibana-app /src/plugins/vis_type_xy/ @elastic/kibana-app /src/plugins/vis_type_table/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app @@ -84,7 +84,6 @@ /x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest-management /x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui -/x-pack/legacy/plugins/uptime @elastic/uptime /x-pack/plugins/uptime @elastic/uptime # Machine Learning @@ -167,8 +166,6 @@ /x-pack/plugins/telemetry_collection_xpack/ @elastic/pulse # Kibana Alerting Services -/x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services -/x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services /x-pack/plugins/alerting/ @elastic/kibana-alerting-services /x-pack/plugins/actions/ @elastic/kibana-alerting-services /x-pack/plugins/event_log/ @elastic/kibana-alerting-services @@ -176,7 +173,6 @@ /x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services /x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services /x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services -/x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services /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 @@ -209,22 +205,21 @@ /x-pack/plugins/watcher/ @elastic/es-ui # Endpoint -/x-pack/plugins/endpoint/ @elastic/endpoint-app-team -/x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team -/x-pack/test/endpoint_api_integration_no_ingest/ @elastic/endpoint-app-team -/x-pack/test/functional_endpoint/ @elastic/endpoint-app-team -/x-pack/test/functional_endpoint_ingest_failure/ @elastic/endpoint-app-team -/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team -/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team -/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team +/x-pack/plugins/endpoint/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/endpoint_api_integration_no_ingest/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/functional_endpoint/ @elastic/endpoint-app-team @elastic/siem +/x-pack/test/functional_endpoint_ingest_failure/ @elastic/endpoint-app-team @elastic/siem +/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 # SIEM -/x-pack/legacy/plugins/siem/ @elastic/siem -/x-pack/plugins/siem/ @elastic/siem -/x-pack/test/detection_engine_api_integration @elastic/siem -/x-pack/test/api_integration/apis/siem @elastic/siem -/x-pack/plugins/case @elastic/siem +/x-pack/plugins/siem/ @elastic/siem @elastic/endpoint-app-team +/x-pack/test/detection_engine_api_integration @elastic/siem @elastic/endpoint-app-team +/x-pack/test/api_integration/apis/siem @elastic/siem @elastic/endpoint-app-team +/x-pack/plugins/case @elastic/siem @elastic/endpoint-app-team +/x-pack/plugins/lists @elastic/siem @elastic/endpoint-app-team # Security Intelligence And Analytics -/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules @elastic/security-intelligence-analytics /x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules @elastic/security-intelligence-analytics diff --git a/.i18nrc.json b/.i18nrc.json index 35ce7452343466..b04c02f6b22655 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -50,10 +50,10 @@ "visTypeMetric": "src/plugins/vis_type_metric", "visTypeTable": "src/plugins/vis_type_table", "visTypeTagCloud": "src/plugins/vis_type_tagcloud", - "visTypeTimeseries": ["src/legacy/core_plugins/vis_type_timeseries", "src/plugins/vis_type_timeseries"], + "visTypeTimeseries": "src/plugins/vis_type_timeseries", "visTypeVega": "src/plugins/vis_type_vega", - "visTypeVislib": "src/legacy/core_plugins/vis_type_vislib", - "visTypeXy": "src/legacy/core_plugins/vis_type_xy", + "visTypeVislib": "src/plugins/vis_type_vislib", + "visTypeXy": "src/plugins/vis_type_xy", "visualizations": "src/plugins/visualizations", "visualize": "src/plugins/visualize" }, diff --git a/.sass-lint.yml b/.sass-lint.yml index 9b31f3fae6d161..44b4d493841361 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -2,8 +2,8 @@ files: include: - 'src/legacy/core_plugins/metrics/**/*.s+(a|c)ss' - 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss' - - 'src/legacy/core_plugins/vis_type_vislib/**/*.s+(a|c)ss' - - 'src/legacy/core_plugins/vis_type_xy/**/*.s+(a|c)ss' + - 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss' + - 'src/plugins/vis_type_xy/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/canvas/**/*.s+(a|c)ss' - 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss' diff --git a/docs/development/core/public/kibana-plugin-core-public.appbase.defaultpath.md b/docs/development/core/public/kibana-plugin-core-public.appbase.defaultpath.md new file mode 100644 index 00000000000000..51492756ef2327 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.appbase.defaultpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppBase](./kibana-plugin-core-public.appbase.md) > [defaultPath](./kibana-plugin-core-public.appbase.defaultpath.md) + +## AppBase.defaultPath property + +Allow to define the default path a user should be directed to when navigating to the app. When defined, this value will be used as a default for the `path` option when calling [navigateToApp](./kibana-plugin-core-public.applicationstart.navigatetoapp.md)\`, and will also be appended to the [application navLink](./kibana-plugin-core-public.chromenavlink.md) in the navigation bar. + +Signature: + +```typescript +defaultPath?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appbase.md b/docs/development/core/public/kibana-plugin-core-public.appbase.md index b73785647f23cc..7b624f12ac1df0 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appbase.md +++ b/docs/development/core/public/kibana-plugin-core-public.appbase.md @@ -18,6 +18,7 @@ export interface AppBase | [capabilities](./kibana-plugin-core-public.appbase.capabilities.md) | Partial<Capabilities> | Custom capabilities defined by the app. | | [category](./kibana-plugin-core-public.appbase.category.md) | AppCategory | The category definition of the product See [AppCategory](./kibana-plugin-core-public.appcategory.md) See DEFAULT\_APP\_CATEGORIES for more reference | | [chromeless](./kibana-plugin-core-public.appbase.chromeless.md) | boolean | Hide the UI chrome when the application is mounted. Defaults to false. Takes precedence over chrome service visibility settings. | +| [defaultPath](./kibana-plugin-core-public.appbase.defaultpath.md) | string | Allow to define the default path a user should be directed to when navigating to the app. When defined, this value will be used as a default for the path option when calling [navigateToApp](./kibana-plugin-core-public.applicationstart.navigatetoapp.md)\`, and will also be appended to the [application navLink](./kibana-plugin-core-public.chromenavlink.md) in the navigation bar. | | [euiIconType](./kibana-plugin-core-public.appbase.euiicontype.md) | string | A EUI iconType that will be used for the app's icon. This icon takes precendence over the icon property. | | [icon](./kibana-plugin-core-public.appbase.icon.md) | string | A URL to an image file used as an icon. Used as a fallback if euiIconType is not provided. | | [id](./kibana-plugin-core-public.appbase.id.md) | string | The unique identifier of the application | diff --git a/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md b/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md index cdf9171a46aed0..3d8b5d115c8a27 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md +++ b/docs/development/core/public/kibana-plugin-core-public.appupdatablefields.md @@ -9,5 +9,5 @@ Defines the list of fields that can be updated via an [AppUpdater](./kibana-plug Signature: ```typescript -export declare type AppUpdatableFields = Pick; +export declare type AppUpdatableFields = Pick; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlink.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlink.md index 1cc1a1194a5379..a9fabb38df8696 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlink.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavlink.md @@ -29,5 +29,5 @@ export interface ChromeNavLink | [subUrlBase](./kibana-plugin-core-public.chromenavlink.suburlbase.md) | string | A url base that legacy apps can set to match deep URLs to an application. | | [title](./kibana-plugin-core-public.chromenavlink.title.md) | string | The title of the application. | | [tooltip](./kibana-plugin-core-public.chromenavlink.tooltip.md) | string | A tooltip shown when hovering over an app link. | -| [url](./kibana-plugin-core-public.chromenavlink.url.md) | string | A url that legacy apps can set to deep link into their applications. | +| [url](./kibana-plugin-core-public.chromenavlink.url.md) | string | The route used to open the [default path](./kibana-plugin-core-public.appbase.defaultpath.md) of an application. If unset, baseUrl will be used instead. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromenavlink.url.md b/docs/development/core/public/kibana-plugin-core-public.chromenavlink.url.md index 0c415ed1a7fadc..1e0b8900159930 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromenavlink.url.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromenavlink.url.md @@ -4,11 +4,7 @@ ## ChromeNavLink.url property -> Warning: This API is now obsolete. -> -> - -A url that legacy apps can set to deep link into their applications. +The route used to open the [default path](./kibana-plugin-core-public.appbase.defaultpath.md) of an application. If unset, `baseUrl` will be used instead. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md index a502c40db0cd8c..a3294fb0a087ae 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md @@ -9,22 +9,36 @@ A migration function for a [saved object type](./kibana-plugin-core-server.saved Signature: ```typescript -export declare type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; +export declare type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; ``` ## Example ```typescript -const migrateProperty: SavedObjectMigrationFn = (doc, { log }) => { - if(doc.attributes.someProp === null) { - log.warn('Skipping migration'); - } else { - doc.attributes.someProp = migrateProperty(doc.attributes.someProp); - } - - return doc; +interface TypeV1Attributes { + someKey: string; + obsoleteProperty: number; } +interface TypeV2Attributes { + someKey: string; + newProperty: string; +} + +const migrateToV2: SavedObjectMigrationFn = (doc, { log }) => { + const { obsoleteProperty, ...otherAttributes } = doc.attributes; + // instead of mutating `doc` we make a shallow copy so that we can use separate types for the input + // and output attributes. We don't need to make a deep copy, we just need to ensure that obsolete + // attributes are not present on the returned doc. + return { + ...doc, + attributes: { + ...otherAttributes, + newProperty: migrate(obsoleteProperty), + }, + }; +}; + ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md index 6d4e252fe7532e..3f4090619edbfb 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md @@ -9,5 +9,5 @@ Describes Saved Object documents that have passed through the migration framewor Signature: ```typescript -export declare type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export declare type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.md index 96f11436308569..dbc7d0ca431cec 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.md @@ -19,5 +19,6 @@ export interface SavedObjectsCoreFieldMapping | [enabled](./kibana-plugin-core-server.savedobjectscorefieldmapping.enabled.md) | boolean | | | [fields](./kibana-plugin-core-server.savedobjectscorefieldmapping.fields.md) | {
[subfield: string]: {
type: string;
};
} | | | [index](./kibana-plugin-core-server.savedobjectscorefieldmapping.index.md) | boolean | | +| [null\_value](./kibana-plugin-core-server.savedobjectscorefieldmapping.null_value.md) | number | boolean | string | | | [type](./kibana-plugin-core-server.savedobjectscorefieldmapping.type.md) | string | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.null_value.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.null_value.md new file mode 100644 index 00000000000000..627ea3695383a0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscorefieldmapping.null_value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCoreFieldMapping](./kibana-plugin-core-server.savedobjectscorefieldmapping.md) > [null\_value](./kibana-plugin-core-server.savedobjectscorefieldmapping.null_value.md) + +## SavedObjectsCoreFieldMapping.null\_value property + +Signature: + +```typescript +null_value?: number | boolean | string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md index be51400addbbc5..8e2395ee6310d8 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md @@ -9,5 +9,5 @@ Describes Saved Object documents from Kibana < 7.0.0 which don't have a `refe Signature: ```typescript -export declare type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export declare type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md index 04a0d871cab2dc..3969a97fa7789f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.gettime.md @@ -7,7 +7,10 @@ Signature: ```typescript -export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, forceNow?: Date): import("../..").RangeFilter | undefined; +export declare function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { + forceNow?: Date; + fieldName?: string; +}): import("../..").RangeFilter | undefined; ``` ## Parameters @@ -16,7 +19,7 @@ export declare function getTime(indexPattern: IIndexPattern | undefined, timeRan | --- | --- | --- | | indexPattern | IIndexPattern | undefined | | | timeRange | TimeRange | | -| forceNow | Date | | +| options | {
forceNow?: Date;
fieldName?: string;
} | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md new file mode 100644 index 00000000000000..c3998876c97121 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) > [getTimeField](./kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md) + +## IIndexPattern.getTimeField() method + +Signature: + +```typescript +getTimeField?(): IFieldType | undefined; +``` +Returns: + +`IFieldType | undefined` + 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 1bbd6cf67f0ce3..1cb89822eb605d 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 @@ -21,3 +21,9 @@ export interface IIndexPattern | [title](./kibana-plugin-plugins-data-public.iindexpattern.title.md) | string | | | [type](./kibana-plugin-plugins-data-public.iindexpattern.type.md) | string | | +## Methods + +| Method | Description | +| --- | --- | +| [getTimeField()](./kibana-plugin-plugins-data-public.iindexpattern.gettimefield.md) | | + 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 0fd82ffb2240c8..e1df493143b736 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 @@ -43,7 +43,7 @@ | [getEsPreference(uiSettings, sessionId)](./kibana-plugin-plugins-data-public.getespreference.md) | | | [getQueryLog(uiSettings, storage, appName, language)](./kibana-plugin-plugins-data-public.getquerylog.md) | | | [getSearchErrorType({ message })](./kibana-plugin-plugins-data-public.getsearcherrortype.md) | | -| [getTime(indexPattern, timeRange, forceNow)](./kibana-plugin-plugins-data-public.gettime.md) | | +| [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-public.gettime.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-public.plugin.md) | | ## Interfaces diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 7411e2df1b613c..cc3fa8c2720ded 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -37,7 +37,7 @@ const ISTANBUL_PRESET_PATH = require.resolve('@kbn/babel-preset/istanbul_preset' const BABEL_PRESET_PATH = require.resolve('@kbn/babel-preset/webpack_preset'); const STATIC_BUNDLE_PLUGINS = [ - // { id: 'data', dirname: 'data' }, + { id: 'data', dirname: 'data' }, { id: 'kibanaReact', dirname: 'kibana_react' }, { id: 'kibanaUtils', dirname: 'kibana_utils' }, { id: 'esUiShared', dirname: 'es_ui_shared' }, @@ -60,13 +60,8 @@ function dynamicExternals(bundle: Bundle, context: string, request: string) { return; } - // don't allow any static bundle to rely on other static bundles - if (STATIC_BUNDLE_PLUGINS.some(p => bundle.id === p.id)) { - return; - } - - // ignore requests that don't include a /data/public, /kibana_react/public, or - // /kibana_utils/public segment as a cheap way to avoid doing path resolution + // ignore requests that don't include a /{dirname}/public for one of our + // "static" bundles as a cheap way to avoid doing path resolution // for paths that couldn't possibly resolve to what we're looking for const reqToStaticBundle = STATIC_BUNDLE_PLUGINS.some(p => request.includes(`/${p.dirname}/public`) diff --git a/renovate.json5 b/renovate.json5 index ffa006264873d3..c0ddcaf4f23c8f 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -846,6 +846,14 @@ '@types/semver', ], }, + { + groupSlug: 'set-value', + groupName: 'set-value related packages', + packageNames: [ + 'set-value', + '@types/set-value', + ], + }, { groupSlug: 'sinon', groupName: 'sinon related packages', diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 8c5fe4875aaea2..c91c00bc1aa021 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -957,7 +957,7 @@ const migration = (doc, log) => {...} Would be converted to: ```typescript -const migration: SavedObjectMigrationFn = (doc, { log }) => {...} +const migration: SavedObjectMigrationFn = (doc, { log }) => {...} ``` ### Remarks diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index c25918c6b7328c..e29837aecb1253 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -87,7 +87,7 @@ describe('#setup()', () => { ).toThrowErrorMatchingInlineSnapshot(`"Applications cannot be registered after \\"setup\\""`); }); - it('allows to register a statusUpdater for the application', async () => { + it('allows to register an AppUpdater for the application', async () => { const setup = service.setup(setupDeps); const pluginId = Symbol('plugin'); @@ -118,6 +118,7 @@ describe('#setup()', () => { updater$.next(app => ({ status: AppStatus.inaccessible, tooltip: 'App inaccessible due to reason', + defaultPath: 'foo/bar', })); applications = await applications$.pipe(take(1)).toPromise(); @@ -128,6 +129,7 @@ describe('#setup()', () => { legacy: false, navLinkStatus: AppNavLinkStatus.default, status: AppStatus.inaccessible, + defaultPath: 'foo/bar', tooltip: 'App inaccessible due to reason', }) ); @@ -209,7 +211,7 @@ describe('#setup()', () => { }); }); - describe('registerAppStatusUpdater', () => { + describe('registerAppUpdater', () => { it('updates status fields', async () => { const setup = service.setup(setupDeps); @@ -413,6 +415,36 @@ describe('#setup()', () => { }) ); }); + + it('allows to update the basePath', async () => { + const setup = service.setup(setupDeps); + + const pluginId = Symbol('plugin'); + setup.register(pluginId, createApp({ id: 'app1' })); + + const updater = new BehaviorSubject(app => ({})); + setup.registerAppUpdater(updater); + + const start = await service.start(startDeps); + await start.navigateToApp('app1'); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1', undefined); + MockHistory.push.mockClear(); + + updater.next(app => ({ defaultPath: 'default-path' })); + await start.navigateToApp('app1'); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/default-path', undefined); + MockHistory.push.mockClear(); + + updater.next(app => ({ defaultPath: 'another-path' })); + await start.navigateToApp('app1'); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/another-path', undefined); + MockHistory.push.mockClear(); + + updater.next(app => ({})); + await start.navigateToApp('app1'); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1', undefined); + MockHistory.push.mockClear(); + }); }); it("`registerMountContext` calls context container's registerContext", () => { @@ -676,6 +708,57 @@ describe('#start()', () => { expect(MockHistory.push).toHaveBeenCalledWith('/custom/path#/hash/router/path', undefined); }); + it('preserves trailing slash when path contains a hash', async () => { + const { register } = service.setup(setupDeps); + + register(Symbol(), createApp({ id: 'app2', appRoute: '/custom/app-path' })); + + const { navigateToApp } = await service.start(startDeps); + await navigateToApp('app2', { path: '#/' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path#/', undefined); + MockHistory.push.mockClear(); + + await navigateToApp('app2', { path: '#/foo/bar/' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path#/foo/bar/', undefined); + MockHistory.push.mockClear(); + + await navigateToApp('app2', { path: '/path#/' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path#/', undefined); + MockHistory.push.mockClear(); + + await navigateToApp('app2', { path: '/path#/hash/' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path#/hash/', undefined); + MockHistory.push.mockClear(); + + await navigateToApp('app2', { path: '/path/' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom/app-path/path', undefined); + MockHistory.push.mockClear(); + }); + + it('appends the defaultPath when the path parameter is not specified', async () => { + const { register } = service.setup(setupDeps); + + register(Symbol(), createApp({ id: 'app1', defaultPath: 'default/path' })); + register( + Symbol(), + createApp({ id: 'app2', appRoute: '/custom-app-path', defaultPath: '/my-base' }) + ); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('app1', { path: 'defined-path' }); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/defined-path', undefined); + + await navigateToApp('app1', {}); + expect(MockHistory.push).toHaveBeenCalledWith('/app/app1/default/path', undefined); + + await navigateToApp('app2', { path: 'defined-path' }); + expect(MockHistory.push).toHaveBeenCalledWith('/custom-app-path/defined-path', undefined); + + await navigateToApp('app2', {}); + expect(MockHistory.push).toHaveBeenCalledWith('/custom-app-path/my-base', undefined); + }); + it('includes state if specified', async () => { const { register } = service.setup(setupDeps); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 1c9492d81c7f68..bafa1932e5e927 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -46,6 +46,7 @@ import { Mounter, } from './types'; import { getLeaveAction, isConfirmAction } from './application_leave'; +import { appendAppPath } from './utils'; interface SetupDeps { context: ContextSetup; @@ -81,13 +82,7 @@ const getAppUrl = (mounters: Map, appId: string, path: string = const appBasePath = mounters.get(appId)?.appRoute ? `/${mounters.get(appId)!.appRoute}` : `/app/${appId}`; - - // Only preppend slash if not a hash or query path - path = path.startsWith('#') || path.startsWith('?') ? path : `/${path}`; - - return `${appBasePath}${path}` - .replace(/\/{2,}/g, '/') // Remove duplicate slashes - .replace(/\/$/, ''); // Remove trailing slash + return appendAppPath(appBasePath, path); }; const allApplicationsFilter = '__ALL__'; @@ -290,6 +285,9 @@ export class ApplicationService { }, navigateToApp: async (appId, { path, state }: { path?: string; state?: any } = {}) => { if (await this.shouldNavigate(overlays)) { + if (path === undefined) { + path = applications$.value.get(appId)?.defaultPath; + } this.appLeaveHandlers.delete(this.currentAppId$.value!); this.navigate!(getAppUrl(availableMounters, appId, path), state); this.currentAppId$.next(appId); diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index 318afb652999ef..0734e178033e24 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -66,6 +66,13 @@ export interface AppBase { */ navLinkStatus?: AppNavLinkStatus; + /** + * Allow to define the default path a user should be directed to when navigating to the app. + * When defined, this value will be used as a default for the `path` option when calling {@link ApplicationStart.navigateToApp | navigateToApp}`, + * and will also be appended to the {@link ChromeNavLink | application navLink} in the navigation bar. + */ + defaultPath?: string; + /** * An {@link AppUpdater} observable that can be used to update the application {@link AppUpdatableFields} at runtime. * @@ -187,7 +194,10 @@ export enum AppNavLinkStatus { * Defines the list of fields that can be updated via an {@link AppUpdater}. * @public */ -export type AppUpdatableFields = Pick; +export type AppUpdatableFields = Pick< + AppBase, + 'status' | 'navLinkStatus' | 'tooltip' | 'defaultPath' +>; /** * Updater for applications. @@ -642,7 +652,8 @@ export interface ApplicationStart { * Navigate to a given app * * @param appId - * @param options.path - optional path inside application to deep link to + * @param options.path - optional path inside application to deep link to. + * If undefined, will use {@link AppBase.defaultPath | the app's default path}` as default. * @param options.state - optional state to forward to the application */ navigateToApp(appId: string, options?: { path?: string; state?: any }): Promise; diff --git a/src/core/public/application/utils.test.ts b/src/core/public/application/utils.test.ts new file mode 100644 index 00000000000000..7ed0919f88c614 --- /dev/null +++ b/src/core/public/application/utils.test.ts @@ -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. + */ + +import { removeSlashes, appendAppPath } from './utils'; + +describe('removeSlashes', () => { + it('only removes duplicates by default', () => { + expect(removeSlashes('/some//url//to//')).toEqual('/some/url/to/'); + expect(removeSlashes('some/////other//url')).toEqual('some/other/url'); + }); + + it('remove trailing slash when `trailing` is true', () => { + expect(removeSlashes('/some//url//to//', { trailing: true })).toEqual('/some/url/to'); + }); + + it('remove leading slash when `leading` is true', () => { + expect(removeSlashes('/some//url//to//', { leading: true })).toEqual('some/url/to/'); + }); + + it('does not removes duplicates when `duplicates` is false', () => { + expect(removeSlashes('/some//url//to/', { leading: true, duplicates: false })).toEqual( + 'some//url//to/' + ); + expect(removeSlashes('/some//url//to/', { trailing: true, duplicates: false })).toEqual( + '/some//url//to' + ); + }); + + it('accept mixed options', () => { + expect( + removeSlashes('/some//url//to/', { leading: true, duplicates: false, trailing: true }) + ).toEqual('some//url//to'); + expect( + removeSlashes('/some//url//to/', { leading: true, duplicates: true, trailing: true }) + ).toEqual('some/url/to'); + }); +}); + +describe('appendAppPath', () => { + it('appends the appBasePath with given path', () => { + expect(appendAppPath('/app/my-app', '/some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app/', 'some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', 'some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', '')).toEqual('/app/my-app'); + }); + + it('preserves the trailing slash only if included in the hash', () => { + expect(appendAppPath('/app/my-app', '/some-path/')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', '/some-path#/')).toEqual('/app/my-app/some-path#/'); + expect(appendAppPath('/app/my-app', '/some-path#/hash/')).toEqual( + '/app/my-app/some-path#/hash/' + ); + expect(appendAppPath('/app/my-app', '/some-path#/hash')).toEqual('/app/my-app/some-path#/hash'); + }); +}); diff --git a/src/core/public/application/utils.ts b/src/core/public/application/utils.ts new file mode 100644 index 00000000000000..048f195fe12230 --- /dev/null +++ b/src/core/public/application/utils.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. + */ + +/** + * Utility to remove trailing, leading or duplicate slashes. + * By default will only remove duplicates. + */ +export const removeSlashes = ( + url: string, + { + trailing = false, + leading = false, + duplicates = true, + }: { trailing?: boolean; leading?: boolean; duplicates?: boolean } = {} +): string => { + if (duplicates) { + url = url.replace(/\/{2,}/g, '/'); + } + if (trailing) { + url = url.replace(/\/$/, ''); + } + if (leading) { + url = url.replace(/^\//, ''); + } + return url; +}; + +export const appendAppPath = (appBasePath: string, path: string = '') => { + // Only prepend slash if not a hash or query path + path = path === '' || path.startsWith('#') || path.startsWith('?') ? path : `/${path}`; + // Do not remove trailing slash when in hashbang + const removeTrailing = path.indexOf('#') === -1; + return removeSlashes(`${appBasePath}${path}`, { + trailing: removeTrailing, + duplicates: true, + leading: false, + }); +}; diff --git a/src/core/public/chrome/nav_links/nav_link.ts b/src/core/public/chrome/nav_links/nav_link.ts index d0ef2aeb265feb..fb2972735c2b70 100644 --- a/src/core/public/chrome/nav_links/nav_link.ts +++ b/src/core/public/chrome/nav_links/nav_link.ts @@ -44,6 +44,12 @@ export interface ChromeNavLink { */ readonly baseUrl: string; + /** + * The route used to open the {@link AppBase.defaultPath | default path } of an application. + * If unset, `baseUrl` will be used instead. + */ + readonly url?: string; + /** * An ordinal used to sort nav links relative to one another for display. */ @@ -99,18 +105,6 @@ export interface ChromeNavLink { */ readonly linkToLastSubUrl?: boolean; - /** - * A url that legacy apps can set to deep link into their applications. - * - * @internalRemarks - * Currently used by the "lastSubUrl" feature legacy/ui/chrome. This should - * be removed once the ApplicationService is implemented and mounting apps. At that - * time, each app can handle opening to the previous location when they are mounted. - * - * @deprecated - */ - readonly url?: string; - /** * Indicates whether or not this app is currently on the screen. * diff --git a/src/core/public/chrome/nav_links/to_nav_link.test.ts b/src/core/public/chrome/nav_links/to_nav_link.test.ts index 23fdabe0f34301..4c319873af804e 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.test.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.test.ts @@ -85,6 +85,38 @@ describe('toNavLink', () => { expect(link.properties.baseUrl).toEqual('http://localhost/base-path/my-route/my-path'); }); + it('generates the `url` property', () => { + let link = toNavLink( + app({ + appRoute: '/my-route/my-path', + }), + basePath + ); + expect(link.properties.url).toEqual('http://localhost/base-path/my-route/my-path'); + + link = toNavLink( + app({ + appRoute: '/my-route/my-path', + defaultPath: 'some/default/path', + }), + basePath + ); + expect(link.properties.url).toEqual( + 'http://localhost/base-path/my-route/my-path/some/default/path' + ); + }); + + it('does not generate `url` for legacy app', () => { + const link = toNavLink( + legacyApp({ + appUrl: '/my-legacy-app/#foo', + defaultPath: '/some/default/path', + }), + basePath + ); + expect(link.properties.url).toBeUndefined(); + }); + it('uses appUrl when converting legacy applications', () => { expect( toNavLink( diff --git a/src/core/public/chrome/nav_links/to_nav_link.ts b/src/core/public/chrome/nav_links/to_nav_link.ts index 18e4b7b26b6ba1..f79b1df77f8e1c 100644 --- a/src/core/public/chrome/nav_links/to_nav_link.ts +++ b/src/core/public/chrome/nav_links/to_nav_link.ts @@ -20,9 +20,11 @@ import { App, AppNavLinkStatus, AppStatus, LegacyApp } from '../../application'; import { IBasePath } from '../../http'; import { NavLinkWrapper } from './nav_link'; +import { appendAppPath } from '../../application/utils'; export function toNavLink(app: App | LegacyApp, basePath: IBasePath): NavLinkWrapper { const useAppStatus = app.navLinkStatus === AppNavLinkStatus.default; + const baseUrl = isLegacyApp(app) ? basePath.prepend(app.appUrl) : basePath.prepend(app.appRoute!); return new NavLinkWrapper({ ...app, hidden: useAppStatus @@ -30,9 +32,12 @@ export function toNavLink(app: App | LegacyApp, basePath: IBasePath): NavLinkWra : app.navLinkStatus === AppNavLinkStatus.hidden, disabled: useAppStatus ? false : app.navLinkStatus === AppNavLinkStatus.disabled, legacy: isLegacyApp(app), - baseUrl: isLegacyApp(app) - ? relativeToAbsolute(basePath.prepend(app.appUrl)) - : relativeToAbsolute(basePath.prepend(app.appRoute!)), + baseUrl: relativeToAbsolute(baseUrl), + ...(isLegacyApp(app) + ? {} + : { + url: relativeToAbsolute(appendAppPath(baseUrl, app.defaultPath)), + }), }); } diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 52b59c53b658c0..d97ef477c2ee0c 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -53,7 +53,7 @@ export function euiNavLink( order, tooltip, } = navLink; - let href = navLink.baseUrl; + let href = navLink.url ?? navLink.baseUrl; if (legacy) { href = url && !active ? url : baseUrl; diff --git a/src/core/public/plugins/plugin.test.mocks.ts b/src/core/public/plugins/plugin.test.mocks.ts index b877847aaa90ed..422442c9ca4d2a 100644 --- a/src/core/public/plugins/plugin.test.mocks.ts +++ b/src/core/public/plugins/plugin.test.mocks.ts @@ -24,8 +24,8 @@ export const mockPlugin = { }; export const mockInitializer = jest.fn(() => mockPlugin); -export const mockPluginLoader = jest.fn().mockResolvedValue(mockInitializer); +export const mockPluginReader = jest.fn(() => mockInitializer); -jest.mock('./plugin_loader', () => ({ - loadPluginBundle: mockPluginLoader, +jest.mock('./plugin_reader', () => ({ + read: mockPluginReader, })); diff --git a/src/core/public/plugins/plugin.test.ts b/src/core/public/plugins/plugin.test.ts index 39330711f79809..8fe745db9554df 100644 --- a/src/core/public/plugins/plugin.test.ts +++ b/src/core/public/plugins/plugin.test.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { mockInitializer, mockPlugin, mockPluginLoader } from './plugin.test.mocks'; +import { mockInitializer, mockPlugin, mockPluginReader } from './plugin.test.mocks'; import { DiscoveredPlugin } from '../../server'; import { coreMock } from '../mocks'; @@ -38,10 +38,9 @@ function createManifest( let plugin: PluginWrapper>; const opaqueId = Symbol(); const initializerContext = coreMock.createPluginInitializerContext(); -const addBasePath = (path: string) => path; beforeEach(() => { - mockPluginLoader.mockClear(); + mockPluginReader.mockClear(); mockPlugin.setup.mockClear(); mockPlugin.start.mockClear(); mockPlugin.stop.mockClear(); @@ -49,20 +48,8 @@ beforeEach(() => { }); describe('PluginWrapper', () => { - test('`load` calls loadPluginBundle', () => { - plugin.load(addBasePath); - expect(mockPluginLoader).toHaveBeenCalledWith(addBasePath, 'plugin-a'); - }); - - test('`setup` fails if load is not called first', async () => { - await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Plugin \\"plugin-a\\" can't be setup since its bundle isn't loaded."` - ); - }); - test('`setup` fails if plugin.setup is not a function', async () => { mockInitializer.mockReturnValueOnce({ start: jest.fn() } as any); - await plugin.load(addBasePath); await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot( `"Instance of plugin \\"plugin-a\\" does not define \\"setup\\" function."` ); @@ -70,20 +57,17 @@ describe('PluginWrapper', () => { test('`setup` fails if plugin.start is not a function', async () => { mockInitializer.mockReturnValueOnce({ setup: jest.fn() } as any); - await plugin.load(addBasePath); await expect(plugin.setup({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot( `"Instance of plugin \\"plugin-a\\" does not define \\"start\\" function."` ); }); test('`setup` calls initializer with initializer context', async () => { - await plugin.load(addBasePath); await plugin.setup({} as any, {} as any); expect(mockInitializer).toHaveBeenCalledWith(initializerContext); }); test('`setup` calls plugin.setup with context and dependencies', async () => { - await plugin.load(addBasePath); const context = { any: 'thing' } as any; const deps = { otherDep: 'value' }; await plugin.setup(context, deps); @@ -91,14 +75,12 @@ describe('PluginWrapper', () => { }); test('`start` fails if setup is not called first', async () => { - await plugin.load(addBasePath); await expect(plugin.start({} as any, {} as any)).rejects.toThrowErrorMatchingInlineSnapshot( `"Plugin \\"plugin-a\\" can't be started since it isn't set up."` ); }); test('`start` calls plugin.start with context and dependencies', async () => { - await plugin.load(addBasePath); await plugin.setup({} as any, {} as any); const context = { any: 'thing' } as any; const deps = { otherDep: 'value' }; @@ -114,20 +96,21 @@ describe('PluginWrapper', () => { }; let startDependenciesResolved = false; - mockPluginLoader.mockResolvedValueOnce(() => ({ - setup: jest.fn(), - start: async () => { - // Add small delay to ensure startDependencies is not resolved until after the plugin instance's start resolves. - await new Promise(resolve => setTimeout(resolve, 10)); - expect(startDependenciesResolved).toBe(false); - return pluginStartContract; - }, - })); - await plugin.load(addBasePath); + mockPluginReader.mockReturnValueOnce( + jest.fn(() => ({ + setup: jest.fn(), + start: jest.fn(async () => { + // Add small delay to ensure startDependencies is not resolved until after the plugin instance's start resolves. + await new Promise(resolve => setTimeout(resolve, 10)); + expect(startDependenciesResolved).toBe(false); + return pluginStartContract; + }), + stop: jest.fn(), + })) + ); await plugin.setup({} as any, {} as any); const context = { any: 'thing' } as any; const deps = { otherDep: 'value' }; - // Add promise callback prior to calling `start` to ensure calls in `setup` will not resolve before `start` is // called. const startDependenciesCheck = plugin.startDependencies.then(res => { @@ -145,7 +128,6 @@ describe('PluginWrapper', () => { }); test('`stop` calls plugin.stop', async () => { - await plugin.load(addBasePath); await plugin.setup({} as any, {} as any); await plugin.stop(); expect(mockPlugin.stop).toHaveBeenCalled(); @@ -153,7 +135,6 @@ describe('PluginWrapper', () => { test('`stop` does not fail if plugin.stop does not exist', async () => { mockInitializer.mockReturnValueOnce({ setup: jest.fn(), start: jest.fn() } as any); - await plugin.load(addBasePath); await plugin.setup({} as any, {} as any); expect(() => plugin.stop()).not.toThrow(); }); diff --git a/src/core/public/plugins/plugin.ts b/src/core/public/plugins/plugin.ts index e51c45040c452e..591165fcd28390 100644 --- a/src/core/public/plugins/plugin.ts +++ b/src/core/public/plugins/plugin.ts @@ -21,7 +21,7 @@ import { Subject } from 'rxjs'; import { first } from 'rxjs/operators'; import { DiscoveredPlugin, PluginOpaqueId } from '../../server'; import { PluginInitializerContext } from './plugin_context'; -import { loadPluginBundle } from './plugin_loader'; +import { read } from './plugin_reader'; import { CoreStart, CoreSetup } from '..'; /** @@ -69,7 +69,6 @@ export class PluginWrapper< public readonly configPath: DiscoveredPlugin['configPath']; public readonly requiredPlugins: DiscoveredPlugin['requiredPlugins']; public readonly optionalPlugins: DiscoveredPlugin['optionalPlugins']; - private initializer?: PluginInitializer; private instance?: Plugin; private readonly startDependencies$ = new Subject<[CoreStart, TPluginsStart, TStart]>(); @@ -86,18 +85,6 @@ export class PluginWrapper< this.optionalPlugins = discoveredPlugin.optionalPlugins; } - /** - * Loads the plugin's bundle into the browser. Should be called in parallel with all plugins - * using `Promise.all`. Must be called before `setup`. - * @param addBasePath Function that adds the base path to a string for plugin bundle path. - */ - public async load(addBasePath: (path: string) => string) { - this.initializer = await loadPluginBundle( - addBasePath, - this.name - ); - } - /** * Instantiates plugin and calls `setup` function exposed by the plugin initializer. * @param setupContext Context that consists of various core services tailored specifically @@ -146,11 +133,14 @@ export class PluginWrapper< } private async createPluginInstance() { - if (this.initializer === undefined) { - throw new Error(`Plugin "${this.name}" can't be setup since its bundle isn't loaded.`); - } - - const instance = this.initializer(this.initializerContext); + const initializer = read(this.name) as PluginInitializer< + TSetup, + TStart, + TPluginsSetup, + TPluginsStart + >; + + const instance = initializer(this.initializerContext); if (typeof instance.setup !== 'function') { throw new Error(`Instance of plugin "${this.name}" does not define "setup" function.`); diff --git a/src/core/public/plugins/plugin_loader.mock.ts b/src/core/public/plugins/plugin_loader.mock.ts deleted file mode 100644 index abdd9d4ddce2ab..00000000000000 --- a/src/core/public/plugins/plugin_loader.mock.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 { PluginName } from 'src/core/server'; -import { LoadPluginBundle, UnknownPluginInitializer } from './plugin_loader'; - -/** - * @param initializerProvider A function provided by the test to resolve initializers. - */ -const createLoadPluginBundleMock = ( - initializerProvider: (name: PluginName) => UnknownPluginInitializer -): jest.Mock, Parameters> => - jest.fn((addBasePath, pluginName, _ = {}) => { - return Promise.resolve(initializerProvider(pluginName)) as any; - }); - -export const loadPluginBundleMock = { create: createLoadPluginBundleMock }; diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts deleted file mode 100644 index b4e2c3095f14a7..00000000000000 --- a/src/core/public/plugins/plugin_loader.test.ts +++ /dev/null @@ -1,125 +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 { CoreWindow, loadPluginBundle } from './plugin_loader'; - -let createdScriptTags = [] as any[]; -let appendChildSpy: jest.SpyInstance; -let createElementSpy: jest.SpyInstance< - HTMLElement, - [string, (ElementCreationOptions | undefined)?] ->; - -const coreWindow = (window as unknown) as CoreWindow; - -beforeEach(() => { - // Mock document.createElement to return fake tags we can use to inspect what - // loadPluginBundles does. - createdScriptTags = []; - createElementSpy = jest.spyOn(document, 'createElement').mockImplementation(() => { - const scriptTag = { setAttribute: jest.fn() } as any; - createdScriptTags.push(scriptTag); - return scriptTag; - }); - - // Mock document.body.appendChild to avoid errors about appending objects that aren't `Node`'s - // and so we can verify that the script tags were added to the page. - appendChildSpy = jest.spyOn(document.body, 'appendChild').mockReturnValue({} as any); - - // Mock global fields needed for loading modules. - coreWindow.__kbnBundles__ = {}; -}); - -afterEach(() => { - appendChildSpy.mockRestore(); - createElementSpy.mockRestore(); - delete coreWindow.__kbnBundles__; -}); - -const addBasePath = (path: string) => path; - -test('`loadPluginBundles` creates a script tag and loads initializer', async () => { - const loadPromise = loadPluginBundle(addBasePath, 'plugin-a'); - - // Verify it sets up the script tag correctly and adds it to document.body - expect(createdScriptTags).toHaveLength(1); - const fakeScriptTag = createdScriptTags[0]; - expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( - 'src', - '/bundles/plugin/plugin-a/plugin-a.plugin.js' - ); - expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith('id', 'kbn-plugin-plugin-a'); - expect(fakeScriptTag.onload).toBeInstanceOf(Function); - expect(fakeScriptTag.onerror).toBeInstanceOf(Function); - expect(appendChildSpy).toHaveBeenCalledWith(fakeScriptTag); - - // Setup a fake initializer as if a plugin bundle had actually been loaded. - const fakeInitializer = jest.fn(); - coreWindow.__kbnBundles__['plugin/plugin-a'] = { plugin: fakeInitializer }; - // Call the onload callback - fakeScriptTag.onload(); - await expect(loadPromise).resolves.toEqual(fakeInitializer); -}); - -test('`loadPluginBundles` includes the basePath', async () => { - loadPluginBundle((path: string) => `/mybasepath${path}`, 'plugin-a'); - - // Verify it sets up the script tag correctly and adds it to document.body - expect(createdScriptTags).toHaveLength(1); - const fakeScriptTag = createdScriptTags[0]; - expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( - 'src', - '/mybasepath/bundles/plugin/plugin-a/plugin-a.plugin.js' - ); -}); - -test('`loadPluginBundles` rejects if script.onerror is called', async () => { - const loadPromise = loadPluginBundle(addBasePath, 'plugin-a'); - const fakeScriptTag1 = createdScriptTags[0]; - // Call the error on the second script - fakeScriptTag1.onerror(new Error('Whoa there!')); - - await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` - ); -}); - -test('`loadPluginBundles` rejects if timeout is reached', async () => { - await expect( - // Override the timeout to 1 ms for testi. - loadPluginBundle(addBasePath, 'plugin-a', { timeoutMs: 1 }) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` - ); -}); - -test('`loadPluginBundles` rejects if bundle does attach an initializer to window.__kbnBundles__', async () => { - const loadPromise = loadPluginBundle(addBasePath, 'plugin-a'); - - const fakeScriptTag1 = createdScriptTags[0]; - - // Setup a fake initializer as if a plugin bundle had actually been loaded. - coreWindow.__kbnBundles__['plugin/plugin-a'] = undefined; - // Call the onload callback - fakeScriptTag1.onload(); - - await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin/plugin-a/plugin-a.plugin.js)."` - ); -}); diff --git a/src/core/public/plugins/plugin_loader.ts b/src/core/public/plugins/plugin_loader.ts deleted file mode 100644 index bf7711055e97ba..00000000000000 --- a/src/core/public/plugins/plugin_loader.ts +++ /dev/null @@ -1,141 +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 { PluginName } from '../../server'; -import { PluginInitializer } from './plugin'; - -/** - * Unknown variant for internal use only for when plugins are not known. - * @internal - */ -export type UnknownPluginInitializer = PluginInitializer>; - -/** - * Custom window type for loading bundles. Do not extend global Window to avoid leaking these types. - * @internal - */ -export interface CoreWindow { - __kbnBundles__: { - [pluginBundleName: string]: { plugin: UnknownPluginInitializer } | undefined; - }; -} - -/** - * Timeout for loading a single script in milliseconds. - * @internal - */ -export const LOAD_TIMEOUT = 120 * 1000; // 2 minutes - -/** - * Loads the bundle for a plugin onto the page and returns their PluginInitializer. This should - * be called for all plugins (once per plugin) in parallel using Promise.all. - * - * If this is slowing down browser load time, there are some ways we could make this faster: - * - Add these bundles in the generated bootstrap.js file so they're loaded immediately - * - Concatenate all the bundles files on the backend and serve them in single request. - * - Use HTTP/2 to load these bundles without having to open new connections for each. - * - * This may not be much of an issue since these should be cached by the browser after the first - * page load. - * - * @param basePath - * @param plugins - * @internal - */ -export const loadPluginBundle: LoadPluginBundle = < - TSetup, - TStart, - TPluginsSetup extends object, - TPluginsStart extends object ->( - addBasePath: (path: string) => string, - pluginName: PluginName, - { timeoutMs = LOAD_TIMEOUT }: { timeoutMs?: number } = {} -) => - new Promise>( - (resolve, reject) => { - const coreWindow = (window as unknown) as CoreWindow; - const exportId = `plugin/${pluginName}`; - - const readPluginExport = () => { - const PluginExport: any = coreWindow.__kbnBundles__[exportId]; - if (typeof PluginExport?.plugin !== 'function') { - reject( - new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`) - ); - } else { - resolve( - PluginExport.plugin as PluginInitializer - ); - } - }; - - if (coreWindow.__kbnBundles__[exportId]) { - readPluginExport(); - return; - } - - const script = document.createElement('script'); - // Assumes that all plugin bundles get put into the bundles/plugins subdirectory - const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`); - script.setAttribute('src', bundlePath); - script.setAttribute('id', `kbn-plugin-${pluginName}`); - script.setAttribute('async', ''); - - const cleanupTag = () => { - clearTimeout(timeout); - // Set to null for IE memory leak issue. Webpack does the same thing. - // @ts-ignore - script.onload = script.onerror = null; - }; - - // Wire up resolve and reject - script.onload = () => { - cleanupTag(); - readPluginExport(); - }; - - script.onerror = () => { - cleanupTag(); - reject(new Error(`Failed to load "${pluginName}" bundle (${bundlePath})`)); - }; - - const timeout = setTimeout(() => { - cleanupTag(); - reject(new Error(`Timeout reached when loading "${pluginName}" bundle (${bundlePath})`)); - }, timeoutMs); - - // Add the script tag to the end of the body to start downloading - document.body.appendChild(script); - } - ); - -/** - * @internal - */ -export type LoadPluginBundle = < - TSetup, - TStart, - TPluginsSetup extends object, - TPluginsStart extends object ->( - addBasePath: (path: string) => string, - pluginName: PluginName, - options?: { timeoutMs?: number } -) => Promise>; diff --git a/src/core/public/plugins/plugin_reader.test.ts b/src/core/public/plugins/plugin_reader.test.ts new file mode 100644 index 00000000000000..d4324f81de8e64 --- /dev/null +++ b/src/core/public/plugins/plugin_reader.test.ts @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { CoreWindow, read, UnknownPluginInitializer } from './plugin_reader'; + +const coreWindow: CoreWindow = window as any; +beforeEach(() => { + coreWindow.__kbnBundles__ = {}; +}); + +it('handles undefined plugin exports', () => { + coreWindow.__kbnBundles__['plugin/foo'] = undefined; + + expect(() => { + read('foo'); + }).toThrowError(`Definition of plugin "foo" not found and may have failed to load.`); +}); + +it('handles plugin exports with a "plugin" export that is not a function', () => { + coreWindow.__kbnBundles__['plugin/foo'] = { + plugin: 1234, + } as any; + + expect(() => { + read('foo'); + }).toThrowError(`Definition of plugin "foo" should be a function.`); +}); + +it('returns the plugin initializer when the "plugin" named export is a function', () => { + const plugin: UnknownPluginInitializer = () => { + return undefined as any; + }; + + coreWindow.__kbnBundles__['plugin/foo'] = { plugin }; + + expect(read('foo')).toBe(plugin); +}); diff --git a/src/core/public/plugins/plugin_reader.ts b/src/core/public/plugins/plugin_reader.ts new file mode 100644 index 00000000000000..1907dfa6a3e99c --- /dev/null +++ b/src/core/public/plugins/plugin_reader.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { PluginInitializer } from './plugin'; + +/** + * Unknown variant for internal use only for when plugins are not known. + * @internal + */ +export type UnknownPluginInitializer = PluginInitializer>; + +/** + * Custom window type for loading bundles. Do not extend global Window to avoid leaking these types. + * @internal + */ +export interface CoreWindow { + __kbnBundles__: { + [pluginBundleName: string]: { plugin: UnknownPluginInitializer } | undefined; + }; +} + +/** + * Reads the plugin's bundle declared in the global context. + */ +export function read(name: string) { + const coreWindow = (window as unknown) as CoreWindow; + const exportId = `plugin/${name}`; + const pluginExport = coreWindow.__kbnBundles__[exportId]; + if (!pluginExport) { + throw new Error(`Definition of plugin "${name}" not found and may have failed to load.`); + } else if (typeof pluginExport.plugin !== 'function') { + throw new Error(`Definition of plugin "${name}" should be a function.`); + } else { + return pluginExport.plugin; + } +} diff --git a/src/core/public/plugins/plugins_service.test.mocks.ts b/src/core/public/plugins/plugins_service.test.mocks.ts index a76078932518f2..85b84e561056ad 100644 --- a/src/core/public/plugins/plugins_service.test.mocks.ts +++ b/src/core/public/plugins/plugins_service.test.mocks.ts @@ -19,16 +19,16 @@ import { PluginName } from 'kibana/server'; import { Plugin } from './plugin'; -import { loadPluginBundleMock } from './plugin_loader.mock'; export type MockedPluginInitializer = jest.Mock>, any>; export const mockPluginInitializerProvider: jest.Mock< MockedPluginInitializer, [PluginName] -> = jest.fn().mockRejectedValue(new Error('No provider specified')); +> = jest.fn().mockImplementation(() => () => { + throw new Error('No provider specified'); +}); -export const mockLoadPluginBundle = loadPluginBundleMock.create(mockPluginInitializerProvider); -jest.mock('./plugin_loader', () => ({ - loadPluginBundle: mockLoadPluginBundle, +jest.mock('./plugin_reader', () => ({ + read: mockPluginInitializerProvider, })); diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 688eaf4f2bfc7a..6d71844bc19c84 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -21,7 +21,6 @@ import { omit, pick } from 'lodash'; import { MockedPluginInitializer, - mockLoadPluginBundle, mockPluginInitializerProvider, } from './plugins_service.test.mocks'; @@ -32,6 +31,7 @@ import { PluginsServiceStartDeps, PluginsServiceSetupDeps, } from './plugins_service'; + import { InjectedPluginMetadata } from '../injected_metadata'; import { notificationServiceMock } from '../notifications/notifications_service.mock'; import { applicationServiceMock } from '../application/application_service.mock'; @@ -152,10 +152,6 @@ describe('PluginsService', () => { ] as unknown) as [[PluginName, any]]); }); - afterEach(() => { - mockLoadPluginBundle.mockClear(); - }); - describe('#getOpaqueIds()', () => { it('returns dependency tree of symbols', () => { const pluginsService = new PluginsService(mockCoreContext, plugins); @@ -174,15 +170,6 @@ describe('PluginsService', () => { }); describe('#setup()', () => { - it('fails if any bundle cannot be loaded', async () => { - mockLoadPluginBundle.mockRejectedValueOnce(new Error('Could not load bundle')); - - const pluginsService = new PluginsService(mockCoreContext, plugins); - await expect(pluginsService.setup(mockSetupDeps)).rejects.toThrowErrorMatchingInlineSnapshot( - `"Could not load bundle"` - ); - }); - it('fails if any plugin instance does not have a setup function', async () => { mockPluginInitializers.set('pluginA', (() => ({})) as any); const pluginsService = new PluginsService(mockCoreContext, plugins); @@ -191,25 +178,6 @@ describe('PluginsService', () => { ); }); - it('calls loadPluginBundles with http and plugins', async () => { - const pluginsService = new PluginsService(mockCoreContext, plugins); - await pluginsService.setup(mockSetupDeps); - - expect(mockLoadPluginBundle).toHaveBeenCalledTimes(3); - expect(mockLoadPluginBundle).toHaveBeenCalledWith( - mockSetupDeps.http.basePath.prepend, - 'pluginA' - ); - expect(mockLoadPluginBundle).toHaveBeenCalledWith( - mockSetupDeps.http.basePath.prepend, - 'pluginB' - ); - expect(mockLoadPluginBundle).toHaveBeenCalledWith( - mockSetupDeps.http.basePath.prepend, - 'pluginC' - ); - }); - it('initializes plugins with PluginInitializerContext', async () => { const pluginsService = new PluginsService(mockCoreContext, plugins); await pluginsService.setup(mockSetupDeps); @@ -302,7 +270,6 @@ describe('PluginsService', () => { const pluginsService = new PluginsService(mockCoreContext, plugins); const promise = pluginsService.setup(mockSetupDeps); - jest.runAllTimers(); // load plugin bundles await flushPromises(); jest.runAllTimers(); // setup plugins diff --git a/src/core/public/plugins/plugins_service.ts b/src/core/public/plugins/plugins_service.ts index e698af689036d7..862aa5043ad4b7 100644 --- a/src/core/public/plugins/plugins_service.ts +++ b/src/core/public/plugins/plugins_service.ts @@ -93,9 +93,6 @@ export class PluginsService implements CoreService { - // Load plugin bundles - await this.loadPluginBundles(deps.http.basePath.prepend); - // Setup each plugin with required and optional plugin contracts const contracts = new Map(); for (const [pluginName, plugin] of this.plugins.entries()) { @@ -167,9 +164,4 @@ export class PluginsService implements CoreService string) { - // Load all bundles in parallel - return Promise.all([...this.plugins.values()].map(plugin => plugin.load(addBasePath))); - } } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index b92bb209d26073..af06b207889c22 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -36,6 +36,7 @@ export interface AppBase { capabilities?: Partial; category?: AppCategory; chromeless?: boolean; + defaultPath?: string; euiIconType?: string; icon?: string; id: string; @@ -168,7 +169,7 @@ export enum AppStatus { export type AppUnmount = () => void; // @public -export type AppUpdatableFields = Pick; +export type AppUpdatableFields = Pick; // @public export type AppUpdater = (app: AppBase) => Partial | undefined; @@ -290,7 +291,6 @@ export interface ChromeNavLink { readonly subUrlBase?: string; readonly title: string; readonly tooltip?: string; - // @deprecated readonly url?: string; } 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 index 1790b096a71aea..dfa2396d5904bc 100644 --- a/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts +++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.test.ts @@ -48,7 +48,7 @@ describe('logLegacyThirdPartyPluginDeprecationWarning', () => { 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://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.", + "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.", ] `); }); @@ -65,7 +65,7 @@ describe('logLegacyThirdPartyPluginDeprecationWarning', () => { 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://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html for a list of breaking changes and https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md for documentation on how to migrate legacy plugins.", + "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.", ] `); }); diff --git a/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts index f9c3dcbf554cb8..df86f5a2b40317 100644 --- a/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts +++ b/src/core/server/legacy/plugins/log_legacy_plugins_warning.ts @@ -22,9 +22,10 @@ import { LegacyPluginSpec } from '../types'; const internalPaths = ['/src/legacy/core_plugins', '/x-pack']; -const breakingChangesUrl = - 'https://www.elastic.co/guide/en/kibana/master/breaking-changes-8.0.html'; -const migrationGuideUrl = 'https://github.com/elastic/kibana/blob/master/src/core/MIGRATION.md'; +// 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, diff --git a/src/core/server/rendering/views/template.tsx b/src/core/server/rendering/views/template.tsx index b38259a84cb9cc..73e119a5a97e73 100644 --- a/src/core/server/rendering/views/template.tsx +++ b/src/core/server/rendering/views/template.tsx @@ -104,6 +104,10 @@ export const Template: FunctionComponent = ({ + + {/* Inject stylesheets into the before scripts so that KP plugins with bundled styles will override them */} + + {createElement('kbn-csp', { diff --git a/src/core/server/saved_objects/mappings/types.ts b/src/core/server/saved_objects/mappings/types.ts index 47fc29f8cf7d29..c1b65763949bbe 100644 --- a/src/core/server/saved_objects/mappings/types.ts +++ b/src/core/server/saved_objects/mappings/types.ts @@ -131,6 +131,7 @@ export interface IndexMappingMeta { */ export interface SavedObjectsCoreFieldMapping { type: string; + null_value?: number | boolean | string; index?: boolean; enabled?: boolean; fields?: { diff --git a/src/core/server/saved_objects/migrations/core/index.ts b/src/core/server/saved_objects/migrations/core/index.ts index 466d399f653cd6..f7274740ea5fe3 100644 --- a/src/core/server/saved_objects/migrations/core/index.ts +++ b/src/core/server/saved_objects/migrations/core/index.ts @@ -21,5 +21,5 @@ export { DocumentMigrator } from './document_migrator'; export { IndexMigrator } from './index_migrator'; export { buildActiveMappings } from './build_active_mappings'; export { CallCluster } from './call_cluster'; -export { LogFn } from './migration_logger'; +export { LogFn, SavedObjectsMigrationLogger } from './migration_logger'; export { MigrationResult, MigrationStatus } from './migration_coordinator'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js b/src/core/server/saved_objects/migrations/mocks.ts similarity index 60% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js rename to src/core/server/saved_objects/migrations/mocks.ts index 024dee60ef2bf4..76a890d26bfa0c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js +++ b/src/core/server/saved_objects/migrations/mocks.ts @@ -17,27 +17,27 @@ * under the License. */ -import './lib/types/pie'; -import './lib/types/point_series'; -import './lib/types'; -import './lib/layout/layout_types'; -import './lib/data'; -import './visualizations/vis_types'; -import { Vis } from './vis'; +import { SavedObjectMigrationContext } from './types'; +import { SavedObjectsMigrationLogger } from './core'; -// prefetched for faster optimization runs -// end prefetching +const createLoggerMock = (): jest.Mocked => { + const mock = { + debug: jest.fn(), + info: jest.fn(), + warning: jest.fn(), + warn: jest.fn(), + }; -/** - * Provides the Kibana4 Visualization Library - * - * @module vislib - * @main vislib - * @return {Object} Contains the version number and the Vis Class for creating visualizations - */ -export function VislibProvider() { - return { - version: '0.0.0', - Vis, + return mock; +}; + +const createContextMock = (): jest.Mocked => { + const mock = { + log: createLoggerMock(), }; -} + return mock; +}; + +export const migrationMocks = { + createContext: createContextMock, +}; diff --git a/src/core/server/saved_objects/migrations/types.ts b/src/core/server/saved_objects/migrations/types.ts index 6bc085dde872e3..85f15b4c18b66a 100644 --- a/src/core/server/saved_objects/migrations/types.ts +++ b/src/core/server/saved_objects/migrations/types.ts @@ -26,23 +26,37 @@ import { SavedObjectsMigrationLogger } from './core/migration_logger'; * * @example * ```typescript - * const migrateProperty: SavedObjectMigrationFn = (doc, { log }) => { - * if(doc.attributes.someProp === null) { - * log.warn('Skipping migration'); - * } else { - * doc.attributes.someProp = migrateProperty(doc.attributes.someProp); - * } + * interface TypeV1Attributes { + * someKey: string; + * obsoleteProperty: number; + * } * - * return doc; + * interface TypeV2Attributes { + * someKey: string; + * newProperty: string; * } + * + * const migrateToV2: SavedObjectMigrationFn = (doc, { log }) => { + * const { obsoleteProperty, ...otherAttributes } = doc.attributes; + * // instead of mutating `doc` we make a shallow copy so that we can use separate types for the input + * // and output attributes. We don't need to make a deep copy, we just need to ensure that obsolete + * // attributes are not present on the returned doc. + * return { + * ...doc, + * attributes: { + * ...otherAttributes, + * newProperty: migrate(obsoleteProperty), + * }, + * }; + * }; * ``` * * @public */ -export type SavedObjectMigrationFn = ( - doc: SavedObjectUnsanitizedDoc, +export type SavedObjectMigrationFn = ( + doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext -) => SavedObjectUnsanitizedDoc; +) => SavedObjectUnsanitizedDoc; /** * Migration context provided when invoking a {@link SavedObjectMigrationFn | migration handler} diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 7ba4613c857d7b..4e1f5981d6a410 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -31,6 +31,7 @@ import { savedObjectsClientProviderMock } from './service/lib/scoped_client_prov import { savedObjectsRepositoryMock } from './service/lib/repository.mock'; import { savedObjectsClientMock } from './service/saved_objects_client.mock'; import { typeRegistryMock } from './saved_objects_type_registry.mock'; +import { migrationMocks } from './migrations/mocks'; import { ServiceStatusLevels } from '../status'; type SavedObjectsServiceContract = PublicMethodsOf; @@ -105,4 +106,5 @@ export const savedObjectsServiceMock = { createSetupContract: createSetupContractMock, createInternalStartContract: createInternalStartContractMock, createStartContract: createStartContractMock, + createMigrationContext: migrationMocks.createContext, }; diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index a33e16895078ed..acd2c7b5284aa2 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -47,8 +47,8 @@ export interface SavedObjectsRawDocSource { /** * Saved Object base document */ -interface SavedObjectDoc { - attributes: any; +interface SavedObjectDoc { + attributes: T; id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional type: string; namespace?: string; @@ -69,7 +69,7 @@ interface Referencable { * * @public */ -export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; /** * Describes Saved Object documents that have passed through the migration @@ -77,4 +77,4 @@ export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; * * @public */ -export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a36e746f6d9405..e8b77a8570291e 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1680,7 +1680,7 @@ export interface SavedObjectMigrationContext { } // @public -export type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; +export type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; // @public export interface SavedObjectMigrationMap { @@ -1708,7 +1708,7 @@ export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOpti // Warning: (ae-forgotten-export) The symbol "Referencable" needs to be exported by the entry point index.d.ts // // @public -export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; // @public (undocumented) export interface SavedObjectsBaseOptions { @@ -1840,6 +1840,8 @@ export interface SavedObjectsCoreFieldMapping { // (undocumented) index?: boolean; // (undocumented) + null_value?: number | boolean | string; + // (undocumented) type: string; } @@ -2309,7 +2311,7 @@ export class SavedObjectTypeRegistry { } // @public -export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; // @public export type ScopeableRequest = KibanaRequest | LegacyRequest | FakeRequest; 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 eb7a121c2e64b2..d1fb544de733c3 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 @@ -234,6 +234,7 @@ kibana_vars=( xpack.security.session.idleTimeout xpack.security.session.lifespan xpack.security.loginAssistanceMessage + xpack.security.loginHelp telemetry.allowChangingOptInStatus telemetry.enabled telemetry.optIn diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 43a2cbd78c5023..c5387590fcf661 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -40,6 +40,7 @@ export default { ], collectCoverageFrom: [ 'src/plugins/**/*.{ts,tsx}', + '!src/plugins/**/{__test__,__snapshots__,__examples__,mocks,tests}/**/*', '!src/plugins/**/*.d.ts', 'packages/kbn-ui-framework/src/components/**/*.js', '!packages/kbn-ui-framework/src/components/index.js', diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index fc95288eabed85..66296736b3ad04 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -117,7 +117,6 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'src/legacy/core_plugins/tile_map/public/__tests__/shadedCircleMarkers.png', 'src/legacy/core_plugins/tile_map/public/__tests__/shadedGeohashGrid.png', 'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json', - 'src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js', 'src/core/server/core_app/assets/favicons/android-chrome-192x192.png', 'src/core/server/core_app/assets/favicons/android-chrome-256x256.png', 'src/core/server/core_app/assets/favicons/android-chrome-512x512.png', diff --git a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js index 21b7ea7dbf4c3b..9f5f4b764f9b05 100644 --- a/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vega/vega_visualization.js @@ -59,12 +59,14 @@ import { setData, setSavedObjects, setNotifications, + setKibanaMapFactory, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../../plugins/vis_type_vega/public/services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { setInjectedVarFunc } from '../../../../../../plugins/maps_legacy/public/kibana_services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceSettings } from '../../../../../../plugins/maps_legacy/public/map/service_settings'; +import { getKibanaMapFactoryProvider } from '../../../../../../plugins/maps_legacy/public'; const THRESHOLD = 0.1; const PIXEL_DIFF = 30; @@ -77,6 +79,18 @@ describe('VegaVisualizations', () => { let vegaVisualizationDependencies; let vegaVisType; + const coreSetupMock = { + notifications: { + toasts: {}, + }, + uiSettings: { + get: () => {}, + }, + injectedMetadata: { + getInjectedVar: () => {}, + }, + }; + setKibanaMapFactory(getKibanaMapFactoryProvider(coreSetupMock)); setInjectedVars({ emsTileLayerId: {}, enableExternalUrls: true, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/_vis_fixture.js similarity index 82% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/_vis_fixture.js index 05cea7addf560d..8a542fec0639c3 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/_vis_fixture.js @@ -20,13 +20,10 @@ import _ from 'lodash'; import $ from 'jquery'; -import { Vis } from '../../../vis'; +import { Vis } from '../../../../../../plugins/vis_type_vislib/public/vislib/vis'; // TODO: Remove when converted to jest mocks -import { - ColorsService, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../../plugins/charts/public/services'; +import { ColorsService } from '../../../../../../plugins/charts/public/services'; const $visCanvas = $('
') .attr('id', 'vislib-vis-fixtures') @@ -72,17 +69,6 @@ const getDeps = () => { }; }; -export const getMockUiState = () => { - const map = new Map(); - - return (() => ({ - get: (...args) => map.get(...args), - set: (...args) => map.set(...args), - setSilent: (...args) => map.set(...args), - on: () => undefined, - }))(); -}; - export function getVis(visLibParams, element) { return new Vis( element || $visCanvas.new(), diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/chart_title.js similarity index 91% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/chart_title.js index b65571becd83c6..81fef155daf57e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/chart_title.js @@ -21,9 +21,9 @@ import d3 from 'd3'; import _ from 'lodash'; import expect from '@kbn/expect'; -import { ChartTitle } from '../../lib/chart_title'; -import { VisConfig } from '../../lib/vis_config'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { ChartTitle } from '../../../../../../../plugins/vis_type_vislib/public/vislib/lib/chart_title'; +import { VisConfig } from '../../../../../../../plugins/vis_type_vislib/public/vislib/lib/vis_config'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; describe('Vislib ChartTitle Class Test Suite', function() { let mockUiState; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/dispatch.js similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/dispatch.js index a5d8eb80419a11..eb4e109690c37f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/dispatch.js @@ -22,9 +22,10 @@ import d3 from 'd3'; import expect from '@kbn/expect'; // Data -import data from './fixtures/mock_data/date_histogram/_series'; +import data from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; -import { getVis, getMockUiState } from './fixtures/_vis_fixture'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; describe('Vislib Dispatch Class Test Suite', function() { function destroyVis(vis) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/handler/handler.js similarity index 86% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/handler/handler.js index 8e25015c101860..27f7f4ed3e0732 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/handler/handler.js @@ -21,12 +21,12 @@ import expect from '@kbn/expect'; import $ from 'jquery'; // Data -import series from '../fixtures/mock_data/date_histogram/_series'; -import columns from '../fixtures/mock_data/date_histogram/_columns'; -import rows from '../fixtures/mock_data/date_histogram/_rows'; -import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series'; - -import { getVis, getMockUiState } from '../fixtures/_vis_fixture'; +import series from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import columns from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns'; +import rows from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'; +import { getMockUiState } from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../../_vis_fixture'; const dateHistogramArray = [series, columns, rows, stackedSeries]; const names = ['series', 'columns', 'rows', 'stackedSeries']; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/layout/layout.js similarity index 85% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/layout/layout.js index f72794e27e834a..505b0a04c61837 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/lib/layout/layout.js @@ -22,14 +22,14 @@ import expect from '@kbn/expect'; import $ from 'jquery'; // Data -import series from '../fixtures/mock_data/date_histogram/_series'; -import columns from '../fixtures/mock_data/date_histogram/_columns'; -import rows from '../fixtures/mock_data/date_histogram/_rows'; -import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series'; - -import { Layout } from '../../../lib/layout/layout'; -import { getVis, getMockUiState } from '../fixtures/_vis_fixture'; -import { VisConfig } from '../../../lib/vis_config'; +import series from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import columns from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns'; +import rows from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'; +import { getMockUiState } from '../../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { Layout } from '../../../../../../../../plugins/vis_type_vislib/public/vislib/lib/layout/layout'; +import { VisConfig } from '../../../../../../../../plugins/vis_type_vislib/public/vislib/lib/vis_config'; +import { getVis } from '../../_vis_fixture'; const dateHistogramArray = [series, columns, rows, stackedSeries]; const names = ['series', 'columns', 'rows', 'stackedSeries']; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/vis.js similarity index 92% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/vis.js index 4852f71d8c45b3..67f29ee96a3364 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/vis.js @@ -21,11 +21,12 @@ import _ from 'lodash'; import $ from 'jquery'; import expect from '@kbn/expect'; -import series from './lib/fixtures/mock_data/date_histogram/_series'; -import columns from './lib/fixtures/mock_data/date_histogram/_columns'; -import rows from './lib/fixtures/mock_data/date_histogram/_rows'; -import stackedSeries from './lib/fixtures/mock_data/date_histogram/_stacked_series'; -import { getVis, getMockUiState } from './lib/fixtures/_vis_fixture'; +import series from '../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import columns from '../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns'; +import rows from '../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from '../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'; +import { getMockUiState } from '../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from './_vis_fixture'; const dataArray = [series, columns, rows, stackedSeries]; const names = ['series', 'columns', 'rows', 'stackedSeries']; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/area_chart.js similarity index 89% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/area_chart.js index c3f5859eb454cc..eb529c380cdda4 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/area_chart.js @@ -22,15 +22,16 @@ import _ from 'lodash'; import $ from 'jquery'; import expect from '@kbn/expect'; -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; const dataTypesArray = { - 'series pos': require('../lib/fixtures/mock_data/date_histogram/_series'), - 'series pos neg': require('../lib/fixtures/mock_data/date_histogram/_series_pos_neg'), - 'series neg': require('../lib/fixtures/mock_data/date_histogram/_series_neg'), - 'term columns': require('../lib/fixtures/mock_data/terms/_columns'), - 'range rows': require('../lib/fixtures/mock_data/range/_rows'), - stackedSeries: require('../lib/fixtures/mock_data/date_histogram/_stacked_series'), + 'series pos': require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'), + 'series pos neg': require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg'), + 'series neg': require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg'), + 'term columns': require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns'), + 'range rows': require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows'), + stackedSeries: require('../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'), }; const visLibParams = { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/chart.js similarity index 92% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/chart.js index 9653f9abab6fb8..4c5e3db3162430 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/chart.js @@ -20,8 +20,9 @@ import d3 from 'd3'; import expect from '@kbn/expect'; -import { Chart } from '../../visualizations/_chart'; -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import { Chart } from '../../../../../../../plugins/vis_type_vislib/public/vislib/visualizations/_chart'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; describe('Vislib _chart Test Suite', function() { let vis; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/column_chart.js similarity index 89% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/column_chart.js index 2216294fcbac11..5cbd5948bc477a 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/column_chart.js @@ -23,17 +23,18 @@ import $ from 'jquery'; import expect from '@kbn/expect'; // Data -import series from '../lib/fixtures/mock_data/date_histogram/_series'; -import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; -import termsColumns from '../lib/fixtures/mock_data/terms/_columns'; -import histogramRows from '../lib/fixtures/mock_data/histogram/_rows'; -import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series'; - -import { seriesMonthlyInterval } from '../lib/fixtures/mock_data/date_histogram/_series_monthly_interval'; -import { rowsSeriesWithHoles } from '../lib/fixtures/mock_data/date_histogram/_rows_series_with_holes'; -import rowsWithZeros from '../lib/fixtures/mock_data/date_histogram/_rows'; -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import series from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg'; +import termsColumns from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns'; +import histogramRows from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows'; +import stackedSeries from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'; + +import { seriesMonthlyInterval } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval'; +import { rowsSeriesWithHoles } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes'; +import rowsWithZeros from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; // tuple, with the format [description, mode, data] const dataTypesArray = [ diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/gauge_chart.js similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/gauge_chart.js index fe25734fcbfde5..d8ce8f1f5f44bf 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/gauge_chart.js @@ -21,8 +21,9 @@ import $ from 'jquery'; import _ from 'lodash'; import expect from '@kbn/expect'; -import data from '../lib/fixtures/mock_data/terms/_seriesMultiple'; -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import data from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; describe('Vislib Gauge Chart Test Suite', function() { let vis; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/heatmap_chart.js similarity index 89% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/heatmap_chart.js index f4c952be191deb..765b9118e68445 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/heatmap_chart.js @@ -23,13 +23,13 @@ import d3 from 'd3'; import expect from '@kbn/expect'; // Data -import series from '../lib/fixtures/mock_data/date_histogram/_series'; -import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; -import termsColumns from '../lib/fixtures/mock_data/terms/_columns'; -import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series'; - -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import series from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg'; +import termsColumns from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns'; +import stackedSeries from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; // tuple, with the format [description, mode, data] const dataTypesArray = [ diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/line_chart.js similarity index 89% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/line_chart.js index 1269fe7bcf62e9..691417e968eedb 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/line_chart.js @@ -23,14 +23,14 @@ import $ from 'jquery'; import _ from 'lodash'; // Data -import seriesPos from '../lib/fixtures/mock_data/date_histogram/_series'; -import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; -import histogramColumns from '../lib/fixtures/mock_data/histogram/_columns'; -import rangeRows from '../lib/fixtures/mock_data/range/_rows'; -import termSeries from '../lib/fixtures/mock_data/terms/_series'; - -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import seriesPos from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg'; +import histogramColumns from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns'; +import rangeRows from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows'; +import termSeries from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; const dataTypes = [ ['series pos', seriesPos], diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/pie_chart.js similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/pie_chart.js index caafb2c636271a..506ad2af85c34d 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/pie_chart.js @@ -22,7 +22,8 @@ import _ from 'lodash'; import $ from 'jquery'; import expect from '@kbn/expect'; -import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; +import { getMockUiState } from '../../../../../../../plugins/vis_type_vislib/public/fixtures/mocks'; +import { getVis } from '../_vis_fixture'; import { pieChartMockData } from './pie_chart_mock_data'; const names = ['rows', 'columns', 'slices']; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart_mock_data.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/pie_chart_mock_data.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart_mock_data.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_vislib/visualizations/pie_chart_mock_data.js diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index 6ccbc13aeeb571..63664661036520 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -21,6 +21,8 @@ // these are necessary to bootstrap the local angular. // They can stay even after NP cutover import angular from 'angular'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { EuiIcon } from '@elastic/eui'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { CoreStart, LegacyCoreStart } from 'kibana/public'; diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss index d49c59970f521d..26805554370b96 100644 --- a/src/legacy/core_plugins/kibana/public/index.scss +++ b/src/legacy/core_plugins/kibana/public/index.scss @@ -7,16 +7,9 @@ // Public UI styles @import 'src/legacy/ui/public/index'; -// vis_type_vislib UI styles -@import 'src/legacy/core_plugins/vis_type_vislib/public/index'; - // Discover styles @import 'discover/index'; -// Visualization styles are imported here for running karma Browser tests -// should be somehow included through the "visualizations" plugin initialization -@import '../../../../plugins/visualizations/public/index'; - // Has to come after visualize because of some // bad cascading in the Editor layout @import '../../../../plugins/maps_legacy/public/index'; diff --git a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js index 6e1b0b71609417..87592cf4e750e4 100644 --- a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js @@ -54,6 +54,7 @@ import { BaseVisType } from '../../../../../plugins/visualizations/public/vis_ty import { setInjectedVarFunc } from '../../../../../plugins/maps_legacy/public/kibana_services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceSettings } from '../../../../../plugins/maps_legacy/public/map/service_settings'; +import { getBaseMapsVis } from '../../../../../plugins/maps_legacy/public'; const THRESHOLD = 0.45; const PIXEL_DIFF = 96; @@ -101,7 +102,7 @@ describe('RegionMapsVisualizationTests', function() { let getManifestStub; beforeEach( - ngMock.inject((Private, $injector) => { + ngMock.inject(() => { setInjectedVarFunc(injectedVar => { switch (injectedVar) { case 'mapConfig': @@ -127,17 +128,28 @@ describe('RegionMapsVisualizationTests', function() { } }); const serviceSettings = new ServiceSettings(); - const uiSettings = $injector.get('config'); const regionmapsConfig = { includeElasticMapsService: true, layers: [], }; + const coreSetupMock = { + notifications: { + toasts: {}, + }, + uiSettings: { + get: () => {}, + }, + injectedMetadata: { + getInjectedVar: () => {}, + }, + }; + const BaseMapsVisualization = getBaseMapsVis(coreSetupMock, serviceSettings); dependencies = { serviceSettings, - $injector, regionmapsConfig, - uiSettings, + uiSettings: coreSetupMock.uiSettings, + BaseMapsVisualization, }; regionMapVisType = new BaseVisType(createRegionMapTypeDefinition(dependencies)); diff --git a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx index 31a27c4da7fcfe..5604067433f136 100644 --- a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx +++ b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx @@ -32,8 +32,7 @@ import { SelectOption, SwitchOption, } from '../../../../../plugins/charts/public'; -import { WmsOptions } from '../../../tile_map/public/components/wms_options'; -import { RegionMapVisParams } from '../types'; +import { RegionMapVisParams, WmsOptions } from '../../../../../plugins/maps_legacy/public'; const mapLayerForOption = ({ layerId, name }: VectorLayer) => ({ text: name, diff --git a/src/legacy/core_plugins/region_map/public/legacy.ts b/src/legacy/core_plugins/region_map/public/legacy.ts index b0cc767a044e88..4bbd839331e56b 100644 --- a/src/legacy/core_plugins/region_map/public/legacy.ts +++ b/src/legacy/core_plugins/region_map/public/legacy.ts @@ -21,17 +21,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { RegionMapPluginSetupDependencies } from './plugin'; -import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, visualizations: npSetup.plugins.visualizations, mapsLegacy: npSetup.plugins.mapsLegacy, - - // Temporary solution - // It will be removed when all dependent services are migrated to the new platform. - __LEGACY: new LegacyDependenciesPlugin(), }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/region_map/public/plugin.ts b/src/legacy/core_plugins/region_map/public/plugin.ts index 1453c2155e2d6f..08a73517dc13b3 100644 --- a/src/legacy/core_plugins/region_map/public/plugin.ts +++ b/src/legacy/core_plugins/region_map/public/plugin.ts @@ -25,28 +25,28 @@ import { } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; - -import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; - // @ts-ignore import { createRegionMapFn } from './region_map_fn'; // @ts-ignore import { createRegionMapTypeDefinition } from './region_map_type'; -import { IServiceSettings, MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public'; +import { + getBaseMapsVis, + IServiceSettings, + MapsLegacyPluginSetup, +} from '../../../../plugins/maps_legacy/public'; /** @private */ -interface RegionMapVisualizationDependencies extends LegacyDependenciesPluginSetup { +interface RegionMapVisualizationDependencies { uiSettings: IUiSettingsClient; regionmapsConfig: RegionMapsConfig; serviceSettings: IServiceSettings; - notificationService: any; + BaseMapsVisualization: any; } /** @internal */ export interface RegionMapPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; - __LEGACY: LegacyDependenciesPlugin; mapsLegacy: MapsLegacyPluginSetup; } @@ -66,14 +66,13 @@ export class RegionMapPlugin implements Plugin, void> { public async setup( core: CoreSetup, - { expressions, visualizations, mapsLegacy, __LEGACY }: RegionMapPluginSetupDependencies + { expressions, visualizations, mapsLegacy }: RegionMapPluginSetupDependencies ) { const visualizationDependencies: Readonly = { uiSettings: core.uiSettings, regionmapsConfig: core.injectedMetadata.getInjectedVar('regionmap') as RegionMapsConfig, serviceSettings: mapsLegacy.serviceSettings, - notificationService: core.notifications.toasts, - ...(await __LEGACY.setup()), + BaseMapsVisualization: getBaseMapsVis(core, mapsLegacy.serviceSettings), }; expressions.registerFunction(createRegionMapFn); diff --git a/src/legacy/core_plugins/region_map/public/region_map_type.js b/src/legacy/core_plugins/region_map/public/region_map_type.js index 9174b03cf843cf..b7ed14ed3706eb 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_type.js +++ b/src/legacy/core_plugins/region_map/public/region_map_type.js @@ -23,9 +23,7 @@ import { createRegionMapVisualization } from './region_map_visualization'; import { RegionMapOptions } from './components/region_map_options'; import { truncatedColorSchemas } from '../../../../plugins/charts/public'; import { Schemas } from '../../../../plugins/vis_default_editor/public'; - -// TODO: reference to TILE_MAP plugin should be removed -import { ORIGIN } from '../../tile_map/common/origin'; +import { ORIGIN } from '../../../../plugins/maps_legacy/public'; export function createRegionMapTypeDefinition(dependencies) { const { uiSettings, regionmapsConfig, serviceSettings } = dependencies; diff --git a/src/legacy/core_plugins/region_map/public/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/region_map_visualization.js index f08d53ee35c8d3..5dbc1ecad277f1 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/region_map_visualization.js @@ -21,30 +21,21 @@ import { i18n } from '@kbn/i18n'; import ChoroplethLayer from './choropleth_layer'; import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; import { toastNotifications } from 'ui/notify'; - -import { TileMapTooltipFormatter } from './tooltip_formatter'; import { truncatedColorMaps } from '../../../../plugins/charts/public'; - -// TODO: reference to TILE_MAP plugin should be removed -import { BaseMapsVisualizationProvider } from '../../tile_map/public/base_maps_visualization'; +import { tooltipFormatter } from './tooltip_formatter'; +import { mapTooltipProvider } from '../../../../plugins/maps_legacy/public'; export function createRegionMapVisualization({ serviceSettings, - $injector, uiSettings, - notificationService, + BaseMapsVisualization, }) { - const BaseMapsVisualization = new BaseMapsVisualizationProvider( - serviceSettings, - notificationService - ); - const tooltipFormatter = new TileMapTooltipFormatter($injector); - return class RegionMapsVisualization extends BaseMapsVisualization { constructor(container, vis) { super(container, vis); this._vis = this.vis; this._choroplethLayer = null; + this._tooltipFormatter = mapTooltipProvider(container, tooltipFormatter); } async render(esResponse, visParams) { @@ -89,7 +80,7 @@ export function createRegionMapVisualization({ this._choroplethLayer.setMetrics(results, metricFieldFormatter, valueColumn.name); if (termColumn && valueColumn) { this._choroplethLayer.setTooltipFormatter( - tooltipFormatter, + this._tooltipFormatter, metricFieldFormatter, termColumn.name, valueColumn.name @@ -123,7 +114,7 @@ export function createRegionMapVisualization({ this._choroplethLayer.setColorRamp(truncatedColorMaps[visParams.colorSchema].value); this._choroplethLayer.setLineWeight(visParams.outlineWeight); this._choroplethLayer.setTooltipFormatter( - tooltipFormatter, + this._tooltipFormatter, metricFieldFormatter, this._metricLabel ); diff --git a/src/legacy/core_plugins/region_map/public/tooltip.html b/src/legacy/core_plugins/region_map/public/tooltip.html deleted file mode 100644 index 0d57120c80a983..00000000000000 --- a/src/legacy/core_plugins/region_map/public/tooltip.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - -
{{detail.label}}{{detail.value}}
diff --git a/src/legacy/core_plugins/region_map/public/tooltip_formatter.js b/src/legacy/core_plugins/region_map/public/tooltip_formatter.js index 6df08aea0baa69..8d38095ac25e07 100644 --- a/src/legacy/core_plugins/region_map/public/tooltip_formatter.js +++ b/src/legacy/core_plugins/region_map/public/tooltip_formatter.js @@ -17,39 +17,24 @@ * under the License. */ -import $ from 'jquery'; -import template from './tooltip.html'; - -export const TileMapTooltipFormatter = $injector => { - const $rootScope = $injector.get('$rootScope'); - const $compile = $injector.get('$compile'); - - const $tooltipScope = $rootScope.$new(); - const $el = $('
').html(template); - - $compile($el)($tooltipScope); - - return function tooltipFormatter(metric, fieldFormatter, fieldName, metricName) { - if (!metric) { - return ''; - } - - $tooltipScope.details = []; - if (fieldName && metric) { - $tooltipScope.details.push({ - label: fieldName, - value: metric.term, - }); - } - - if (metric) { - $tooltipScope.details.push({ - label: metricName, - value: fieldFormatter ? fieldFormatter.convert(metric.value, 'text') : metric.value, - }); - } - - $tooltipScope.$apply(); - return $el.html(); - }; -}; +export function tooltipFormatter(metric, fieldFormatter, fieldName, metricName) { + if (!metric) { + return ''; + } + + const details = []; + if (fieldName && metric) { + details.push({ + label: fieldName, + value: metric.term, + }); + } + + if (metric) { + details.push({ + label: metricName, + value: fieldFormatter ? fieldFormatter.convert(metric.value, 'text') : metric.value, + }); + } + return details; +} diff --git a/src/legacy/core_plugins/region_map/public/util.ts b/src/legacy/core_plugins/region_map/public/util.ts index 24c721da1f31ac..b4e0dcd5f35102 100644 --- a/src/legacy/core_plugins/region_map/public/util.ts +++ b/src/legacy/core_plugins/region_map/public/util.ts @@ -18,8 +18,7 @@ */ import { FileLayer, VectorLayer } from '../../../../plugins/maps_legacy/public'; -// TODO: reference to TILE_MAP plugin should be removed -import { ORIGIN } from '../../../../legacy/core_plugins/tile_map/common/origin'; +import { ORIGIN } from '../../../../plugins/maps_legacy/public'; export const mapToLayerWithId = (prefix: string, layer: FileLayer): VectorLayer => ({ ...layer, diff --git a/src/legacy/core_plugins/tests_bundle/index.js b/src/legacy/core_plugins/tests_bundle/index.js index e1966a9e8b2667..3348096c0e2f1e 100644 --- a/src/legacy/core_plugins/tests_bundle/index.js +++ b/src/legacy/core_plugins/tests_bundle/index.js @@ -18,6 +18,7 @@ */ import { createReadStream } from 'fs'; +import { resolve } from 'path'; import globby from 'globby'; import MultiStream from 'multistream'; @@ -40,6 +41,7 @@ export default kibana => { }, uiExports: { + styleSheetPaths: resolve(__dirname, 'public/index.scss'), async __bundleProvider__(kbnServer) { const modules = new Set(); diff --git a/src/legacy/core_plugins/tests_bundle/public/index.scss b/src/legacy/core_plugins/tests_bundle/public/index.scss new file mode 100644 index 00000000000000..8020cef8d84927 --- /dev/null +++ b/src/legacy/core_plugins/tests_bundle/public/index.scss @@ -0,0 +1,6 @@ +@import 'src/legacy/ui/public/styles/styling_constants'; + +// This file pulls some styles of NP plugins into the legacy test stylesheet +// so they are available for karma browser tests. +@import '../../../../plugins/vis_type_vislib/public/index'; +@import '../../../../plugins/visualizations/public/index'; diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js index 3904c43707906b..bce2e157ebbc8d 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js @@ -53,6 +53,7 @@ import { import { ServiceSettings } from '../../../../../plugins/maps_legacy/public/map/service_settings'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { setInjectedVarFunc } from '../../../../../plugins/maps_legacy/public/kibana_services'; +import { getBaseMapsVis } from '../../../../../plugins/maps_legacy/public'; function mockRawData() { const stack = [dummyESResponse]; @@ -114,15 +115,26 @@ describe('CoordinateMapsVisualizationTest', function() { return 'not found'; } }); + + const coreSetupMock = { + notifications: { + toasts: {}, + }, + uiSettings: {}, + injectedMetadata: { + getInjectedVar: () => {}, + }, + }; const serviceSettings = new ServiceSettings(); + const BaseMapsVisualization = getBaseMapsVis(coreSetupMock, serviceSettings); const uiSettings = $injector.get('config'); dependencies = { - serviceSettings, - uiSettings, - $injector, - getPrecision, getZoomPrecision, + getPrecision, + BaseMapsVisualization, + uiSettings, + serviceSettings, }; visType = new BaseVisType(createTileMapTypeDefinition(dependencies)); diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/geohash_layer.js b/src/legacy/core_plugins/tile_map/public/__tests__/geohash_layer.js index fc029d6bccb6e6..bdf9cd806eb8b5 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/geohash_layer.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/geohash_layer.js @@ -24,7 +24,8 @@ import scaledCircleMarkersPng from './scaledCircleMarkers.png'; // import shadedCircleMarkersPng from './shadedCircleMarkers.png'; import { ImageComparator } from 'test_utils/image_comparator'; import GeoHashSampleData from './dummy_es_response.json'; -import { KibanaMap } from '../../../../../plugins/maps_legacy/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { KibanaMap } from '../../../../../plugins/maps_legacy/public/map/kibana_map'; describe('geohash_layer', function() { let domNode; diff --git a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx index 9ca42fe3e4074c..1efb0b2f884f85 100644 --- a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx @@ -28,9 +28,7 @@ import { SelectOption, SwitchOption, } from '../../../../../plugins/charts/public'; -import { WmsOptions } from './wms_options'; -import { TileMapVisParams } from '../types'; -import { MapTypes } from '../map_types'; +import { WmsOptions, TileMapVisParams, MapTypes } from '../../../../../plugins/maps_legacy/public'; export type TileMapOptionsProps = VisOptionsProps; diff --git a/src/legacy/core_plugins/tile_map/public/editors/_tooltip.html b/src/legacy/core_plugins/tile_map/public/editors/_tooltip.html deleted file mode 100644 index 9df5b94a21edad..00000000000000 --- a/src/legacy/core_plugins/tile_map/public/editors/_tooltip.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - -
{{detail.label}}{{detail.value}}
diff --git a/src/legacy/core_plugins/tile_map/public/editors/_tooltip_formatter.js b/src/legacy/core_plugins/tile_map/public/editors/_tooltip_formatter.js deleted file mode 100644 index eec90e512b4623..00000000000000 --- a/src/legacy/core_plugins/tile_map/public/editors/_tooltip_formatter.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 $ from 'jquery'; -import { i18n } from '@kbn/i18n'; - -import template from './_tooltip.html'; - -export function TileMapTooltipFormatterProvider($injector) { - const $rootScope = $injector.get('$rootScope'); - const $compile = $injector.get('$compile'); - - const $tooltipScope = $rootScope.$new(); - const $el = $('
').html(template); - - $compile($el)($tooltipScope); - - return function tooltipFormatter(metricTitle, metricFormat, feature) { - if (!feature) { - return ''; - } - - $tooltipScope.details = [ - { - label: metricTitle, - value: metricFormat(feature.properties.value), - }, - { - label: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { - defaultMessage: 'Latitude', - }), - value: feature.geometry.coordinates[1], - }, - { - label: i18n.translate('tileMap.tooltipFormatter.longitudeLabel', { - defaultMessage: 'Longitude', - }), - value: feature.geometry.coordinates[0], - }, - ]; - - $tooltipScope.$apply(); - - return $el.html(); - }; -} diff --git a/src/legacy/core_plugins/tile_map/public/geohash_layer.js b/src/legacy/core_plugins/tile_map/public/geohash_layer.js index b9acf1a15208f7..f0261483d302d2 100644 --- a/src/legacy/core_plugins/tile_map/public/geohash_layer.js +++ b/src/legacy/core_plugins/tile_map/public/geohash_layer.js @@ -20,12 +20,11 @@ import L from 'leaflet'; import { min, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { KibanaMapLayer } from '../../../../plugins/maps_legacy/public'; +import { KibanaMapLayer, MapTypes } from '../../../../plugins/maps_legacy/public'; import { HeatmapMarkers } from './markers/heatmap'; import { ScaledCirclesMarkers } from './markers/scaled_circles'; import { ShadedCirclesMarkers } from './markers/shaded_circles'; import { GeohashGridMarkers } from './markers/geohash_grid'; -import { MapTypes } from './map_types'; export class GeohashLayer extends KibanaMapLayer { constructor(featureCollection, featureCollectionMetaData, options, zoom, kibanaMap) { diff --git a/src/legacy/core_plugins/tile_map/public/legacy.ts b/src/legacy/core_plugins/tile_map/public/legacy.ts index 741e118750f320..dd8d4c6e9311e6 100644 --- a/src/legacy/core_plugins/tile_map/public/legacy.ts +++ b/src/legacy/core_plugins/tile_map/public/legacy.ts @@ -21,17 +21,12 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { TileMapPluginSetupDependencies } from './plugin'; -import { LegacyDependenciesPlugin } from './shim'; import { plugin } from '.'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, visualizations: npSetup.plugins.visualizations, mapsLegacy: npSetup.plugins.mapsLegacy, - - // Temporary solution - // It will be removed when all dependent services are migrated to the new platform. - __LEGACY: new LegacyDependenciesPlugin(), }; const pluginInstance = plugin({} as PluginInitializerContext); diff --git a/src/legacy/core_plugins/tile_map/public/plugin.ts b/src/legacy/core_plugins/tile_map/public/plugin.ts index 2b97407b17b38c..aa1460a7e2890a 100644 --- a/src/legacy/core_plugins/tile_map/public/plugin.ts +++ b/src/legacy/core_plugins/tile_map/public/plugin.ts @@ -25,22 +25,21 @@ import { } from '../../../../core/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; - -import { LegacyDependenciesPlugin, LegacyDependenciesPluginSetup } from './shim'; +// TODO: Determine why visualizations don't populate without this +import 'angular-sanitize'; // @ts-ignore import { createTileMapFn } from './tile_map_fn'; // @ts-ignore import { createTileMapTypeDefinition } from './tile_map_type'; -import { IServiceSettings, MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public'; +import { getBaseMapsVis, MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public'; /** @private */ -interface TileMapVisualizationDependencies extends LegacyDependenciesPluginSetup { - serviceSettings: IServiceSettings; +interface TileMapVisualizationDependencies { uiSettings: IUiSettingsClient; getZoomPrecision: any; getPrecision: any; - notificationService: any; + BaseMapsVisualization: any; } /** @internal */ @@ -48,7 +47,6 @@ export interface TileMapPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; mapsLegacy: MapsLegacyPluginSetup; - __LEGACY: LegacyDependenciesPlugin; } /** @internal */ @@ -61,16 +59,14 @@ export class TileMapPlugin implements Plugin, void> { public async setup( core: CoreSetup, - { expressions, visualizations, mapsLegacy, __LEGACY }: TileMapPluginSetupDependencies + { expressions, visualizations, mapsLegacy }: TileMapPluginSetupDependencies ) { - const { getZoomPrecision, getPrecision, serviceSettings } = mapsLegacy; + const { getZoomPrecision, getPrecision } = mapsLegacy; const visualizationDependencies: Readonly = { - serviceSettings, getZoomPrecision, getPrecision, - notificationService: core.notifications.toasts, + BaseMapsVisualization: getBaseMapsVis(core, mapsLegacy.serviceSettings), uiSettings: core.uiSettings, - ...(await __LEGACY.setup()), }; expressions.registerFunction(() => createTileMapFn(visualizationDependencies)); diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_type.js b/src/legacy/core_plugins/tile_map/public/tile_map_type.js index ae3a839b600e94..ca6a586d220080 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_type.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_type.js @@ -19,11 +19,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { convertToGeoJson } from '../../../../plugins/maps_legacy/public'; +import { convertToGeoJson, MapTypes } from '../../../../plugins/maps_legacy/public'; import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { createTileMapVisualization } from './tile_map_visualization'; import { TileMapOptions } from './components/tile_map_options'; -import { MapTypes } from './map_types'; import { supportsCssFilters } from './css_filters'; import { truncatedColorSchemas } from '../../../../plugins/charts/public'; diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js b/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js index fdce8bc51fe869..6a7bda5e188831 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_visualization.js @@ -19,30 +19,24 @@ import { get } from 'lodash'; import { GeohashLayer } from './geohash_layer'; -import { BaseMapsVisualizationProvider } from './base_maps_visualization'; -import { TileMapTooltipFormatterProvider } from './editors/_tooltip_formatter'; import { npStart } from 'ui/new_platform'; import { getFormat } from '../../../ui/public/visualize/loader/pipeline_helpers/utilities'; -import { scaleBounds, geoContains } from '../../../../plugins/maps_legacy/public'; - -export const createTileMapVisualization = ({ - serviceSettings, - $injector, - getZoomPrecision, - getPrecision, - notificationService, -}) => { - const BaseMapsVisualization = new BaseMapsVisualizationProvider( - serviceSettings, - notificationService - ); - const tooltipFormatter = new TileMapTooltipFormatterProvider($injector); +import { + scaleBounds, + geoContains, + mapTooltipProvider, +} from '../../../../plugins/maps_legacy/public'; +import { tooltipFormatter } from './tooltip_formatter'; + +export const createTileMapVisualization = dependencies => { + const { getZoomPrecision, getPrecision, BaseMapsVisualization } = dependencies; return class CoordinateMapsVisualization extends BaseMapsVisualization { constructor(element, vis) { super(element, vis); this._geohashLayer = null; + this._tooltipFormatter = mapTooltipProvider(element, tooltipFormatter); } updateGeohashAgg = () => { @@ -190,18 +184,15 @@ export const createTileMapVisualization = ({ const metricDimension = this._params.dimensions.metric; const metricLabel = metricDimension ? metricDimension.label : ''; const metricFormat = getFormat(metricDimension && metricDimension.format); - const boundTooltipFormatter = tooltipFormatter.bind( - null, - metricLabel, - metricFormat.getConverterFor('text') - ); return { label: metricLabel, valueFormatter: this._geoJsonFeatureCollectionAndMeta ? metricFormat.getConverterFor('text') : null, - tooltipFormatter: this._geoJsonFeatureCollectionAndMeta ? boundTooltipFormatter : null, + tooltipFormatter: this._geoJsonFeatureCollectionAndMeta + ? this._tooltipFormatter.bind(null, metricLabel, metricFormat.getConverterFor('text')) + : null, mapType: newParams.mapType, isFilteredByCollar: this._isFilteredByCollar(), colorRamp: newParams.colorSchema, diff --git a/src/legacy/core_plugins/tile_map/public/shim/legacy_dependencies_plugin.ts b/src/legacy/core_plugins/tile_map/public/tooltip_formatter.js similarity index 57% rename from src/legacy/core_plugins/tile_map/public/shim/legacy_dependencies_plugin.ts rename to src/legacy/core_plugins/tile_map/public/tooltip_formatter.js index 5296e98b09efe8..1c87d4dcca2b57 100644 --- a/src/legacy/core_plugins/tile_map/public/shim/legacy_dependencies_plugin.ts +++ b/src/legacy/core_plugins/tile_map/public/tooltip_formatter.js @@ -17,27 +17,29 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { CoreStart, Plugin } from 'kibana/public'; -// TODO: Determine why visualizations don't populate without this -import 'angular-sanitize'; +import { i18n } from '@kbn/i18n'; -/** @internal */ -export interface LegacyDependenciesPluginSetup { - $injector: any; -} - -export class LegacyDependenciesPlugin - implements Plugin, void> { - public async setup() { - const $injector = await chrome.dangerouslyGetActiveInjector(); - - return { - $injector, - } as LegacyDependenciesPluginSetup; +export function tooltipFormatter(metricTitle, metricFormat, feature) { + if (!feature) { + return ''; } - public start(core: CoreStart) { - // nothing to do here yet - } + return [ + { + label: metricTitle, + value: metricFormat(feature.properties.value), + }, + { + label: i18n.translate('tileMap.tooltipFormatter.latitudeLabel', { + defaultMessage: 'Latitude', + }), + value: feature.geometry.coordinates[1], + }, + { + label: i18n.translate('tileMap.tooltipFormatter.longitudeLabel', { + defaultMessage: 'Longitude', + }), + value: feature.geometry.coordinates[0], + }, + ]; } diff --git a/src/legacy/core_plugins/timelion/public/app.js b/src/legacy/core_plugins/timelion/public/app.js index 7f5c7d4664af86..80ffa440e7285a 100644 --- a/src/legacy/core_plugins/timelion/public/app.js +++ b/src/legacy/core_plugins/timelion/public/app.js @@ -18,6 +18,8 @@ */ import _ from 'lodash'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { i18n } from '@kbn/i18n'; diff --git a/src/legacy/core_plugins/vis_type_vislib/index.ts b/src/legacy/core_plugins/vis_type_vislib/index.ts deleted file mode 100644 index da9476285a9b2e..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/index.ts +++ /dev/null @@ -1,44 +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 { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../types'; - -const visTypeVislibPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'vis_type_vislib', - require: ['kibana', 'elasticsearch', 'interpreter'], - publicDir: resolve(__dirname, 'public'), - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - uiExports: { - hacks: [resolve(__dirname, 'public/legacy')], - injectDefaultVars: server => ({}), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default visTypeVislibPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_vislib/package.json b/src/legacy/core_plugins/vis_type_vislib/package.json deleted file mode 100644 index e30a9e2b358342..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "vis_type_vislib", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts deleted file mode 100644 index 579caa1cb88f67..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts +++ /dev/null @@ -1,43 +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 { npSetup, npStart } from 'ui/new_platform'; -import { PluginInitializerContext } from 'kibana/public'; - -import { plugin } from '.'; -import { - VisTypeVislibPluginSetupDependencies, - VisTypeVislibPluginStartDependencies, -} from './plugin'; - -const setupPlugins: Readonly = { - expressions: npSetup.plugins.expressions, - visualizations: npSetup.plugins.visualizations, - charts: npSetup.plugins.charts, - visTypeXy: npSetup.plugins.visTypeXy, -}; - -const startPlugins: Readonly = { - data: npStart.plugins.data, -}; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, setupPlugins); -export const start = pluginInstance.start(npStart.core, startPlugins); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts deleted file mode 100644 index c04ffa506eb04d..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.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. - */ - -import { search } from '../../../../plugins/data/public'; -export const { tabifyAggResponse, tabifyGetColumns } = search; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_columns.js deleted file mode 100644 index b5b14c279b40e0..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_columns.js +++ /dev/null @@ -1,300 +0,0 @@ -import moment from 'moment'; - -export default { - 'columns': [ - { - 'label': '200: response', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1415826608440, - 'max': 1415827508440 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - }, - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1415826600000, - 'y': 4 - }, - { - 'x': 1415826630000, - 'y': 8 - }, - { - 'x': 1415826660000, - 'y': 7 - }, - { - 'x': 1415826690000, - 'y': 5 - }, - { - 'x': 1415826720000, - 'y': 5 - }, - { - 'x': 1415826750000, - 'y': 4 - }, - { - 'x': 1415826780000, - 'y': 10 - }, - { - 'x': 1415826810000, - 'y': 7 - }, - { - 'x': 1415826840000, - 'y': 9 - }, - { - 'x': 1415826870000, - 'y': 8 - }, - { - 'x': 1415826900000, - 'y': 9 - }, - { - 'x': 1415826930000, - 'y': 8 - }, - { - 'x': 1415826960000, - 'y': 3 - }, - { - 'x': 1415826990000, - 'y': 9 - }, - { - 'x': 1415827020000, - 'y': 6 - }, - { - 'x': 1415827050000, - 'y': 8 - }, - { - 'x': 1415827080000, - 'y': 7 - }, - { - 'x': 1415827110000, - 'y': 4 - }, - { - 'x': 1415827140000, - 'y': 6 - }, - { - 'x': 1415827170000, - 'y': 10 - }, - { - 'x': 1415827200000, - 'y': 2 - }, - { - 'x': 1415827230000, - 'y': 8 - }, - { - 'x': 1415827260000, - 'y': 5 - }, - { - 'x': 1415827290000, - 'y': 6 - }, - { - 'x': 1415827320000, - 'y': 6 - }, - { - 'x': 1415827350000, - 'y': 10 - }, - { - 'x': 1415827380000, - 'y': 6 - }, - { - 'x': 1415827410000, - 'y': 6 - }, - { - 'x': 1415827440000, - 'y': 12 - }, - { - 'x': 1415827470000, - 'y': 9 - }, - { - 'x': 1415827500000, - 'y': 1 - } - ] - } - ] - }, - { - 'label': '503: response', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1415826608440, - 'max': 1415827508440 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - }, - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1415826630000, - 'y': 1 - }, - { - 'x': 1415826660000, - 'y': 1 - }, - { - 'x': 1415826720000, - 'y': 1 - }, - { - 'x': 1415826780000, - 'y': 1 - }, - { - 'x': 1415826900000, - 'y': 1 - }, - { - 'x': 1415827020000, - 'y': 1 - }, - { - 'x': 1415827080000, - 'y': 1 - }, - { - 'x': 1415827110000, - 'y': 2 - } - ] - } - ] - }, - { - 'label': '404: response', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1415826608440, - 'max': 1415827508440 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - }, - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1415826660000, - 'y': 1 - }, - { - 'x': 1415826720000, - 'y': 1 - }, - { - 'x': 1415826810000, - 'y': 1 - }, - { - 'x': 1415826960000, - 'y': 1 - }, - { - 'x': 1415827050000, - 'y': 1 - }, - { - 'x': 1415827260000, - 'y': 1 - }, - { - 'x': 1415827380000, - 'y': 1 - }, - { - 'x': 1415827410000, - 'y': 1 - } - ] - } - ] - } - ], - 'xAxisOrderedValues': [ - 1415826600000, - 1415826630000, - 1415826660000, - 1415826690000, - 1415826720000, - 1415826750000, - 1415826780000, - 1415826810000, - 1415826840000, - 1415826870000, - 1415826900000, - 1415826930000, - 1415826960000, - 1415826990000, - 1415827020000, - 1415827050000, - 1415827080000, - 1415827110000, - 1415827140000, - 1415827170000, - 1415827200000, - 1415827230000, - 1415827260000, - 1415827290000, - 1415827320000, - 1415827350000, - 1415827380000, - 1415827410000, - 1415827440000, - 1415827470000, - 1415827500000, - ], - 'hits': 225 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows.js deleted file mode 100644 index 98609d8ffbcd3b..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows.js +++ /dev/null @@ -1,1678 +0,0 @@ -import moment from 'moment'; - -export default { - 'rows': [ - { - 'label': '0.0-1000.0: bytes', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1415826260456, - 'max': 1415827160456 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - }, - 'series': [ - { - 'label': 'jpg', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'css', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'png', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827110000, - 'y': 1, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'php', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 2 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'gif', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 3, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826660000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 1, - 'y0': 1 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 1, - 'y0': 1 - }, - { - 'x': 1415827110000, - 'y': 1, - 'y0': 2 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - } - ] - }, - { - 'label': '1000.0-2000.0: bytes', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1415826260457, - 'max': 1415827160457 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - }, - 'series': [ - { - 'label': 'jpg', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 2, - 'y0': 0 - }, - { - 'x': 1415826840000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'css', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 2 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'png', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 2 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'php', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 2 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - }, - { - 'label': 'gif', - 'values': [ - { - 'x': 1415826240000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826270000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826300000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826330000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826360000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826390000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826420000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826450000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826480000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826510000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826540000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826570000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826600000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826630000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826660000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826690000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826720000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826750000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826780000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826810000, - 'y': 0, - 'y0': 2 - }, - { - 'x': 1415826840000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826870000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826900000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415826930000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826960000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415826990000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827020000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827050000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827080000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 1415827110000, - 'y': 0, - 'y0': 1 - }, - { - 'x': 1415827140000, - 'y': 0, - 'y0': 0 - } - ] - } - ] - } - ], - 'xAxisOrderedValues': [ - 1415826240000, - 1415826270000, - 1415826300000, - 1415826330000, - 1415826360000, - 1415826390000, - 1415826420000, - 1415826450000, - 1415826480000, - 1415826510000, - 1415826540000, - 1415826570000, - 1415826600000, - 1415826630000, - 1415826660000, - 1415826690000, - 1415826720000, - 1415826750000, - 1415826780000, - 1415826810000, - 1415826840000, - 1415826870000, - 1415826900000, - 1415826930000, - 1415826960000, - 1415826990000, - 1415827020000, - 1415827050000, - 1415827080000, - 1415827110000, - 1415827140000, - ], - 'hits': 236 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows_series_with_holes.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows_series_with_holes.js deleted file mode 100644 index 4ca631c7fc4972..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows_series_with_holes.js +++ /dev/null @@ -1,123 +0,0 @@ -import moment from 'moment'; - -export const rowsSeriesWithHoles = { - rows: [ - { - 'label': '', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'min': 1411761457636, - 'max': 1411762357636, - 'interval': 30000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1411761450000, - 'y': 41 - }, - { - 'x': 1411761510000, - 'y': 22 - }, - { - 'x': 1411761540000, - 'y': 17 - }, - { - 'x': 1411761840000, - 'y': 20 - }, - { - 'x': 1411761870000, - 'y': 20 - }, - { - 'x': 1411761900000, - 'y': 21 - }, - { - 'x': 1411761930000, - 'y': 17 - }, - { - 'x': 1411761960000, - 'y': 20 - }, - { - 'x': 1411761990000, - 'y': 13 - }, - { - 'x': 1411762020000, - 'y': 14 - }, - { - 'x': 1411762050000, - 'y': 25 - }, - { - 'x': 1411762080000, - 'y': 17 - }, - { - 'x': 1411762110000, - 'y': 14 - }, - { - 'x': 1411762140000, - 'y': 22 - }, - { - 'x': 1411762170000, - 'y': 14 - }, - { - 'x': 1411762200000, - 'y': 19 - }, - { - 'x': 1411762320000, - 'y': 15 - }, - { - 'x': 1411762350000, - 'y': 4 - } - ] - } - ], - 'hits': 533, - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'xAxisOrderedValues': [ - 1411761450000, - 1411761510000, - 1411761540000, - 1411761840000, - 1411761870000, - 1411761900000, - 1411761930000, - 1411761960000, - 1411761990000, - 1411762020000, - 1411762050000, - 1411762080000, - 1411762110000, - 1411762140000, - 1411762170000, - 1411762200000, - 1411762320000, - 1411762350000, - ], -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series.js deleted file mode 100644 index 13e2ab7b7fb1ac..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series.js +++ /dev/null @@ -1,184 +0,0 @@ -import moment from 'moment'; - -export default { - 'label': '', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'min': 1411761457636, - 'max': 1411762357636, - 'interval': 30000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1411761450000, - 'y': 41 - }, - { - 'x': 1411761480000, - 'y': 18 - }, - { - 'x': 1411761510000, - 'y': 22 - }, - { - 'x': 1411761540000, - 'y': 17 - }, - { - 'x': 1411761570000, - 'y': 17 - }, - { - 'x': 1411761600000, - 'y': 21 - }, - { - 'x': 1411761630000, - 'y': 16 - }, - { - 'x': 1411761660000, - 'y': 17 - }, - { - 'x': 1411761690000, - 'y': 15 - }, - { - 'x': 1411761720000, - 'y': 19 - }, - { - 'x': 1411761750000, - 'y': 11 - }, - { - 'x': 1411761780000, - 'y': 13 - }, - { - 'x': 1411761810000, - 'y': 24 - }, - { - 'x': 1411761840000, - 'y': 20 - }, - { - 'x': 1411761870000, - 'y': 20 - }, - { - 'x': 1411761900000, - 'y': 21 - }, - { - 'x': 1411761930000, - 'y': 17 - }, - { - 'x': 1411761960000, - 'y': 20 - }, - { - 'x': 1411761990000, - 'y': 13 - }, - { - 'x': 1411762020000, - 'y': 14 - }, - { - 'x': 1411762050000, - 'y': 25 - }, - { - 'x': 1411762080000, - 'y': 17 - }, - { - 'x': 1411762110000, - 'y': 14 - }, - { - 'x': 1411762140000, - 'y': 22 - }, - { - 'x': 1411762170000, - 'y': 14 - }, - { - 'x': 1411762200000, - 'y': 19 - }, - { - 'x': 1411762230000, - 'y': 22 - }, - { - 'x': 1411762260000, - 'y': 17 - }, - { - 'x': 1411762290000, - 'y': 8 - }, - { - 'x': 1411762320000, - 'y': 15 - }, - { - 'x': 1411762350000, - 'y': 4 - } - ] - } - ], - 'hits': 533, - 'xAxisOrderedValues': [ - 1411761450000, - 1411761480000, - 1411761510000, - 1411761540000, - 1411761570000, - 1411761600000, - 1411761630000, - 1411761660000, - 1411761690000, - 1411761720000, - 1411761750000, - 1411761780000, - 1411761810000, - 1411761840000, - 1411761870000, - 1411761900000, - 1411761930000, - 1411761960000, - 1411761990000, - 1411762020000, - 1411762050000, - 1411762080000, - 1411762110000, - 1411762140000, - 1411762170000, - 1411762200000, - 1411762230000, - 1411762260000, - 1411762290000, - 1411762320000, - 1411762350000, - ], - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_monthly_interval.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_monthly_interval.js deleted file mode 100644 index 6b7c574ab55511..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_monthly_interval.js +++ /dev/null @@ -1,89 +0,0 @@ -import moment from 'moment'; - -export const seriesMonthlyInterval = { - 'label': '', - 'xAxisLabel': '@timestamp per month', - 'ordered': { - 'date': true, - 'min': 1451631600000, - 'max': 1483254000000, - 'interval': 2678000000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1451631600000, - 'y': 10220 - }, - { - 'x': 1454310000000, - 'y': 9997, - }, - { - 'x': 1456815600000, - 'y': 10792, - }, - { - 'x': 1459490400000, - 'y': 10262 - }, - { - 'x': 1462082400000, - 'y': 10080 - }, - { - 'x': 1464760800000, - 'y': 11161 - }, - { - 'x': 1467352800000, - 'y': 9933 - }, - { - 'x': 1470031200000, - 'y': 10342 - }, - { - 'x': 1472709600000, - 'y': 10887 - }, - { - 'x': 1475301600000, - 'y': 9666 - }, - { - 'x': 1477980000000, - 'y': 9556 - }, - { - 'x': 1480575600000, - 'y': 11644 - } - ] - } - ], - 'hits': 533, - 'xAxisOrderedValues': [ - 1451631600000, - 1454310000000, - 1456815600000, - 1459490400000, - 1462082400000, - 1464760800000, - 1467352800000, - 1470031200000, - 1472709600000, - 1475301600000, - 1477980000000, - 1480575600000, - ], - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_neg.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_neg.js deleted file mode 100644 index ff5cd05b2f2d49..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_neg.js +++ /dev/null @@ -1,184 +0,0 @@ -import moment from 'moment'; - -export default { - 'label': '', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'min': 1411761457636, - 'max': 1411762357636, - 'interval': 30000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1411761450000, - 'y': -41 - }, - { - 'x': 1411761480000, - 'y': -18 - }, - { - 'x': 1411761510000, - 'y': -22 - }, - { - 'x': 1411761540000, - 'y': -17 - }, - { - 'x': 1411761570000, - 'y': -17 - }, - { - 'x': 1411761600000, - 'y': -21 - }, - { - 'x': 1411761630000, - 'y': -16 - }, - { - 'x': 1411761660000, - 'y': -17 - }, - { - 'x': 1411761690000, - 'y': -15 - }, - { - 'x': 1411761720000, - 'y': -19 - }, - { - 'x': 1411761750000, - 'y': -11 - }, - { - 'x': 1411761780000, - 'y': -13 - }, - { - 'x': 1411761810000, - 'y': -24 - }, - { - 'x': 1411761840000, - 'y': -20 - }, - { - 'x': 1411761870000, - 'y': -20 - }, - { - 'x': 1411761900000, - 'y': -21 - }, - { - 'x': 1411761930000, - 'y': -17 - }, - { - 'x': 1411761960000, - 'y': -20 - }, - { - 'x': 1411761990000, - 'y': -13 - }, - { - 'x': 1411762020000, - 'y': -14 - }, - { - 'x': 1411762050000, - 'y': -25 - }, - { - 'x': 1411762080000, - 'y': -17 - }, - { - 'x': 1411762110000, - 'y': -14 - }, - { - 'x': 1411762140000, - 'y': -22 - }, - { - 'x': 1411762170000, - 'y': -14 - }, - { - 'x': 1411762200000, - 'y': -19 - }, - { - 'x': 1411762230000, - 'y': -22 - }, - { - 'x': 1411762260000, - 'y': -17 - }, - { - 'x': 1411762290000, - 'y': -8 - }, - { - 'x': 1411762320000, - 'y': -15 - }, - { - 'x': 1411762350000, - 'y': -4 - } - ] - } - ], - 'hits': 533, - 'xAxisOrderedValues': [ - 1411761450000, - 1411761480000, - 1411761510000, - 1411761540000, - 1411761570000, - 1411761600000, - 1411761630000, - 1411761660000, - 1411761690000, - 1411761720000, - 1411761750000, - 1411761780000, - 1411761810000, - 1411761840000, - 1411761870000, - 1411761900000, - 1411761930000, - 1411761960000, - 1411761990000, - 1411762020000, - 1411762050000, - 1411762080000, - 1411762110000, - 1411762140000, - 1411762170000, - 1411762200000, - 1411762230000, - 1411762260000, - 1411762290000, - 1411762320000, - 1411762350000, - ], - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_pos_neg.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_pos_neg.js deleted file mode 100644 index 06d9b31dc6b578..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_pos_neg.js +++ /dev/null @@ -1,184 +0,0 @@ -import moment from 'moment'; - -export default { - 'label': '', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'min': 1411761457636, - 'max': 1411762357636, - 'interval': 30000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 1411761450000, - 'y': 41 - }, - { - 'x': 1411761480000, - 'y': 18 - }, - { - 'x': 1411761510000, - 'y': -22 - }, - { - 'x': 1411761540000, - 'y': -17 - }, - { - 'x': 1411761570000, - 'y': -17 - }, - { - 'x': 1411761600000, - 'y': -21 - }, - { - 'x': 1411761630000, - 'y': -16 - }, - { - 'x': 1411761660000, - 'y': 17 - }, - { - 'x': 1411761690000, - 'y': 15 - }, - { - 'x': 1411761720000, - 'y': 19 - }, - { - 'x': 1411761750000, - 'y': 11 - }, - { - 'x': 1411761780000, - 'y': -13 - }, - { - 'x': 1411761810000, - 'y': -24 - }, - { - 'x': 1411761840000, - 'y': -20 - }, - { - 'x': 1411761870000, - 'y': -20 - }, - { - 'x': 1411761900000, - 'y': -21 - }, - { - 'x': 1411761930000, - 'y': 17 - }, - { - 'x': 1411761960000, - 'y': 20 - }, - { - 'x': 1411761990000, - 'y': -13 - }, - { - 'x': 1411762020000, - 'y': -14 - }, - { - 'x': 1411762050000, - 'y': 25 - }, - { - 'x': 1411762080000, - 'y': -17 - }, - { - 'x': 1411762110000, - 'y': -14 - }, - { - 'x': 1411762140000, - 'y': -22 - }, - { - 'x': 1411762170000, - 'y': -14 - }, - { - 'x': 1411762200000, - 'y': 19 - }, - { - 'x': 1411762230000, - 'y': 22 - }, - { - 'x': 1411762260000, - 'y': 17 - }, - { - 'x': 1411762290000, - 'y': 8 - }, - { - 'x': 1411762320000, - 'y': -15 - }, - { - 'x': 1411762350000, - 'y': -4 - } - ] - } - ], - 'hits': 533, - 'xAxisOrderedValues': [ - 1411761450000, - 1411761480000, - 1411761510000, - 1411761540000, - 1411761570000, - 1411761600000, - 1411761630000, - 1411761660000, - 1411761690000, - 1411761720000, - 1411761750000, - 1411761780000, - 1411761810000, - 1411761840000, - 1411761870000, - 1411761900000, - 1411761930000, - 1411761960000, - 1411761990000, - 1411762020000, - 1411762050000, - 1411762080000, - 1411762110000, - 1411762140000, - 1411762170000, - 1411762200000, - 1411762230000, - 1411762260000, - 1411762290000, - 1411762320000, - 1411762350000, - ], - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series.js deleted file mode 100644 index 5208c7e996cd87..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series.js +++ /dev/null @@ -1,1557 +0,0 @@ -import moment from 'moment'; - -export default { - 'label': '', - 'xAxisLabel': '@timestamp per 10 min', - 'ordered': { - 'date': true, - 'min': 1413544140087, - 'max': 1413587340087, - 'interval': 600000 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'html', - 'values': [ - { - 'x': 1413543600000, - 'y': 140 - }, - { - 'x': 1413544200000, - 'y': 1388 - }, - { - 'x': 1413544800000, - 'y': 1308 - }, - { - 'x': 1413545400000, - 'y': 1356 - }, - { - 'x': 1413546000000, - 'y': 1314 - }, - { - 'x': 1413546600000, - 'y': 1343 - }, - { - 'x': 1413547200000, - 'y': 1353 - }, - { - 'x': 1413547800000, - 'y': 1353 - }, - { - 'x': 1413548400000, - 'y': 1334 - }, - { - 'x': 1413549000000, - 'y': 1433 - }, - { - 'x': 1413549600000, - 'y': 1331 - }, - { - 'x': 1413550200000, - 'y': 1349 - }, - { - 'x': 1413550800000, - 'y': 1323 - }, - { - 'x': 1413551400000, - 'y': 1203 - }, - { - 'x': 1413552000000, - 'y': 1231 - }, - { - 'x': 1413552600000, - 'y': 1227 - }, - { - 'x': 1413553200000, - 'y': 1187 - }, - { - 'x': 1413553800000, - 'y': 1119 - }, - { - 'x': 1413554400000, - 'y': 1159 - }, - { - 'x': 1413555000000, - 'y': 1117 - }, - { - 'x': 1413555600000, - 'y': 1152 - }, - { - 'x': 1413556200000, - 'y': 1057 - }, - { - 'x': 1413556800000, - 'y': 1009 - }, - { - 'x': 1413557400000, - 'y': 979 - }, - { - 'x': 1413558000000, - 'y': 975 - }, - { - 'x': 1413558600000, - 'y': 848 - }, - { - 'x': 1413559200000, - 'y': 873 - }, - { - 'x': 1413559800000, - 'y': 808 - }, - { - 'x': 1413560400000, - 'y': 784 - }, - { - 'x': 1413561000000, - 'y': 799 - }, - { - 'x': 1413561600000, - 'y': 684 - }, - { - 'x': 1413562200000, - 'y': 727 - }, - { - 'x': 1413562800000, - 'y': 621 - }, - { - 'x': 1413563400000, - 'y': 615 - }, - { - 'x': 1413564000000, - 'y': 569 - }, - { - 'x': 1413564600000, - 'y': 523 - }, - { - 'x': 1413565200000, - 'y': 474 - }, - { - 'x': 1413565800000, - 'y': 470 - }, - { - 'x': 1413566400000, - 'y': 466 - }, - { - 'x': 1413567000000, - 'y': 394 - }, - { - 'x': 1413567600000, - 'y': 404 - }, - { - 'x': 1413568200000, - 'y': 389 - }, - { - 'x': 1413568800000, - 'y': 312 - }, - { - 'x': 1413569400000, - 'y': 274 - }, - { - 'x': 1413570000000, - 'y': 285 - }, - { - 'x': 1413570600000, - 'y': 299 - }, - { - 'x': 1413571200000, - 'y': 207 - }, - { - 'x': 1413571800000, - 'y': 213 - }, - { - 'x': 1413572400000, - 'y': 119 - }, - { - 'x': 1413573600000, - 'y': 122 - }, - { - 'x': 1413574200000, - 'y': 169 - }, - { - 'x': 1413574800000, - 'y': 151 - }, - { - 'x': 1413575400000, - 'y': 152 - }, - { - 'x': 1413576000000, - 'y': 115 - }, - { - 'x': 1413576600000, - 'y': 117 - }, - { - 'x': 1413577200000, - 'y': 108 - }, - { - 'x': 1413577800000, - 'y': 100 - }, - { - 'x': 1413578400000, - 'y': 78 - }, - { - 'x': 1413579000000, - 'y': 88 - }, - { - 'x': 1413579600000, - 'y': 63 - }, - { - 'x': 1413580200000, - 'y': 58 - }, - { - 'x': 1413580800000, - 'y': 45 - }, - { - 'x': 1413581400000, - 'y': 57 - }, - { - 'x': 1413582000000, - 'y': 34 - }, - { - 'x': 1413582600000, - 'y': 41 - }, - { - 'x': 1413583200000, - 'y': 24 - }, - { - 'x': 1413583800000, - 'y': 27 - }, - { - 'x': 1413584400000, - 'y': 19 - }, - { - 'x': 1413585000000, - 'y': 24 - }, - { - 'x': 1413585600000, - 'y': 18 - }, - { - 'x': 1413586200000, - 'y': 17 - }, - { - 'x': 1413586800000, - 'y': 14 - } - ] - }, - { - 'label': 'php', - 'values': [ - { - 'x': 1413543600000, - 'y': 90 - }, - { - 'x': 1413544200000, - 'y': 949 - }, - { - 'x': 1413544800000, - 'y': 1012 - }, - { - 'x': 1413545400000, - 'y': 1027 - }, - { - 'x': 1413546000000, - 'y': 1073 - }, - { - 'x': 1413546600000, - 'y': 992 - }, - { - 'x': 1413547200000, - 'y': 1005 - }, - { - 'x': 1413547800000, - 'y': 1014 - }, - { - 'x': 1413548400000, - 'y': 987 - }, - { - 'x': 1413549000000, - 'y': 982 - }, - { - 'x': 1413549600000, - 'y': 1086 - }, - { - 'x': 1413550200000, - 'y': 998 - }, - { - 'x': 1413550800000, - 'y': 935 - }, - { - 'x': 1413551400000, - 'y': 995 - }, - { - 'x': 1413552000000, - 'y': 926 - }, - { - 'x': 1413552600000, - 'y': 897 - }, - { - 'x': 1413553200000, - 'y': 873 - }, - { - 'x': 1413553800000, - 'y': 885 - }, - { - 'x': 1413554400000, - 'y': 859 - }, - { - 'x': 1413555000000, - 'y': 852 - }, - { - 'x': 1413555600000, - 'y': 779 - }, - { - 'x': 1413556200000, - 'y': 739 - }, - { - 'x': 1413556800000, - 'y': 783 - }, - { - 'x': 1413557400000, - 'y': 784 - }, - { - 'x': 1413558000000, - 'y': 687 - }, - { - 'x': 1413558600000, - 'y': 660 - }, - { - 'x': 1413559200000, - 'y': 672 - }, - { - 'x': 1413559800000, - 'y': 600 - }, - { - 'x': 1413560400000, - 'y': 659 - }, - { - 'x': 1413561000000, - 'y': 540 - }, - { - 'x': 1413561600000, - 'y': 539 - }, - { - 'x': 1413562200000, - 'y': 481 - }, - { - 'x': 1413562800000, - 'y': 498 - }, - { - 'x': 1413563400000, - 'y': 444 - }, - { - 'x': 1413564000000, - 'y': 452 - }, - { - 'x': 1413564600000, - 'y': 408 - }, - { - 'x': 1413565200000, - 'y': 358 - }, - { - 'x': 1413565800000, - 'y': 321 - }, - { - 'x': 1413566400000, - 'y': 305 - }, - { - 'x': 1413567000000, - 'y': 292 - }, - { - 'x': 1413567600000, - 'y': 289 - }, - { - 'x': 1413568200000, - 'y': 239 - }, - { - 'x': 1413568800000, - 'y': 256 - }, - { - 'x': 1413569400000, - 'y': 220 - }, - { - 'x': 1413570000000, - 'y': 205 - }, - { - 'x': 1413570600000, - 'y': 201 - }, - { - 'x': 1413571200000, - 'y': 183 - }, - { - 'x': 1413571800000, - 'y': 172 - }, - { - 'x': 1413572400000, - 'y': 73 - }, - { - 'x': 1413573600000, - 'y': 90 - }, - { - 'x': 1413574200000, - 'y': 130 - }, - { - 'x': 1413574800000, - 'y': 104 - }, - { - 'x': 1413575400000, - 'y': 108 - }, - { - 'x': 1413576000000, - 'y': 92 - }, - { - 'x': 1413576600000, - 'y': 79 - }, - { - 'x': 1413577200000, - 'y': 90 - }, - { - 'x': 1413577800000, - 'y': 72 - }, - { - 'x': 1413578400000, - 'y': 68 - }, - { - 'x': 1413579000000, - 'y': 52 - }, - { - 'x': 1413579600000, - 'y': 60 - }, - { - 'x': 1413580200000, - 'y': 51 - }, - { - 'x': 1413580800000, - 'y': 32 - }, - { - 'x': 1413581400000, - 'y': 37 - }, - { - 'x': 1413582000000, - 'y': 30 - }, - { - 'x': 1413582600000, - 'y': 29 - }, - { - 'x': 1413583200000, - 'y': 24 - }, - { - 'x': 1413583800000, - 'y': 16 - }, - { - 'x': 1413584400000, - 'y': 15 - }, - { - 'x': 1413585000000, - 'y': 15 - }, - { - 'x': 1413585600000, - 'y': 10 - }, - { - 'x': 1413586200000, - 'y': 9 - }, - { - 'x': 1413586800000, - 'y': 9 - } - ] - }, - { - 'label': 'png', - 'values': [ - { - 'x': 1413543600000, - 'y': 44 - }, - { - 'x': 1413544200000, - 'y': 495 - }, - { - 'x': 1413544800000, - 'y': 489 - }, - { - 'x': 1413545400000, - 'y': 492 - }, - { - 'x': 1413546000000, - 'y': 556 - }, - { - 'x': 1413546600000, - 'y': 536 - }, - { - 'x': 1413547200000, - 'y': 511 - }, - { - 'x': 1413547800000, - 'y': 479 - }, - { - 'x': 1413548400000, - 'y': 544 - }, - { - 'x': 1413549000000, - 'y': 513 - }, - { - 'x': 1413549600000, - 'y': 501 - }, - { - 'x': 1413550200000, - 'y': 532 - }, - { - 'x': 1413550800000, - 'y': 440 - }, - { - 'x': 1413551400000, - 'y': 455 - }, - { - 'x': 1413552000000, - 'y': 455 - }, - { - 'x': 1413552600000, - 'y': 471 - }, - { - 'x': 1413553200000, - 'y': 428 - }, - { - 'x': 1413553800000, - 'y': 457 - }, - { - 'x': 1413554400000, - 'y': 450 - }, - { - 'x': 1413555000000, - 'y': 418 - }, - { - 'x': 1413555600000, - 'y': 398 - }, - { - 'x': 1413556200000, - 'y': 397 - }, - { - 'x': 1413556800000, - 'y': 359 - }, - { - 'x': 1413557400000, - 'y': 398 - }, - { - 'x': 1413558000000, - 'y': 339 - }, - { - 'x': 1413558600000, - 'y': 363 - }, - { - 'x': 1413559200000, - 'y': 297 - }, - { - 'x': 1413559800000, - 'y': 323 - }, - { - 'x': 1413560400000, - 'y': 302 - }, - { - 'x': 1413561000000, - 'y': 260 - }, - { - 'x': 1413561600000, - 'y': 276 - }, - { - 'x': 1413562200000, - 'y': 249 - }, - { - 'x': 1413562800000, - 'y': 248 - }, - { - 'x': 1413563400000, - 'y': 235 - }, - { - 'x': 1413564000000, - 'y': 234 - }, - { - 'x': 1413564600000, - 'y': 188 - }, - { - 'x': 1413565200000, - 'y': 192 - }, - { - 'x': 1413565800000, - 'y': 173 - }, - { - 'x': 1413566400000, - 'y': 160 - }, - { - 'x': 1413567000000, - 'y': 137 - }, - { - 'x': 1413567600000, - 'y': 158 - }, - { - 'x': 1413568200000, - 'y': 111 - }, - { - 'x': 1413568800000, - 'y': 145 - }, - { - 'x': 1413569400000, - 'y': 118 - }, - { - 'x': 1413570000000, - 'y': 104 - }, - { - 'x': 1413570600000, - 'y': 80 - }, - { - 'x': 1413571200000, - 'y': 79 - }, - { - 'x': 1413571800000, - 'y': 86 - }, - { - 'x': 1413572400000, - 'y': 47 - }, - { - 'x': 1413573600000, - 'y': 49 - }, - { - 'x': 1413574200000, - 'y': 68 - }, - { - 'x': 1413574800000, - 'y': 78 - }, - { - 'x': 1413575400000, - 'y': 77 - }, - { - 'x': 1413576000000, - 'y': 50 - }, - { - 'x': 1413576600000, - 'y': 51 - }, - { - 'x': 1413577200000, - 'y': 40 - }, - { - 'x': 1413577800000, - 'y': 42 - }, - { - 'x': 1413578400000, - 'y': 29 - }, - { - 'x': 1413579000000, - 'y': 24 - }, - { - 'x': 1413579600000, - 'y': 30 - }, - { - 'x': 1413580200000, - 'y': 18 - }, - { - 'x': 1413580800000, - 'y': 15 - }, - { - 'x': 1413581400000, - 'y': 19 - }, - { - 'x': 1413582000000, - 'y': 18 - }, - { - 'x': 1413582600000, - 'y': 13 - }, - { - 'x': 1413583200000, - 'y': 11 - }, - { - 'x': 1413583800000, - 'y': 11 - }, - { - 'x': 1413584400000, - 'y': 13 - }, - { - 'x': 1413585000000, - 'y': 9 - }, - { - 'x': 1413585600000, - 'y': 9 - }, - { - 'x': 1413586200000, - 'y': 9 - }, - { - 'x': 1413586800000, - 'y': 3 - } - ] - }, - { - 'label': 'css', - 'values': [ - { - 'x': 1413543600000, - 'y': 35 - }, - { - 'x': 1413544200000, - 'y': 360 - }, - { - 'x': 1413544800000, - 'y': 343 - }, - { - 'x': 1413545400000, - 'y': 329 - }, - { - 'x': 1413546000000, - 'y': 345 - }, - { - 'x': 1413546600000, - 'y': 336 - }, - { - 'x': 1413547200000, - 'y': 330 - }, - { - 'x': 1413547800000, - 'y': 334 - }, - { - 'x': 1413548400000, - 'y': 326 - }, - { - 'x': 1413549000000, - 'y': 351 - }, - { - 'x': 1413549600000, - 'y': 334 - }, - { - 'x': 1413550200000, - 'y': 351 - }, - { - 'x': 1413550800000, - 'y': 337 - }, - { - 'x': 1413551400000, - 'y': 306 - }, - { - 'x': 1413552000000, - 'y': 346 - }, - { - 'x': 1413552600000, - 'y': 317 - }, - { - 'x': 1413553200000, - 'y': 298 - }, - { - 'x': 1413553800000, - 'y': 288 - }, - { - 'x': 1413554400000, - 'y': 283 - }, - { - 'x': 1413555000000, - 'y': 262 - }, - { - 'x': 1413555600000, - 'y': 245 - }, - { - 'x': 1413556200000, - 'y': 259 - }, - { - 'x': 1413556800000, - 'y': 267 - }, - { - 'x': 1413557400000, - 'y': 230 - }, - { - 'x': 1413558000000, - 'y': 218 - }, - { - 'x': 1413558600000, - 'y': 241 - }, - { - 'x': 1413559200000, - 'y': 213 - }, - { - 'x': 1413559800000, - 'y': 239 - }, - { - 'x': 1413560400000, - 'y': 208 - }, - { - 'x': 1413561000000, - 'y': 187 - }, - { - 'x': 1413561600000, - 'y': 166 - }, - { - 'x': 1413562200000, - 'y': 154 - }, - { - 'x': 1413562800000, - 'y': 184 - }, - { - 'x': 1413563400000, - 'y': 148 - }, - { - 'x': 1413564000000, - 'y': 153 - }, - { - 'x': 1413564600000, - 'y': 149 - }, - { - 'x': 1413565200000, - 'y': 102 - }, - { - 'x': 1413565800000, - 'y': 110 - }, - { - 'x': 1413566400000, - 'y': 121 - }, - { - 'x': 1413567000000, - 'y': 120 - }, - { - 'x': 1413567600000, - 'y': 86 - }, - { - 'x': 1413568200000, - 'y': 96 - }, - { - 'x': 1413568800000, - 'y': 71 - }, - { - 'x': 1413569400000, - 'y': 92 - }, - { - 'x': 1413570000000, - 'y': 65 - }, - { - 'x': 1413570600000, - 'y': 54 - }, - { - 'x': 1413571200000, - 'y': 68 - }, - { - 'x': 1413571800000, - 'y': 57 - }, - { - 'x': 1413572400000, - 'y': 33 - }, - { - 'x': 1413573600000, - 'y': 47 - }, - { - 'x': 1413574200000, - 'y': 42 - }, - { - 'x': 1413574800000, - 'y': 39 - }, - { - 'x': 1413575400000, - 'y': 25 - }, - { - 'x': 1413576000000, - 'y': 31 - }, - { - 'x': 1413576600000, - 'y': 37 - }, - { - 'x': 1413577200000, - 'y': 35 - }, - { - 'x': 1413577800000, - 'y': 19 - }, - { - 'x': 1413578400000, - 'y': 15 - }, - { - 'x': 1413579000000, - 'y': 21 - }, - { - 'x': 1413579600000, - 'y': 16 - }, - { - 'x': 1413580200000, - 'y': 18 - }, - { - 'x': 1413580800000, - 'y': 10 - }, - { - 'x': 1413581400000, - 'y': 13 - }, - { - 'x': 1413582000000, - 'y': 14 - }, - { - 'x': 1413582600000, - 'y': 11 - }, - { - 'x': 1413583200000, - 'y': 4 - }, - { - 'x': 1413583800000, - 'y': 6 - }, - { - 'x': 1413584400000, - 'y': 3 - }, - { - 'x': 1413585000000, - 'y': 6 - }, - { - 'x': 1413585600000, - 'y': 6 - }, - { - 'x': 1413586200000, - 'y': 2 - }, - { - 'x': 1413586800000, - 'y': 3 - } - ] - }, - { - 'label': 'gif', - 'values': [ - { - 'x': 1413543600000, - 'y': 21 - }, - { - 'x': 1413544200000, - 'y': 191 - }, - { - 'x': 1413544800000, - 'y': 176 - }, - { - 'x': 1413545400000, - 'y': 166 - }, - { - 'x': 1413546000000, - 'y': 183 - }, - { - 'x': 1413546600000, - 'y': 170 - }, - { - 'x': 1413547200000, - 'y': 153 - }, - { - 'x': 1413547800000, - 'y': 202 - }, - { - 'x': 1413548400000, - 'y': 175 - }, - { - 'x': 1413549000000, - 'y': 161 - }, - { - 'x': 1413549600000, - 'y': 174 - }, - { - 'x': 1413550200000, - 'y': 167 - }, - { - 'x': 1413550800000, - 'y': 171 - }, - { - 'x': 1413551400000, - 'y': 176 - }, - { - 'x': 1413552000000, - 'y': 139 - }, - { - 'x': 1413552600000, - 'y': 145 - }, - { - 'x': 1413553200000, - 'y': 157 - }, - { - 'x': 1413553800000, - 'y': 148 - }, - { - 'x': 1413554400000, - 'y': 149 - }, - { - 'x': 1413555000000, - 'y': 135 - }, - { - 'x': 1413555600000, - 'y': 118 - }, - { - 'x': 1413556200000, - 'y': 142 - }, - { - 'x': 1413556800000, - 'y': 141 - }, - { - 'x': 1413557400000, - 'y': 146 - }, - { - 'x': 1413558000000, - 'y': 114 - }, - { - 'x': 1413558600000, - 'y': 115 - }, - { - 'x': 1413559200000, - 'y': 136 - }, - { - 'x': 1413559800000, - 'y': 106 - }, - { - 'x': 1413560400000, - 'y': 92 - }, - { - 'x': 1413561000000, - 'y': 97 - }, - { - 'x': 1413561600000, - 'y': 90 - }, - { - 'x': 1413562200000, - 'y': 69 - }, - { - 'x': 1413562800000, - 'y': 66 - }, - { - 'x': 1413563400000, - 'y': 93 - }, - { - 'x': 1413564000000, - 'y': 75 - }, - { - 'x': 1413564600000, - 'y': 68 - }, - { - 'x': 1413565200000, - 'y': 55 - }, - { - 'x': 1413565800000, - 'y': 73 - }, - { - 'x': 1413566400000, - 'y': 57 - }, - { - 'x': 1413567000000, - 'y': 48 - }, - { - 'x': 1413567600000, - 'y': 41 - }, - { - 'x': 1413568200000, - 'y': 39 - }, - { - 'x': 1413568800000, - 'y': 32 - }, - { - 'x': 1413569400000, - 'y': 33 - }, - { - 'x': 1413570000000, - 'y': 39 - }, - { - 'x': 1413570600000, - 'y': 35 - }, - { - 'x': 1413571200000, - 'y': 25 - }, - { - 'x': 1413571800000, - 'y': 28 - }, - { - 'x': 1413572400000, - 'y': 8 - }, - { - 'x': 1413573600000, - 'y': 13 - }, - { - 'x': 1413574200000, - 'y': 23 - }, - { - 'x': 1413574800000, - 'y': 19 - }, - { - 'x': 1413575400000, - 'y': 16 - }, - { - 'x': 1413576000000, - 'y': 22 - }, - { - 'x': 1413576600000, - 'y': 13 - }, - { - 'x': 1413577200000, - 'y': 21 - }, - { - 'x': 1413577800000, - 'y': 11 - }, - { - 'x': 1413578400000, - 'y': 12 - }, - { - 'x': 1413579000000, - 'y': 10 - }, - { - 'x': 1413579600000, - 'y': 7 - }, - { - 'x': 1413580200000, - 'y': 4 - }, - { - 'x': 1413580800000, - 'y': 5 - }, - { - 'x': 1413581400000, - 'y': 7 - }, - { - 'x': 1413582000000, - 'y': 9 - }, - { - 'x': 1413582600000, - 'y': 2 - }, - { - 'x': 1413583200000, - 'y': 2 - }, - { - 'x': 1413583800000, - 'y': 4 - }, - { - 'x': 1413584400000, - 'y': 6 - }, - { - 'x': 1413585600000, - 'y': 2 - }, - { - 'x': 1413586200000, - 'y': 4 - }, - { - 'x': 1413586800000, - 'y': 4 - } - ] - } - ], - 'hits': 108970, - 'xAxisOrderedValues': [ - 1413543600000, - 1413544200000, - 1413544800000, - 1413545400000, - 1413546000000, - 1413546600000, - 1413547200000, - 1413547800000, - 1413548400000, - 1413549000000, - 1413549600000, - 1413550200000, - 1413550800000, - 1413551400000, - 1413552000000, - 1413552600000, - 1413553200000, - 1413553800000, - 1413554400000, - 1413555000000, - 1413555600000, - 1413556200000, - 1413556800000, - 1413557400000, - 1413558000000, - 1413558600000, - 1413559200000, - 1413559800000, - 1413560400000, - 1413561000000, - 1413561600000, - 1413562200000, - 1413562800000, - 1413563400000, - 1413564000000, - 1413564600000, - 1413565200000, - 1413565800000, - 1413566400000, - 1413567000000, - 1413567600000, - 1413568200000, - 1413568800000, - 1413569400000, - 1413570000000, - 1413570600000, - 1413571200000, - 1413571800000, - 1413572400000, - 1413573600000, - 1413574200000, - 1413574800000, - 1413575400000, - 1413576000000, - 1413576600000, - 1413577200000, - 1413577800000, - 1413578400000, - 1413579000000, - 1413579600000, - 1413580200000, - 1413580800000, - 1413581400000, - 1413582000000, - 1413582600000, - 1413583200000, - 1413583800000, - 1413584400000, - 1413585000000, - 1413585600000, - 1413586200000, - 1413586800000, - ], - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_columns.js deleted file mode 100644 index 041fad2ed15b9c..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_columns.js +++ /dev/null @@ -1,112 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'label': 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1: agent.raw', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 10379 - }, - { - 'x': 'png', - 'y': 6395 - } - ] - } - ], - 'xAxisOrderedValues': ['css', 'png'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24: agent.raw', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 9253 - }, - { - 'x': 'png', - 'y': 5571 - } - ] - } - ], - 'xAxisOrderedValues': ['css', 'png'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322): agent.raw', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 7740 - }, - { - 'x': 'png', - 'y': 4697 - } - ] - } - ], - 'xAxisOrderedValues': ['css', 'png'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'hits': 171443 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_rows.js deleted file mode 100644 index cc4f598c7b1b7f..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_rows.js +++ /dev/null @@ -1,109 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'label': '200: response', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 25260 - }, - { - 'x': 'png', - 'y': 15311 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '404: response', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 1352 - }, - { - 'x': 'png', - 'y': 826 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '503: response', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 761 - }, - { - 'x': 'png', - 'y': 527 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'hits': 171443 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_series.js deleted file mode 100644 index 2a2d14d59b67ea..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_series.js +++ /dev/null @@ -1,42 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': 'filters', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'css', - 'y': 27374 - }, - { - 'x': 'html', - 'y': 0 - }, - { - 'x': 'png', - 'y': 16663 - } - ] - } - ], - 'hits': 171454, - 'xAxisOrderedValues': ['css', 'html', 'png'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_columns.js deleted file mode 100644 index d283d793151772..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_columns.js +++ /dev/null @@ -1,3745 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'title': 'Top 2 geo.dest: CN', - 'valueFormatter': _.identity, - 'geoJson': { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 22.5 - ] - }, - 'properties': { - 'value': 42, - 'geohash': 's', - 'center': [ - 22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 's', - 'value': 's', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 42, - 'value': 42, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 0 - ], - [ - 45, - 0 - ], - [ - 45, - 45 - ], - [ - 0, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 22.5 - ] - }, - 'properties': { - 'value': 31, - 'geohash': 'd', - 'center': [ - -67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'd', - 'value': 'd', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 31, - 'value': 31, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 0 - ], - [ - -45, - 0 - ], - [ - -45, - 45 - ], - [ - -90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 22.5 - ] - }, - 'properties': { - 'value': 30, - 'geohash': 'w', - 'center': [ - 112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'w', - 'value': 'w', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 30, - 'value': 30, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 0 - ], - [ - 135, - 0 - ], - [ - 135, - 45 - ], - [ - 90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 22.5 - ] - }, - 'properties': { - 'value': 25, - 'geohash': '9', - 'center': [ - -112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '9', - 'value': '9', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 25, - 'value': 25, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 0 - ], - [ - -90, - 0 - ], - [ - -90, - 45 - ], - [ - -135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 22.5 - ] - }, - 'properties': { - 'value': 22, - 'geohash': 't', - 'center': [ - 67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 't', - 'value': 't', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 22, - 'value': 22, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 0 - ], - [ - 90, - 0 - ], - [ - 90, - 45 - ], - [ - 45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -22.5 - ] - }, - 'properties': { - 'value': 22, - 'geohash': 'k', - 'center': [ - 22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'k', - 'value': 'k', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 22, - 'value': 22, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -45 - ], - [ - 45, - -45 - ], - [ - 45, - 0 - ], - [ - 0, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -22.5 - ] - }, - 'properties': { - 'value': 21, - 'geohash': '6', - 'center': [ - -67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '6', - 'value': '6', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 21, - 'value': 21, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -45 - ], - [ - -45, - -45 - ], - [ - -45, - 0 - ], - [ - -90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 67.5 - ] - }, - 'properties': { - 'value': 19, - 'geohash': 'u', - 'center': [ - 22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'u', - 'value': 'u', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 19, - 'value': 19, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 45 - ], - [ - 45, - 45 - ], - [ - 45, - 90 - ], - [ - 0, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 67.5 - ] - }, - 'properties': { - 'value': 18, - 'geohash': 'v', - 'center': [ - 67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'v', - 'value': 'v', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 18, - 'value': 18, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 45 - ], - [ - 90, - 45 - ], - [ - 90, - 90 - ], - [ - 45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 67.5 - ] - }, - 'properties': { - 'value': 11, - 'geohash': 'c', - 'center': [ - -112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'c', - 'value': 'c', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 11, - 'value': 11, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 45 - ], - [ - -90, - 45 - ], - [ - -90, - 90 - ], - [ - -135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -22.5 - ] - }, - 'properties': { - 'value': 10, - 'geohash': 'r', - 'center': [ - 157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'r', - 'value': 'r', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 10, - 'value': 10, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -45 - ], - [ - 180, - -45 - ], - [ - 180, - 0 - ], - [ - 135, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 67.5 - ] - }, - 'properties': { - 'value': 9, - 'geohash': 'y', - 'center': [ - 112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'y', - 'value': 'y', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 9, - 'value': 9, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 45 - ], - [ - 135, - 45 - ], - [ - 135, - 90 - ], - [ - 90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 22.5 - ] - }, - 'properties': { - 'value': 9, - 'geohash': 'e', - 'center': [ - -22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'e', - 'value': 'e', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 9, - 'value': 9, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 0 - ], - [ - 0, - 0 - ], - [ - 0, - 45 - ], - [ - -45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 67.5 - ] - }, - 'properties': { - 'value': 8, - 'geohash': 'f', - 'center': [ - -67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'f', - 'value': 'f', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 8, - 'value': 8, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 45 - ], - [ - -45, - 45 - ], - [ - -45, - 90 - ], - [ - -90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -22.5 - ] - }, - 'properties': { - 'value': 8, - 'geohash': '7', - 'center': [ - -22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '7', - 'value': '7', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 8, - 'value': 8, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -45 - ], - [ - 0, - -45 - ], - [ - 0, - 0 - ], - [ - -45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -22.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'q', - 'center': [ - 112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'q', - 'value': 'q', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - -45 - ], - [ - 135, - -45 - ], - [ - 135, - 0 - ], - [ - 90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'g', - 'center': [ - -22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'g', - 'value': 'g', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 45 - ], - [ - 0, - 45 - ], - [ - 0, - 90 - ], - [ - -45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 22.5 - ] - }, - 'properties': { - 'value': 4, - 'geohash': 'x', - 'center': [ - 157.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'x', - 'value': 'x', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 4, - 'value': 4, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 0 - ], - [ - 180, - 0 - ], - [ - 180, - 45 - ], - [ - 135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - 67.5 - ] - }, - 'properties': { - 'value': 3, - 'geohash': 'b', - 'center': [ - -157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'b', - 'value': 'b', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 3, - 'value': 3, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -180, - 45 - ], - [ - -135, - 45 - ], - [ - -135, - 90 - ], - [ - -180, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': 'z', - 'center': [ - 157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'z', - 'value': 'z', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 45 - ], - [ - 180, - 45 - ], - [ - 180, - 90 - ], - [ - 135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'm', - 'center': [ - 67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'm', - 'value': 'm', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -45 - ], - [ - 90, - -45 - ], - [ - 90, - 0 - ], - [ - 45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '5', - 'center': [ - -22.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '5', - 'value': '5', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -90 - ], - [ - 0, - -90 - ], - [ - 0, - -45 - ], - [ - -45, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '4', - 'center': [ - -67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '4', - 'value': '4', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -90 - ], - [ - -45, - -90 - ], - [ - -45, - -45 - ], - [ - -90, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '3', - 'center': [ - -112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '3', - 'value': '3', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - -45 - ], - [ - -90, - -45 - ], - [ - -90, - 0 - ], - [ - -135, - 0 - ] - ] - } - } - ], - 'properties': { - 'min': 1, - 'max': 42 - } - } - }, - { - 'label': 'Top 2 geo.dest: IN', - 'valueFormatter': _.identity, - 'geoJson': { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 22.5 - ] - }, - 'properties': { - 'value': 32, - 'geohash': 's', - 'center': [ - 22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 's', - 'value': 's', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 32, - 'value': 32, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 0 - ], - [ - 45, - 0 - ], - [ - 45, - 45 - ], - [ - 0, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -22.5 - ] - }, - 'properties': { - 'value': 31, - 'geohash': '6', - 'center': [ - -67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '6', - 'value': '6', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 31, - 'value': 31, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -45 - ], - [ - -45, - -45 - ], - [ - -45, - 0 - ], - [ - -90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 22.5 - ] - }, - 'properties': { - 'value': 28, - 'geohash': 'd', - 'center': [ - -67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'd', - 'value': 'd', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 28, - 'value': 28, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 0 - ], - [ - -45, - 0 - ], - [ - -45, - 45 - ], - [ - -90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 22.5 - ] - }, - 'properties': { - 'value': 27, - 'geohash': 'w', - 'center': [ - 112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'w', - 'value': 'w', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 27, - 'value': 27, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 0 - ], - [ - 135, - 0 - ], - [ - 135, - 45 - ], - [ - 90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 22.5 - ] - }, - 'properties': { - 'value': 24, - 'geohash': 't', - 'center': [ - 67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 't', - 'value': 't', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 24, - 'value': 24, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 0 - ], - [ - 90, - 0 - ], - [ - 90, - 45 - ], - [ - 45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -22.5 - ] - }, - 'properties': { - 'value': 23, - 'geohash': 'k', - 'center': [ - 22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'k', - 'value': 'k', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 23, - 'value': 23, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -45 - ], - [ - 45, - -45 - ], - [ - 45, - 0 - ], - [ - 0, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 67.5 - ] - }, - 'properties': { - 'value': 17, - 'geohash': 'u', - 'center': [ - 22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'u', - 'value': 'u', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 17, - 'value': 17, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 45 - ], - [ - 45, - 45 - ], - [ - 45, - 90 - ], - [ - 0, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 22.5 - ] - }, - 'properties': { - 'value': 16, - 'geohash': '9', - 'center': [ - -112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '9', - 'value': '9', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 16, - 'value': 16, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 0 - ], - [ - -90, - 0 - ], - [ - -90, - 45 - ], - [ - -135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 67.5 - ] - }, - 'properties': { - 'value': 14, - 'geohash': 'v', - 'center': [ - 67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'v', - 'value': 'v', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 14, - 'value': 14, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 45 - ], - [ - 90, - 45 - ], - [ - 90, - 90 - ], - [ - 45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 22.5 - ] - }, - 'properties': { - 'value': 13, - 'geohash': 'e', - 'center': [ - -22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'e', - 'value': 'e', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 13, - 'value': 13, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 0 - ], - [ - 0, - 0 - ], - [ - 0, - 45 - ], - [ - -45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -22.5 - ] - }, - 'properties': { - 'value': 9, - 'geohash': 'r', - 'center': [ - 157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'r', - 'value': 'r', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 9, - 'value': 9, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -45 - ], - [ - 180, - -45 - ], - [ - 180, - 0 - ], - [ - 135, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'y', - 'center': [ - 112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'y', - 'value': 'y', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 45 - ], - [ - 135, - 45 - ], - [ - 135, - 90 - ], - [ - 90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'g', - 'center': [ - -22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'g', - 'value': 'g', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 45 - ], - [ - 0, - 45 - ], - [ - 0, - 90 - ], - [ - -45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'f', - 'center': [ - -67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'f', - 'value': 'f', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 45 - ], - [ - -45, - 45 - ], - [ - -45, - 90 - ], - [ - -90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 67.5 - ] - }, - 'properties': { - 'value': 5, - 'geohash': 'c', - 'center': [ - -112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'c', - 'value': 'c', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 5, - 'value': 5, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 45 - ], - [ - -90, - 45 - ], - [ - -90, - 90 - ], - [ - -135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - 67.5 - ] - }, - 'properties': { - 'value': 4, - 'geohash': 'b', - 'center': [ - -157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'b', - 'value': 'b', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 4, - 'value': 4, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -180, - 45 - ], - [ - -135, - 45 - ], - [ - -135, - 90 - ], - [ - -180, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -22.5 - ] - }, - 'properties': { - 'value': 3, - 'geohash': 'q', - 'center': [ - 112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'q', - 'value': 'q', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 3, - 'value': 3, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - -45 - ], - [ - 135, - -45 - ], - [ - 135, - 0 - ], - [ - 90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': '4', - 'center': [ - -67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '4', - 'value': '4', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -90 - ], - [ - -45, - -90 - ], - [ - -45, - -45 - ], - [ - -90, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'z', - 'center': [ - 157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'z', - 'value': 'z', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 45 - ], - [ - 180, - 45 - ], - [ - 180, - 90 - ], - [ - 135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'x', - 'center': [ - 157.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'x', - 'value': 'x', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 0 - ], - [ - 180, - 0 - ], - [ - 180, - 45 - ], - [ - 135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'p', - 'center': [ - 157.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'p', - 'value': 'p', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -90 - ], - [ - 180, - -90 - ], - [ - 180, - -45 - ], - [ - 135, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'm', - 'center': [ - 67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': 'm', - 'value': 'm', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -45 - ], - [ - 90, - -45 - ], - [ - 90, - 0 - ], - [ - 45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '7', - 'center': [ - -22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': false - } - }, - 'type': 'bucket' - }, - 'key': '7', - 'value': '7', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -45 - ], - [ - 0, - -45 - ], - [ - 0, - 0 - ], - [ - -45, - 0 - ] - ] - } - } - ], - 'properties': { - 'min': 1, - 'max': 32 - } - } - } - ] -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_geo_json.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_geo_json.js deleted file mode 100644 index 4e65502f8d2788..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_geo_json.js +++ /dev/null @@ -1,1847 +0,0 @@ -import _ from 'lodash'; - -export default { - 'valueFormatter': _.identity, - 'geohashGridAgg': { 'vis': { 'params': {} } }, - 'geoJson': { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 22.5 - ] - }, - 'properties': { - 'value': 608, - 'geohash': 's', - 'center': [ - 22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 's', - 'value': 's', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 608, - 'value': 608, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 0 - ], - [ - 0, - 45 - ], - [ - 45, - 45 - ], - [ - 45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 22.5 - ] - }, - 'properties': { - 'value': 522, - 'geohash': 'w', - 'center': [ - 112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'w', - 'value': 'w', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 522, - 'value': 522, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 90 - ], - [ - 0, - 135 - ], - [ - 45, - 135 - ], - [ - 45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -22.5 - ] - }, - 'properties': { - 'value': 517, - 'geohash': '6', - 'center': [ - -67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '6', - 'value': '6', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 517, - 'value': 517, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -90 - ], - [ - -45, - -45 - ], - [ - 0, - -45 - ], - [ - 0, - -90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 22.5 - ] - }, - 'properties': { - 'value': 446, - 'geohash': 'd', - 'center': [ - -67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'd', - 'value': 'd', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 446, - 'value': 446, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -90 - ], - [ - 0, - -45 - ], - [ - 45, - -45 - ], - [ - 45, - -90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 67.5 - ] - }, - 'properties': { - 'value': 426, - 'geohash': 'u', - 'center': [ - 22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'u', - 'value': 'u', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 426, - 'value': 426, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 0 - ], - [ - 45, - 45 - ], - [ - 90, - 45 - ], - [ - 90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 22.5 - ] - }, - 'properties': { - 'value': 413, - 'geohash': 't', - 'center': [ - 67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 't', - 'value': 't', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 413, - 'value': 413, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 45 - ], - [ - 0, - 90 - ], - [ - 45, - 90 - ], - [ - 45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -22.5 - ] - }, - 'properties': { - 'value': 362, - 'geohash': 'k', - 'center': [ - 22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'k', - 'value': 'k', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 362, - 'value': 362, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 0 - ], - [ - -45, - 45 - ], - [ - 0, - 45 - ], - [ - 0, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 22.5 - ] - }, - 'properties': { - 'value': 352, - 'geohash': '9', - 'center': [ - -112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '9', - 'value': '9', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 352, - 'value': 352, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -135 - ], - [ - 0, - -90 - ], - [ - 45, - -90 - ], - [ - 45, - -135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 22.5 - ] - }, - 'properties': { - 'value': 216, - 'geohash': 'e', - 'center': [ - -22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'e', - 'value': 'e', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 216, - 'value': 216, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -45 - ], - [ - 0, - 0 - ], - [ - 45, - 0 - ], - [ - 45, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 67.5 - ] - }, - 'properties': { - 'value': 183, - 'geohash': 'v', - 'center': [ - 67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'v', - 'value': 'v', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 183, - 'value': 183, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 45 - ], - [ - 45, - 90 - ], - [ - 90, - 90 - ], - [ - 90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -22.5 - ] - }, - 'properties': { - 'value': 158, - 'geohash': 'r', - 'center': [ - 157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'r', - 'value': 'r', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 158, - 'value': 158, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 135 - ], - [ - -45, - 180 - ], - [ - 0, - 180 - ], - [ - 0, - 135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 67.5 - ] - }, - 'properties': { - 'value': 139, - 'geohash': 'y', - 'center': [ - 112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'y', - 'value': 'y', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 139, - 'value': 139, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 90 - ], - [ - 45, - 135 - ], - [ - 90, - 135 - ], - [ - 90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 67.5 - ] - }, - 'properties': { - 'value': 110, - 'geohash': 'c', - 'center': [ - -112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'c', - 'value': 'c', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 110, - 'value': 110, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -135 - ], - [ - 45, - -90 - ], - [ - 90, - -90 - ], - [ - 90, - -135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -22.5 - ] - }, - 'properties': { - 'value': 101, - 'geohash': 'q', - 'center': [ - 112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'q', - 'value': 'q', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 101, - 'value': 101, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 90 - ], - [ - -45, - 135 - ], - [ - 0, - 135 - ], - [ - 0, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -22.5 - ] - }, - 'properties': { - 'value': 101, - 'geohash': '7', - 'center': [ - -22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '7', - 'value': '7', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 101, - 'value': 101, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -45 - ], - [ - -45, - 0 - ], - [ - 0, - 0 - ], - [ - 0, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 67.5 - ] - }, - 'properties': { - 'value': 92, - 'geohash': 'f', - 'center': [ - -67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'f', - 'value': 'f', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 92, - 'value': 92, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -90 - ], - [ - 45, - -45 - ], - [ - 90, - -45 - ], - [ - 90, - -90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - 67.5 - ] - }, - 'properties': { - 'value': 75, - 'geohash': 'b', - 'center': [ - -157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'b', - 'value': 'b', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 75, - 'value': 75, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -180 - ], - [ - 45, - -135 - ], - [ - 90, - -135 - ], - [ - 90, - -180 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 67.5 - ] - }, - 'properties': { - 'value': 64, - 'geohash': 'g', - 'center': [ - -22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'g', - 'value': 'g', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 64, - 'value': 64, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -45 - ], - [ - 45, - 0 - ], - [ - 90, - 0 - ], - [ - 90, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 67.5 - ] - }, - 'properties': { - 'value': 36, - 'geohash': 'z', - 'center': [ - 157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'z', - 'value': 'z', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 36, - 'value': 36, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 135 - ], - [ - 45, - 180 - ], - [ - 90, - 180 - ], - [ - 90, - 135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 22.5 - ] - }, - 'properties': { - 'value': 34, - 'geohash': 'x', - 'center': [ - 157.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'x', - 'value': 'x', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 34, - 'value': 34, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 135 - ], - [ - 0, - 180 - ], - [ - 45, - 180 - ], - [ - 45, - 135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -67.5 - ] - }, - 'properties': { - 'value': 30, - 'geohash': '4', - 'center': [ - -67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '4', - 'value': '4', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 30, - 'value': 30, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -90 - ], - [ - -90, - -45 - ], - [ - -45, - -45 - ], - [ - -45, - -90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - -22.5 - ] - }, - 'properties': { - 'value': 16, - 'geohash': 'm', - 'center': [ - 67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'm', - 'value': 'm', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 16, - 'value': 16, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 45 - ], - [ - -45, - 90 - ], - [ - 0, - 90 - ], - [ - 0, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -67.5 - ] - }, - 'properties': { - 'value': 10, - 'geohash': '5', - 'center': [ - -22.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '5', - 'value': '5', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 10, - 'value': 10, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -45 - ], - [ - -90, - 0 - ], - [ - -45, - 0 - ], - [ - -45, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'p', - 'center': [ - 157.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'p', - 'value': 'p', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 135 - ], - [ - -90, - 180 - ], - [ - -45, - 180 - ], - [ - -45, - 135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - -22.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': '2', - 'center': [ - -157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '2', - 'value': '2', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -180 - ], - [ - -45, - -135 - ], - [ - 0, - -135 - ], - [ - 0, - -180 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -67.5 - ] - }, - 'properties': { - 'value': 4, - 'geohash': 'h', - 'center': [ - 22.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'h', - 'value': 'h', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 4, - 'value': 4, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 0 - ], - [ - -90, - 45 - ], - [ - -45, - 45 - ], - [ - -45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': 'n', - 'center': [ - 112.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'n', - 'value': 'n', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 90 - ], - [ - -90, - 135 - ], - [ - -45, - 135 - ], - [ - -45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - -67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': 'j', - 'center': [ - 67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': 'j', - 'value': 'j', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 45 - ], - [ - -90, - 90 - ], - [ - -45, - 90 - ], - [ - -45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '3', - 'center': [ - -112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '3', - 'value': '3', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -135 - ], - [ - -45, - -90 - ], - [ - 0, - -90 - ], - [ - 0, - -135 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '1', - 'center': [ - -112.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - 'key': '1', - 'value': '1', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -135 - ], - [ - -90, - -90 - ], - [ - -45, - -90 - ], - [ - -45, - -135 - ] - ] - } - } - ], - 'properties': { - 'min': 1, - 'max': 608, - 'zoom': 2, - 'center': [5, 15] - } - }, -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_rows.js deleted file mode 100644 index 64deea0e391a6e..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_rows.js +++ /dev/null @@ -1,3667 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'title': 'Top 2 geo.dest: CN', - 'valueFormatter': _.identity, - 'geoJson': { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 22.5 - ] - }, - 'properties': { - 'value': 39, - 'geohash': 's', - 'center': [ - 22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 's', - 'value': 's', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 39, - 'value': 39, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 0 - ], - [ - 45, - 0 - ], - [ - 45, - 45 - ], - [ - 0, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 22.5 - ] - }, - 'properties': { - 'value': 31, - 'geohash': 'w', - 'center': [ - 112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'w', - 'value': 'w', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 31, - 'value': 31, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 0 - ], - [ - 135, - 0 - ], - [ - 135, - 45 - ], - [ - 90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 22.5 - ] - }, - 'properties': { - 'value': 30, - 'geohash': 'd', - 'center': [ - -67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'd', - 'value': 'd', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 30, - 'value': 30, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 0 - ], - [ - -45, - 0 - ], - [ - -45, - 45 - ], - [ - -90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 22.5 - ] - }, - 'properties': { - 'value': 25, - 'geohash': '9', - 'center': [ - -112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '9', - 'value': '9', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 25, - 'value': 25, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 0 - ], - [ - -90, - 0 - ], - [ - -90, - 45 - ], - [ - -135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 22.5 - ] - }, - 'properties': { - 'value': 23, - 'geohash': 't', - 'center': [ - 67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 't', - 'value': 't', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 23, - 'value': 23, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 0 - ], - [ - 90, - 0 - ], - [ - 90, - 45 - ], - [ - 45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -22.5 - ] - }, - 'properties': { - 'value': 23, - 'geohash': 'k', - 'center': [ - 22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'k', - 'value': 'k', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 23, - 'value': 23, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -45 - ], - [ - 45, - -45 - ], - [ - 45, - 0 - ], - [ - 0, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -22.5 - ] - }, - 'properties': { - 'value': 22, - 'geohash': '6', - 'center': [ - -67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '6', - 'value': '6', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 22, - 'value': 22, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -45 - ], - [ - -45, - -45 - ], - [ - -45, - 0 - ], - [ - -90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 67.5 - ] - }, - 'properties': { - 'value': 20, - 'geohash': 'u', - 'center': [ - 22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'u', - 'value': 'u', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 20, - 'value': 20, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 45 - ], - [ - 45, - 45 - ], - [ - 45, - 90 - ], - [ - 0, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 67.5 - ] - }, - 'properties': { - 'value': 18, - 'geohash': 'v', - 'center': [ - 67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'v', - 'value': 'v', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 18, - 'value': 18, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 45 - ], - [ - 90, - 45 - ], - [ - 90, - 90 - ], - [ - 45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -22.5 - ] - }, - 'properties': { - 'value': 11, - 'geohash': 'r', - 'center': [ - 157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'r', - 'value': 'r', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 11, - 'value': 11, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -45 - ], - [ - 180, - -45 - ], - [ - 180, - 0 - ], - [ - 135, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 22.5 - ] - }, - 'properties': { - 'value': 11, - 'geohash': 'e', - 'center': [ - -22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'e', - 'value': 'e', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 11, - 'value': 11, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 0 - ], - [ - 0, - 0 - ], - [ - 0, - 45 - ], - [ - -45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 67.5 - ] - }, - 'properties': { - 'value': 10, - 'geohash': 'y', - 'center': [ - 112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'y', - 'value': 'y', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 10, - 'value': 10, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 45 - ], - [ - 135, - 45 - ], - [ - 135, - 90 - ], - [ - 90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 67.5 - ] - }, - 'properties': { - 'value': 10, - 'geohash': 'c', - 'center': [ - -112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'c', - 'value': 'c', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 10, - 'value': 10, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 45 - ], - [ - -90, - 45 - ], - [ - -90, - 90 - ], - [ - -135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 67.5 - ] - }, - 'properties': { - 'value': 8, - 'geohash': 'f', - 'center': [ - -67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'f', - 'value': 'f', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 8, - 'value': 8, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 45 - ], - [ - -45, - 45 - ], - [ - -45, - 90 - ], - [ - -90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -22.5 - ] - }, - 'properties': { - 'value': 8, - 'geohash': '7', - 'center': [ - -22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '7', - 'value': '7', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 8, - 'value': 8, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -45 - ], - [ - 0, - -45 - ], - [ - 0, - 0 - ], - [ - -45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -22.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'q', - 'center': [ - 112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'q', - 'value': 'q', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - -45 - ], - [ - 135, - -45 - ], - [ - 135, - 0 - ], - [ - 90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'g', - 'center': [ - -22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'g', - 'value': 'g', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 45 - ], - [ - 0, - 45 - ], - [ - 0, - 90 - ], - [ - -45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 22.5 - ] - }, - 'properties': { - 'value': 4, - 'geohash': 'x', - 'center': [ - 157.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'x', - 'value': 'x', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 4, - 'value': 4, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 0 - ], - [ - 180, - 0 - ], - [ - 180, - 45 - ], - [ - 135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - 67.5 - ] - }, - 'properties': { - 'value': 3, - 'geohash': 'b', - 'center': [ - -157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'b', - 'value': 'b', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 3, - 'value': 3, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -180, - 45 - ], - [ - -135, - 45 - ], - [ - -135, - 90 - ], - [ - -180, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': 'z', - 'center': [ - 157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'z', - 'value': 'z', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 45 - ], - [ - 180, - 45 - ], - [ - 180, - 90 - ], - [ - 135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': '4', - 'center': [ - -67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '4', - 'value': '4', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -90 - ], - [ - -45, - -90 - ], - [ - -45, - -45 - ], - [ - -90, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '5', - 'center': [ - -22.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '5', - 'value': '5', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -90 - ], - [ - 0, - -90 - ], - [ - 0, - -45 - ], - [ - -45, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '3', - 'center': [ - -112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'CN', - 'value': 'CN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '3', - 'value': '3', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - -45 - ], - [ - -90, - -45 - ], - [ - -90, - 0 - ], - [ - -135, - 0 - ] - ] - } - } - ], - 'properties': { - 'min': 1, - 'max': 39 - } - } - }, - { - 'label': 'Top 2 geo.dest: IN', - 'valueFormatter': _.identity, - 'geoJson': { - 'type': 'FeatureCollection', - 'features': [ - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -22.5 - ] - }, - 'properties': { - 'value': 31, - 'geohash': '6', - 'center': [ - -67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '6', - 'value': '6', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 31, - 'value': 31, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -45 - ], - [ - -45, - -45 - ], - [ - -45, - 0 - ], - [ - -90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 22.5 - ] - }, - 'properties': { - 'value': 30, - 'geohash': 's', - 'center': [ - 22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 's', - 'value': 's', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 30, - 'value': 30, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 0 - ], - [ - 45, - 0 - ], - [ - 45, - 45 - ], - [ - 0, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 22.5 - ] - }, - 'properties': { - 'value': 29, - 'geohash': 'w', - 'center': [ - 112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'w', - 'value': 'w', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 29, - 'value': 29, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 0 - ], - [ - 135, - 0 - ], - [ - 135, - 45 - ], - [ - 90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 22.5 - ] - }, - 'properties': { - 'value': 28, - 'geohash': 'd', - 'center': [ - -67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'd', - 'value': 'd', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 28, - 'value': 28, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 0 - ], - [ - -45, - 0 - ], - [ - -45, - 45 - ], - [ - -90, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 22.5 - ] - }, - 'properties': { - 'value': 25, - 'geohash': 't', - 'center': [ - 67.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 't', - 'value': 't', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 25, - 'value': 25, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 0 - ], - [ - 90, - 0 - ], - [ - 90, - 45 - ], - [ - 45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - -22.5 - ] - }, - 'properties': { - 'value': 24, - 'geohash': 'k', - 'center': [ - 22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'k', - 'value': 'k', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 24, - 'value': 24, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - -45 - ], - [ - 45, - -45 - ], - [ - 45, - 0 - ], - [ - 0, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 22.5, - 67.5 - ] - }, - 'properties': { - 'value': 20, - 'geohash': 'u', - 'center': [ - 22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'u', - 'value': 'u', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 20, - 'value': 20, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 0, - 45 - ], - [ - 45, - 45 - ], - [ - 45, - 90 - ], - [ - 0, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 22.5 - ] - }, - 'properties': { - 'value': 18, - 'geohash': '9', - 'center': [ - -112.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '9', - 'value': '9', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 18, - 'value': 18, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 0 - ], - [ - -90, - 0 - ], - [ - -90, - 45 - ], - [ - -135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - 67.5 - ] - }, - 'properties': { - 'value': 14, - 'geohash': 'v', - 'center': [ - 67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'v', - 'value': 'v', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 14, - 'value': 14, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - 45 - ], - [ - 90, - 45 - ], - [ - 90, - 90 - ], - [ - 45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 22.5 - ] - }, - 'properties': { - 'value': 11, - 'geohash': 'e', - 'center': [ - -22.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'e', - 'value': 'e', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 11, - 'value': 11, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 0 - ], - [ - 0, - 0 - ], - [ - 0, - 45 - ], - [ - -45, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -22.5 - ] - }, - 'properties': { - 'value': 9, - 'geohash': 'r', - 'center': [ - 157.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'r', - 'value': 'r', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 9, - 'value': 9, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -45 - ], - [ - 180, - -45 - ], - [ - 180, - 0 - ], - [ - 135, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'y', - 'center': [ - 112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'y', - 'value': 'y', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - 45 - ], - [ - 135, - 45 - ], - [ - 135, - 90 - ], - [ - 90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - 67.5 - ] - }, - 'properties': { - 'value': 6, - 'geohash': 'f', - 'center': [ - -67.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'f', - 'value': 'f', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 6, - 'value': 6, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - 45 - ], - [ - -45, - 45 - ], - [ - -45, - 90 - ], - [ - -90, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - 67.5 - ] - }, - 'properties': { - 'value': 5, - 'geohash': 'g', - 'center': [ - -22.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'g', - 'value': 'g', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 5, - 'value': 5, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - 45 - ], - [ - 0, - 45 - ], - [ - 0, - 90 - ], - [ - -45, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -112.5, - 67.5 - ] - }, - 'properties': { - 'value': 5, - 'geohash': 'c', - 'center': [ - -112.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'c', - 'value': 'c', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 5, - 'value': 5, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -135, - 45 - ], - [ - -90, - 45 - ], - [ - -90, - 90 - ], - [ - -135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -157.5, - 67.5 - ] - }, - 'properties': { - 'value': 4, - 'geohash': 'b', - 'center': [ - -157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'b', - 'value': 'b', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 4, - 'value': 4, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -180, - 45 - ], - [ - -135, - 45 - ], - [ - -135, - 90 - ], - [ - -180, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 112.5, - -22.5 - ] - }, - 'properties': { - 'value': 3, - 'geohash': 'q', - 'center': [ - 112.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'q', - 'value': 'q', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 3, - 'value': 3, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 90, - -45 - ], - [ - 135, - -45 - ], - [ - 135, - 0 - ], - [ - 90, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -67.5, - -67.5 - ] - }, - 'properties': { - 'value': 2, - 'geohash': '4', - 'center': [ - -67.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '4', - 'value': '4', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 2, - 'value': 2, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -90, - -90 - ], - [ - -45, - -90 - ], - [ - -45, - -45 - ], - [ - -90, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'z', - 'center': [ - 157.5, - 67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'z', - 'value': 'z', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 45 - ], - [ - 180, - 45 - ], - [ - 180, - 90 - ], - [ - 135, - 90 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - 22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'x', - 'center': [ - 157.5, - 22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'x', - 'value': 'x', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - 0 - ], - [ - 180, - 0 - ], - [ - 180, - 45 - ], - [ - 135, - 45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 157.5, - -67.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'p', - 'center': [ - 157.5, - -67.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'p', - 'value': 'p', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 135, - -90 - ], - [ - 180, - -90 - ], - [ - 180, - -45 - ], - [ - 135, - -45 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - 67.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': 'm', - 'center': [ - 67.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': 'm', - 'value': 'm', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - 45, - -45 - ], - [ - 90, - -45 - ], - [ - 90, - 0 - ], - [ - 45, - 0 - ] - ] - } - }, - { - 'type': 'Feature', - 'geometry': { - 'type': 'Point', - 'coordinates': [ - -22.5, - -22.5 - ] - }, - 'properties': { - 'value': 1, - 'geohash': '7', - 'center': [ - -22.5, - -22.5 - ], - 'aggConfigResult': { - '$parent': { - '$parent': { - '$parent': null, - 'key': 'IN', - 'value': 'IN', - 'aggConfig': { - 'id': '3', - 'type': 'terms', - 'schema': 'split', - 'params': { - 'field': 'geo.dest', - 'size': 2, - 'order': 'desc', - 'orderBy': '1', - 'row': true - } - }, - 'type': 'bucket' - }, - 'key': '7', - 'value': '7', - 'aggConfig': { - 'id': '2', - 'type': 'geohash_grid', - 'schema': 'segment', - 'params': { - 'field': 'geo.coordinates', - 'precision': 1 - } - }, - 'type': 'bucket' - }, - 'key': 1, - 'value': 1, - 'aggConfig': { - 'id': '1', - 'type': 'count', - 'schema': 'metric', - 'params': {} - }, - 'type': 'metric' - }, - 'rectangle': [ - [ - -45, - -45 - ], - [ - 0, - -45 - ], - [ - 0, - 0 - ], - [ - -45, - 0 - ] - ] - } - } - ], - 'properties': { - 'min': 1, - 'max': 31 - } - } - } - ], - 'hits': 1639 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_columns.js deleted file mode 100644 index 96d2cfd174579c..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_columns.js +++ /dev/null @@ -1,368 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'label': '404: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 2147483600, - 'y': 1, - 'y0': 0 - }, - { - 'x': 3221225400, - 'y': 0, - 'y0': 0 - }, - { - 'x': 4294967200, - 'y': 0, - 'y0': 0 - }, - { - 'x': 5368709100, - 'y': 0, - 'y0': 0 - }, - { - 'x': 6442450900, - 'y': 0, - 'y0': 0 - }, - { - 'x': 7516192700, - 'y': 0, - 'y0': 0 - }, - { - 'x': 8589934500, - 'y': 0, - 'y0': 0 - }, - { - 'x': 10737418200, - 'y': 0, - 'y0': 0 - }, - { - 'x': 11811160000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 12884901800, - 'y': 1, - 'y0': 0 - }, - { - 'x': 13958643700, - 'y': 0, - 'y0': 0 - }, - { - 'x': 15032385500, - 'y': 0, - 'y0': 0 - }, - { - 'x': 16106127300, - 'y': 0, - 'y0': 0 - }, - { - 'x': 18253611000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 19327352800, - 'y': 0, - 'y0': 0 - }, - { - 'x': 20401094600, - 'y': 0, - 'y0': 0 - }, - { - 'x': 21474836400, - 'y': 0, - 'y0': 0 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '200: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 2147483600, - 'y': 0, - 'y0': 0 - }, - { - 'x': 3221225400, - 'y': 2, - 'y0': 0 - }, - { - 'x': 4294967200, - 'y': 3, - 'y0': 0 - }, - { - 'x': 5368709100, - 'y': 3, - 'y0': 0 - }, - { - 'x': 6442450900, - 'y': 1, - 'y0': 0 - }, - { - 'x': 7516192700, - 'y': 1, - 'y0': 0 - }, - { - 'x': 8589934500, - 'y': 4, - 'y0': 0 - }, - { - 'x': 10737418200, - 'y': 0, - 'y0': 0 - }, - { - 'x': 11811160000, - 'y': 1, - 'y0': 0 - }, - { - 'x': 12884901800, - 'y': 1, - 'y0': 0 - }, - { - 'x': 13958643700, - 'y': 1, - 'y0': 0 - }, - { - 'x': 15032385500, - 'y': 2, - 'y0': 0 - }, - { - 'x': 16106127300, - 'y': 3, - 'y0': 0 - }, - { - 'x': 18253611000, - 'y': 4, - 'y0': 0 - }, - { - 'x': 19327352800, - 'y': 5, - 'y0': 0 - }, - { - 'x': 20401094600, - 'y': 2, - 'y0': 0 - }, - { - 'x': 21474836400, - 'y': 2, - 'y0': 0 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '503: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 2147483600, - 'y': 0, - 'y0': 0 - }, - { - 'x': 3221225400, - 'y': 0, - 'y0': 0 - }, - { - 'x': 4294967200, - 'y': 0, - 'y0': 0 - }, - { - 'x': 5368709100, - 'y': 0, - 'y0': 0 - }, - { - 'x': 6442450900, - 'y': 0, - 'y0': 0 - }, - { - 'x': 7516192700, - 'y': 0, - 'y0': 0 - }, - { - 'x': 8589934500, - 'y': 0, - 'y0': 0 - }, - { - 'x': 10737418200, - 'y': 1, - 'y0': 0 - }, - { - 'x': 11811160000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 12884901800, - 'y': 0, - 'y0': 0 - }, - { - 'x': 13958643700, - 'y': 0, - 'y0': 0 - }, - { - 'x': 15032385500, - 'y': 0, - 'y0': 0 - }, - { - 'x': 16106127300, - 'y': 0, - 'y0': 0 - }, - { - 'x': 18253611000, - 'y': 0, - 'y0': 0 - }, - { - 'x': 19327352800, - 'y': 0, - 'y0': 0 - }, - { - 'x': 20401094600, - 'y': 0, - 'y0': 0 - }, - { - 'x': 21474836400, - 'y': 0, - 'y0': 0 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'xAxisOrderedValues': [ - 2147483600, - 3221225400, - 4294967200, - 5368709100, - 6442450900, - 7516192700, - 8589934500, - 10737418200, - 11811160000, - 12884901800, - 13958643700, - 15032385500, - 16106127300, - 18253611000, - 19327352800, - 20401094600, - 21474836400, - ], - 'hits': 40 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_rows.js deleted file mode 100644 index 27050030ebdfd5..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_rows.js +++ /dev/null @@ -1,212 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'label': '404: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 2147483600, - 'y': 1 - }, - { - 'x': 10737418200, - 'y': 1 - }, - { - 'x': 15032385500, - 'y': 2 - }, - { - 'x': 19327352800, - 'y': 1 - }, - { - 'x': 32212254700, - 'y': 1 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '200: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 3221225400, - 'y': 4 - }, - { - 'x': 4294967200, - 'y': 3 - }, - { - 'x': 5368709100, - 'y': 3 - }, - { - 'x': 6442450900, - 'y': 2 - }, - { - 'x': 7516192700, - 'y': 2 - }, - { - 'x': 8589934500, - 'y': 2 - }, - { - 'x': 9663676400, - 'y': 3 - }, - { - 'x': 11811160000, - 'y': 3 - }, - { - 'x': 12884901800, - 'y': 2 - }, - { - 'x': 13958643700, - 'y': 1 - }, - { - 'x': 15032385500, - 'y': 2 - }, - { - 'x': 16106127300, - 'y': 3 - }, - { - 'x': 17179869100, - 'y': 1 - }, - { - 'x': 18253611000, - 'y': 4 - }, - { - 'x': 19327352800, - 'y': 1 - }, - { - 'x': 20401094600, - 'y': 1 - }, - { - 'x': 21474836400, - 'y': 4 - }, - { - 'x': 32212254700, - 'y': 3 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '503: response', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 10737418200, - 'y': 1 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'xAxisOrderedValues': [ - 2147483600, - 3221225400, - 4294967200, - 5368709100, - 6442450900, - 7516192700, - 8589934500, - 9663676400, - 10737418200, - 11811160000, - 12884901800, - 13958643700, - 15032385500, - 16106127300, - 17179869100, - 18253611000, - 19327352800, - 20401094600, - 21474836400, - 32212254700, - ], - 'hits': 51 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_series.js deleted file mode 100644 index 5c7554db2060df..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_series.js +++ /dev/null @@ -1,124 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': 'machine.ram', - 'ordered': { - 'interval': 100 - }, - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 3221225400, - 'y': 5 - }, - { - 'x': 4294967200, - 'y': 2 - }, - { - 'x': 5368709100, - 'y': 5 - }, - { - 'x': 6442450900, - 'y': 4 - }, - { - 'x': 7516192700, - 'y': 1 - }, - { - 'x': 9663676400, - 'y': 9 - }, - { - 'x': 10737418200, - 'y': 5 - }, - { - 'x': 11811160000, - 'y': 5 - }, - { - 'x': 12884901800, - 'y': 2 - }, - { - 'x': 13958643700, - 'y': 3 - }, - { - 'x': 15032385500, - 'y': 3 - }, - { - 'x': 16106127300, - 'y': 3 - }, - { - 'x': 17179869100, - 'y': 1 - }, - { - 'x': 18253611000, - 'y': 6 - }, - { - 'x': 19327352800, - 'y': 3 - }, - { - 'x': 20401094600, - 'y': 3 - }, - { - 'x': 21474836400, - 'y': 7 - }, - { - 'x': 32212254700, - 'y': 4 - } - ] - } - ], - 'hits': 71, - 'xAxisOrderedValues': [ - 3221225400, - 4294967200, - 5368709100, - 6442450900, - 7516192700, - 9663676400, - 10737418200, - 11811160000, - 12884901800, - 13958643700, - 15032385500, - 16106127300, - 17179869100, - 18253611000, - 19327352800, - 20401094600, - 21474836400, - 32212254700, - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_slices.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_slices.js deleted file mode 100644 index c47155840cec50..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_slices.js +++ /dev/null @@ -1,309 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'slices': { - 'children': [ - { - 'name': 0, - 'size': 378611, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 1000, - 'size': 205997, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 2000, - 'size': 397189, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 3000, - 'size': 397195, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 4000, - 'size': 398429, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 5000, - 'size': 397843, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 6000, - 'size': 398140, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 7000, - 'size': 398076, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 8000, - 'size': 396746, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 9000, - 'size': 397418, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 10000, - 'size': 20222, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 11000, - 'size': 20173, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 12000, - 'size': 20026, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 13000, - 'size': 19986, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 14000, - 'size': 20091, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 15000, - 'size': 20052, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 16000, - 'size': 20349, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 17000, - 'size': 20290, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 18000, - 'size': 20399, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 19000, - 'size': 20133, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - }, - { - 'name': 20000, - 'size': 9, - 'aggConfig': { - 'type': 'histogram', - 'schema': 'segment', - 'fieldFormatter': _.constant(String), - 'params': { - 'interval': 1000, - 'extended_bounds': {} - } - } - } - ] - }, - 'names': [ - 0, - 1000, - 2000, - 3000, - 4000, - 5000, - 6000, - 7000, - 8000, - 9000, - 10000, - 11000, - 12000, - 13000, - 14000, - 15000, - 16000, - 17000, - 18000, - 19000, - 20000 - ], - 'hits': 3967374, - 'tooltipFormatter': function (event) { - return event.point; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/not_enough_data/_one_point.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/not_enough_data/_one_point.js deleted file mode 100644 index b05e258133963b..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/not_enough_data/_one_point.js +++ /dev/null @@ -1,34 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': '', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '_all', - 'y': 274 - } - ] - } - ], - 'hits': 274, - 'xAxisOrderedValues': ['_all'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_columns.js deleted file mode 100644 index d6f3fc4361f329..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_columns.js +++ /dev/null @@ -1,62 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'label': 'apache: _type', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 13309 - }, - { - 'x': '1000.0-2000.0', - 'y': 7196 - } - ] - } - ] - }, - { - 'label': 'nginx: _type', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 3278 - }, - { - 'x': '1000.0-2000.0', - 'y': 1804 - } - ] - } - ] - } - ], - 'hits': 171499, - 'xAxisOrderedValues': ['0.0-1000.0', '1000.0-2000.0'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_rows.js deleted file mode 100644 index b420565b1c96b5..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_rows.js +++ /dev/null @@ -1,88 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'label': 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1: agent.raw', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 6422, - 'y0': 0 - }, - { - 'x': '1000.0-2000.0', - 'y': 3446, - 'y0': 0 - } - ] - } - ] - }, - { - 'label': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24: agent.raw', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 5430, - 'y0': 0 - }, - { - 'x': '1000.0-2000.0', - 'y': 3010, - 'y0': 0 - } - ] - } - ] - }, - { - 'label': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322): agent.raw', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 4735, - 'y0': 0 - }, - { - 'x': '1000.0-2000.0', - 'y': 2542, - 'y0': 0 - } - ] - } - ] - } - ], - 'hits': 171501, - 'xAxisOrderedValues': ['0.0-1000.0', '1000.0-2000.0'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_series.js deleted file mode 100644 index 2ac35efadc8f2f..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_series.js +++ /dev/null @@ -1,38 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': 'bytes ranges', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': '0.0-1000.0', - 'y': 16576 - }, - { - 'x': '1000.0-2000.0', - 'y': 9005 - } - ] - } - ], - 'hits': 171500, - 'xAxisOrderedValues': ['0.0-1000.0', '1000.0-2000.0'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_columns.js deleted file mode 100644 index 5b1e29ac0d54ae..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_columns.js +++ /dev/null @@ -1,242 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'label': 'http: links', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 144000 - }, - { - 'x': 'info', - 'y': 128237 - }, - { - 'x': 'security', - 'y': 34518 - }, - { - 'x': 'error', - 'y': 10258 - }, - { - 'x': 'warning', - 'y': 17188 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'info: links', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 108148 - }, - { - 'x': 'info', - 'y': 96242 - }, - { - 'x': 'security', - 'y': 25889 - }, - { - 'x': 'error', - 'y': 7673 - }, - { - 'x': 'warning', - 'y': 12842 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'www.slate.com: links', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 98056 - }, - { - 'x': 'info', - 'y': 87344 - }, - { - 'x': 'security', - 'y': 23577 - }, - { - 'x': 'error', - 'y': 7004 - }, - { - 'x': 'warning', - 'y': 11759 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'twitter.com: links', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 74154 - }, - { - 'x': 'info', - 'y': 65963 - }, - { - 'x': 'security', - 'y': 17832 - }, - { - 'x': 'error', - 'y': 5258 - }, - { - 'x': 'warning', - 'y': 8906 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'www.www.slate.com: links', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 62591 - }, - { - 'x': 'info', - 'y': 55822 - }, - { - 'x': 'security', - 'y': 15100 - }, - { - 'x': 'error', - 'y': 4564 - }, - { - 'x': 'warning', - 'y': 7498 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'hits': 171446 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_rows.js deleted file mode 100644 index 147eb691eb67bc..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_rows.js +++ /dev/null @@ -1,242 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'label': 'h3: headings', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 144000 - }, - { - 'x': 'info', - 'y': 128235 - }, - { - 'x': 'security', - 'y': 34518 - }, - { - 'x': 'error', - 'y': 10257 - }, - { - 'x': 'warning', - 'y': 17188 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'h5: headings', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 144000 - }, - { - 'x': 'info', - 'y': 128235 - }, - { - 'x': 'security', - 'y': 34518 - }, - { - 'x': 'error', - 'y': 10257 - }, - { - 'x': 'warning', - 'y': 17188 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'http: headings', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 144000 - }, - { - 'x': 'info', - 'y': 128235 - }, - { - 'x': 'security', - 'y': 34518 - }, - { - 'x': 'error', - 'y': 10257 - }, - { - 'x': 'warning', - 'y': 17188 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'success: headings', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 120689 - }, - { - 'x': 'info', - 'y': 107621 - }, - { - 'x': 'security', - 'y': 28916 - }, - { - 'x': 'error', - 'y': 8590 - }, - { - 'x': 'warning', - 'y': 14548 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': 'www.slate.com: headings', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 62292 - }, - { - 'x': 'info', - 'y': 55646 - }, - { - 'x': 'security', - 'y': 14823 - }, - { - 'x': 'error', - 'y': 4441 - }, - { - 'x': 'warning', - 'y': 7539 - } - ] - } - ], - 'xAxisOrderedValues': ['success', 'info', 'security', 'error', 'warning'], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'hits': 171445 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_series.js deleted file mode 100644 index 3691d854c6c2a9..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_series.js +++ /dev/null @@ -1,49 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': 'Top 5 unusual terms in @tags', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'success', - 'y': 143995 - }, - { - 'x': 'info', - 'y': 128233 - }, - { - 'x': 'security', - 'y': 34515 - }, - { - 'x': 'error', - 'y': 10256 - }, - { - 'x': 'warning', - 'y': 17188 - } - ] - } - ], - 'hits': 171439, - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/stacked/_stacked.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/stacked/_stacked.js deleted file mode 100644 index 228a22ed534d59..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/stacked/_stacked.js +++ /dev/null @@ -1,1635 +0,0 @@ -import moment from 'moment'; - -export default { - 'label': '', - 'xAxisLabel': '@timestamp per 30 sec', - 'ordered': { - 'date': true, - 'interval': 30000, - 'min': 1416850340336, - 'max': 1416852140336 - }, - 'yAxisLabel': 'Count of documents', - 'xAxisOrderedValues': [ - 1416850320000, - 1416850350000, - 1416850380000, - 1416850410000, - 1416850440000, - 1416850470000, - 1416850500000, - 1416850530000, - 1416850560000, - 1416850590000, - 1416850620000, - 1416850650000, - 1416850680000, - 1416850710000, - 1416850740000, - 1416850770000, - 1416850800000, - 1416850830000, - 1416850860000, - 1416850890000, - 1416850920000, - 1416850950000, - 1416850980000, - 1416851010000, - 1416851040000, - 1416851070000, - 1416851100000, - 1416851130000, - 1416851160000, - 1416851190000, - 1416851220000, - 1416851250000, - 1416851280000, - 1416851310000, - 1416851340000, - 1416851370000, - 1416851400000, - 1416851430000, - 1416851460000, - 1416851490000, - 1416851520000, - 1416851550000, - 1416851580000, - 1416851610000, - 1416851640000, - 1416851670000, - 1416851700000, - 1416851730000, - 1416851760000, - 1416851790000, - 1416851820000, - 1416851850000, - 1416851880000, - 1416851910000, - 1416851940000, - 1416851970000, - 1416852000000, - 1416852030000, - 1416852060000, - 1416852090000, - 1416852120000, - ], - 'series': [ - { - 'label': 'jpg', - 'values': [ - { - 'x': 1416850320000, - 'y': 110, - 'y0': 0 - }, - { - 'x': 1416850350000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416850380000, - 'y': 34, - 'y0': 0 - }, - { - 'x': 1416850410000, - 'y': 21, - 'y0': 0 - }, - { - 'x': 1416850440000, - 'y': 32, - 'y0': 0 - }, - { - 'x': 1416850470000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416850500000, - 'y': 16, - 'y0': 0 - }, - { - 'x': 1416850530000, - 'y': 27, - 'y0': 0 - }, - { - 'x': 1416850560000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416850590000, - 'y': 38, - 'y0': 0 - }, - { - 'x': 1416850620000, - 'y': 33, - 'y0': 0 - }, - { - 'x': 1416850650000, - 'y': 33, - 'y0': 0 - }, - { - 'x': 1416850680000, - 'y': 31, - 'y0': 0 - }, - { - 'x': 1416850710000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416850740000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416850770000, - 'y': 38, - 'y0': 0 - }, - { - 'x': 1416850800000, - 'y': 34, - 'y0': 0 - }, - { - 'x': 1416850830000, - 'y': 30, - 'y0': 0 - }, - { - 'x': 1416850860000, - 'y': 38, - 'y0': 0 - }, - { - 'x': 1416850890000, - 'y': 19, - 'y0': 0 - }, - { - 'x': 1416850920000, - 'y': 23, - 'y0': 0 - }, - { - 'x': 1416850950000, - 'y': 33, - 'y0': 0 - }, - { - 'x': 1416850980000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851010000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416851040000, - 'y': 22, - 'y0': 0 - }, - { - 'x': 1416851070000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851100000, - 'y': 27, - 'y0': 0 - }, - { - 'x': 1416851130000, - 'y': 32, - 'y0': 0 - }, - { - 'x': 1416851160000, - 'y': 32, - 'y0': 0 - }, - { - 'x': 1416851190000, - 'y': 30, - 'y0': 0 - }, - { - 'x': 1416851220000, - 'y': 32, - 'y0': 0 - }, - { - 'x': 1416851250000, - 'y': 36, - 'y0': 0 - }, - { - 'x': 1416851280000, - 'y': 32, - 'y0': 0 - }, - { - 'x': 1416851310000, - 'y': 29, - 'y0': 0 - }, - { - 'x': 1416851340000, - 'y': 22, - 'y0': 0 - }, - { - 'x': 1416851370000, - 'y': 29, - 'y0': 0 - }, - { - 'x': 1416851400000, - 'y': 33, - 'y0': 0 - }, - { - 'x': 1416851430000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851460000, - 'y': 39, - 'y0': 0 - }, - { - 'x': 1416851490000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851520000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851550000, - 'y': 28, - 'y0': 0 - }, - { - 'x': 1416851580000, - 'y': 30, - 'y0': 0 - }, - { - 'x': 1416851610000, - 'y': 29, - 'y0': 0 - }, - { - 'x': 1416851640000, - 'y': 30, - 'y0': 0 - }, - { - 'x': 1416851670000, - 'y': 23, - 'y0': 0 - }, - { - 'x': 1416851700000, - 'y': 23, - 'y0': 0 - }, - { - 'x': 1416851730000, - 'y': 27, - 'y0': 0 - }, - { - 'x': 1416851760000, - 'y': 21, - 'y0': 0 - }, - { - 'x': 1416851790000, - 'y': 24, - 'y0': 0 - }, - { - 'x': 1416851820000, - 'y': 26, - 'y0': 0 - }, - { - 'x': 1416851850000, - 'y': 26, - 'y0': 0 - }, - { - 'x': 1416851880000, - 'y': 21, - 'y0': 0 - }, - { - 'x': 1416851910000, - 'y': 33, - 'y0': 0 - }, - { - 'x': 1416851940000, - 'y': 23, - 'y0': 0 - }, - { - 'x': 1416851970000, - 'y': 46, - 'y0': 0 - }, - { - 'x': 1416852000000, - 'y': 27, - 'y0': 0 - }, - { - 'x': 1416852030000, - 'y': 20, - 'y0': 0 - }, - { - 'x': 1416852060000, - 'y': 34, - 'y0': 0 - }, - { - 'x': 1416852090000, - 'y': 15, - 'y0': 0 - }, - { - 'x': 1416852120000, - 'y': 18, - 'y0': 0 - } - ] - }, - { - 'label': 'css', - 'values': [ - { - 'x': 1416850320000, - 'y': 3, - 'y0': 11 - }, - { - 'x': 1416850350000, - 'y': 13, - 'y0': 24 - }, - { - 'x': 1416850380000, - 'y': 5, - 'y0': 34 - }, - { - 'x': 1416850410000, - 'y': 12, - 'y0': 21 - }, - { - 'x': 1416850440000, - 'y': 9, - 'y0': 32 - }, - { - 'x': 1416850470000, - 'y': 12, - 'y0': 24 - }, - { - 'x': 1416850500000, - 'y': 6, - 'y0': 16 - }, - { - 'x': 1416850530000, - 'y': 6, - 'y0': 27 - }, - { - 'x': 1416850560000, - 'y': 11, - 'y0': 24 - }, - { - 'x': 1416850590000, - 'y': 11, - 'y0': 38 - }, - { - 'x': 1416850620000, - 'y': 6, - 'y0': 33 - }, - { - 'x': 1416850650000, - 'y': 8, - 'y0': 33 - }, - { - 'x': 1416850680000, - 'y': 6, - 'y0': 31 - }, - { - 'x': 1416850710000, - 'y': 4, - 'y0': 24 - }, - { - 'x': 1416850740000, - 'y': 9, - 'y0': 24 - }, - { - 'x': 1416850770000, - 'y': 3, - 'y0': 38 - }, - { - 'x': 1416850800000, - 'y': 5, - 'y0': 34 - }, - { - 'x': 1416850830000, - 'y': 6, - 'y0': 30 - }, - { - 'x': 1416850860000, - 'y': 9, - 'y0': 38 - }, - { - 'x': 1416850890000, - 'y': 5, - 'y0': 19 - }, - { - 'x': 1416850920000, - 'y': 8, - 'y0': 23 - }, - { - 'x': 1416850950000, - 'y': 9, - 'y0': 33 - }, - { - 'x': 1416850980000, - 'y': 5, - 'y0': 28 - }, - { - 'x': 1416851010000, - 'y': 6, - 'y0': 24 - }, - { - 'x': 1416851040000, - 'y': 9, - 'y0': 22 - }, - { - 'x': 1416851070000, - 'y': 9, - 'y0': 28 - }, - { - 'x': 1416851100000, - 'y': 11, - 'y0': 27 - }, - { - 'x': 1416851130000, - 'y': 5, - 'y0': 32 - }, - { - 'x': 1416851160000, - 'y': 8, - 'y0': 32 - }, - { - 'x': 1416851190000, - 'y': 6, - 'y0': 30 - }, - { - 'x': 1416851220000, - 'y': 10, - 'y0': 32 - }, - { - 'x': 1416851250000, - 'y': 5, - 'y0': 36 - }, - { - 'x': 1416851280000, - 'y': 6, - 'y0': 32 - }, - { - 'x': 1416851310000, - 'y': 4, - 'y0': 29 - }, - { - 'x': 1416851340000, - 'y': 8, - 'y0': 22 - }, - { - 'x': 1416851370000, - 'y': 3, - 'y0': 29 - }, - { - 'x': 1416851400000, - 'y': 8, - 'y0': 33 - }, - { - 'x': 1416851430000, - 'y': 10, - 'y0': 28 - }, - { - 'x': 1416851460000, - 'y': 5, - 'y0': 39 - }, - { - 'x': 1416851490000, - 'y': 7, - 'y0': 28 - }, - { - 'x': 1416851520000, - 'y': 6, - 'y0': 28 - }, - { - 'x': 1416851550000, - 'y': 4, - 'y0': 28 - }, - { - 'x': 1416851580000, - 'y': 9, - 'y0': 30 - }, - { - 'x': 1416851610000, - 'y': 3, - 'y0': 29 - }, - { - 'x': 1416851640000, - 'y': 9, - 'y0': 30 - }, - { - 'x': 1416851670000, - 'y': 6, - 'y0': 23 - }, - { - 'x': 1416851700000, - 'y': 11, - 'y0': 23 - }, - { - 'x': 1416851730000, - 'y': 4, - 'y0': 27 - }, - { - 'x': 1416851760000, - 'y': 8, - 'y0': 21 - }, - { - 'x': 1416851790000, - 'y': 5, - 'y0': 24 - }, - { - 'x': 1416851820000, - 'y': 7, - 'y0': 26 - }, - { - 'x': 1416851850000, - 'y': 7, - 'y0': 26 - }, - { - 'x': 1416851880000, - 'y': 4, - 'y0': 21 - }, - { - 'x': 1416851910000, - 'y': 8, - 'y0': 33 - }, - { - 'x': 1416851940000, - 'y': 6, - 'y0': 23 - }, - { - 'x': 1416851970000, - 'y': 6, - 'y0': 46 - }, - { - 'x': 1416852000000, - 'y': 3, - 'y0': 27 - }, - { - 'x': 1416852030000, - 'y': 6, - 'y0': 20 - }, - { - 'x': 1416852060000, - 'y': 5, - 'y0': 34 - }, - { - 'x': 1416852090000, - 'y': 5, - 'y0': 15 - }, - { - 'x': 1416852120000, - 'y': 1, - 'y0': 18 - } - ] - }, - { - 'label': 'gif', - 'values': [ - { - 'x': 1416850320000, - 'y': 1, - 'y0': 14 - }, - { - 'x': 1416850350000, - 'y': 2, - 'y0': 37 - }, - { - 'x': 1416850380000, - 'y': 4, - 'y0': 39 - }, - { - 'x': 1416850410000, - 'y': 2, - 'y0': 33 - }, - { - 'x': 1416850440000, - 'y': 3, - 'y0': 41 - }, - { - 'x': 1416850470000, - 'y': 1, - 'y0': 36 - }, - { - 'x': 1416850500000, - 'y': 1, - 'y0': 22 - }, - { - 'x': 1416850530000, - 'y': 1, - 'y0': 33 - }, - { - 'x': 1416850560000, - 'y': 2, - 'y0': 35 - }, - { - 'x': 1416850590000, - 'y': 5, - 'y0': 49 - }, - { - 'x': 1416850620000, - 'y': 1, - 'y0': 39 - }, - { - 'x': 1416850650000, - 'y': 1, - 'y0': 41 - }, - { - 'x': 1416850680000, - 'y': 4, - 'y0': 37 - }, - { - 'x': 1416850710000, - 'y': 1, - 'y0': 28 - }, - { - 'x': 1416850740000, - 'y': 3, - 'y0': 33 - }, - { - 'x': 1416850770000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416850800000, - 'y': 2, - 'y0': 39 - }, - { - 'x': 1416850830000, - 'y': 5, - 'y0': 36 - }, - { - 'x': 1416850860000, - 'y': 3, - 'y0': 47 - }, - { - 'x': 1416850890000, - 'y': 1, - 'y0': 24 - }, - { - 'x': 1416850920000, - 'y': 3, - 'y0': 31 - }, - { - 'x': 1416850950000, - 'y': 4, - 'y0': 42 - }, - { - 'x': 1416850980000, - 'y': 3, - 'y0': 33 - }, - { - 'x': 1416851010000, - 'y': 5, - 'y0': 30 - }, - { - 'x': 1416851040000, - 'y': 2, - 'y0': 31 - }, - { - 'x': 1416851070000, - 'y': 3, - 'y0': 37 - }, - { - 'x': 1416851100000, - 'y': 5, - 'y0': 38 - }, - { - 'x': 1416851130000, - 'y': 3, - 'y0': 37 - }, - { - 'x': 1416851160000, - 'y': 4, - 'y0': 40 - }, - { - 'x': 1416851190000, - 'y': 9, - 'y0': 36 - }, - { - 'x': 1416851220000, - 'y': 7, - 'y0': 42 - }, - { - 'x': 1416851250000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416851280000, - 'y': 1, - 'y0': 38 - }, - { - 'x': 1416851310000, - 'y': 2, - 'y0': 33 - }, - { - 'x': 1416851340000, - 'y': 5, - 'y0': 30 - }, - { - 'x': 1416851370000, - 'y': 3, - 'y0': 32 - }, - { - 'x': 1416851400000, - 'y': 5, - 'y0': 41 - }, - { - 'x': 1416851430000, - 'y': 4, - 'y0': 38 - }, - { - 'x': 1416851460000, - 'y': 5, - 'y0': 44 - }, - { - 'x': 1416851490000, - 'y': 2, - 'y0': 35 - }, - { - 'x': 1416851520000, - 'y': 2, - 'y0': 34 - }, - { - 'x': 1416851550000, - 'y': 4, - 'y0': 32 - }, - { - 'x': 1416851580000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416851610000, - 'y': 4, - 'y0': 32 - }, - { - 'x': 1416851640000, - 'y': 0, - 'y0': 39 - }, - { - 'x': 1416851670000, - 'y': 2, - 'y0': 29 - }, - { - 'x': 1416851700000, - 'y': 1, - 'y0': 34 - }, - { - 'x': 1416851730000, - 'y': 3, - 'y0': 31 - }, - { - 'x': 1416851760000, - 'y': 0, - 'y0': 29 - }, - { - 'x': 1416851790000, - 'y': 4, - 'y0': 29 - }, - { - 'x': 1416851820000, - 'y': 3, - 'y0': 33 - }, - { - 'x': 1416851850000, - 'y': 3, - 'y0': 33 - }, - { - 'x': 1416851880000, - 'y': 0, - 'y0': 25 - }, - { - 'x': 1416851910000, - 'y': 0, - 'y0': 41 - }, - { - 'x': 1416851940000, - 'y': 3, - 'y0': 29 - }, - { - 'x': 1416851970000, - 'y': 3, - 'y0': 52 - }, - { - 'x': 1416852000000, - 'y': 1, - 'y0': 30 - }, - { - 'x': 1416852030000, - 'y': 5, - 'y0': 26 - }, - { - 'x': 1416852060000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416852090000, - 'y': 1, - 'y0': 20 - }, - { - 'x': 1416852120000, - 'y': 2, - 'y0': 19 - } - ] - }, - { - 'label': 'png', - 'values': [ - { - 'x': 1416850320000, - 'y': 1, - 'y0': 15 - }, - { - 'x': 1416850350000, - 'y': 6, - 'y0': 39 - }, - { - 'x': 1416850380000, - 'y': 6, - 'y0': 43 - }, - { - 'x': 1416850410000, - 'y': 5, - 'y0': 35 - }, - { - 'x': 1416850440000, - 'y': 3, - 'y0': 44 - }, - { - 'x': 1416850470000, - 'y': 5, - 'y0': 37 - }, - { - 'x': 1416850500000, - 'y': 6, - 'y0': 23 - }, - { - 'x': 1416850530000, - 'y': 1, - 'y0': 34 - }, - { - 'x': 1416850560000, - 'y': 3, - 'y0': 37 - }, - { - 'x': 1416850590000, - 'y': 2, - 'y0': 54 - }, - { - 'x': 1416850620000, - 'y': 1, - 'y0': 40 - }, - { - 'x': 1416850650000, - 'y': 1, - 'y0': 42 - }, - { - 'x': 1416850680000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416850710000, - 'y': 5, - 'y0': 29 - }, - { - 'x': 1416850740000, - 'y': 7, - 'y0': 36 - }, - { - 'x': 1416850770000, - 'y': 2, - 'y0': 43 - }, - { - 'x': 1416850800000, - 'y': 3, - 'y0': 41 - }, - { - 'x': 1416850830000, - 'y': 6, - 'y0': 41 - }, - { - 'x': 1416850860000, - 'y': 2, - 'y0': 50 - }, - { - 'x': 1416850890000, - 'y': 4, - 'y0': 25 - }, - { - 'x': 1416850920000, - 'y': 2, - 'y0': 34 - }, - { - 'x': 1416850950000, - 'y': 3, - 'y0': 46 - }, - { - 'x': 1416850980000, - 'y': 8, - 'y0': 36 - }, - { - 'x': 1416851010000, - 'y': 4, - 'y0': 35 - }, - { - 'x': 1416851040000, - 'y': 4, - 'y0': 33 - }, - { - 'x': 1416851070000, - 'y': 1, - 'y0': 40 - }, - { - 'x': 1416851100000, - 'y': 2, - 'y0': 43 - }, - { - 'x': 1416851130000, - 'y': 4, - 'y0': 40 - }, - { - 'x': 1416851160000, - 'y': 3, - 'y0': 44 - }, - { - 'x': 1416851190000, - 'y': 4, - 'y0': 45 - }, - { - 'x': 1416851220000, - 'y': 2, - 'y0': 49 - }, - { - 'x': 1416851250000, - 'y': 4, - 'y0': 43 - }, - { - 'x': 1416851280000, - 'y': 8, - 'y0': 39 - }, - { - 'x': 1416851310000, - 'y': 4, - 'y0': 35 - }, - { - 'x': 1416851340000, - 'y': 4, - 'y0': 35 - }, - { - 'x': 1416851370000, - 'y': 7, - 'y0': 35 - }, - { - 'x': 1416851400000, - 'y': 2, - 'y0': 46 - }, - { - 'x': 1416851430000, - 'y': 3, - 'y0': 42 - }, - { - 'x': 1416851460000, - 'y': 3, - 'y0': 49 - }, - { - 'x': 1416851490000, - 'y': 3, - 'y0': 37 - }, - { - 'x': 1416851520000, - 'y': 4, - 'y0': 36 - }, - { - 'x': 1416851550000, - 'y': 3, - 'y0': 36 - }, - { - 'x': 1416851580000, - 'y': 4, - 'y0': 42 - }, - { - 'x': 1416851610000, - 'y': 5, - 'y0': 36 - }, - { - 'x': 1416851640000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416851670000, - 'y': 3, - 'y0': 31 - }, - { - 'x': 1416851700000, - 'y': 2, - 'y0': 35 - }, - { - 'x': 1416851730000, - 'y': 5, - 'y0': 34 - }, - { - 'x': 1416851760000, - 'y': 4, - 'y0': 29 - }, - { - 'x': 1416851790000, - 'y': 5, - 'y0': 33 - }, - { - 'x': 1416851820000, - 'y': 1, - 'y0': 36 - }, - { - 'x': 1416851850000, - 'y': 3, - 'y0': 36 - }, - { - 'x': 1416851880000, - 'y': 6, - 'y0': 25 - }, - { - 'x': 1416851910000, - 'y': 4, - 'y0': 41 - }, - { - 'x': 1416851940000, - 'y': 7, - 'y0': 32 - }, - { - 'x': 1416851970000, - 'y': 5, - 'y0': 55 - }, - { - 'x': 1416852000000, - 'y': 2, - 'y0': 31 - }, - { - 'x': 1416852030000, - 'y': 2, - 'y0': 31 - }, - { - 'x': 1416852060000, - 'y': 4, - 'y0': 42 - }, - { - 'x': 1416852090000, - 'y': 6, - 'y0': 21 - }, - { - 'x': 1416852120000, - 'y': 2, - 'y0': 21 - } - ] - }, - { - 'label': 'php', - 'values': [ - { - 'x': 1416850320000, - 'y': 0, - 'y0': 16 - }, - { - 'x': 1416850350000, - 'y': 1, - 'y0': 45 - }, - { - 'x': 1416850380000, - 'y': 0, - 'y0': 49 - }, - { - 'x': 1416850410000, - 'y': 2, - 'y0': 40 - }, - { - 'x': 1416850440000, - 'y': 0, - 'y0': 47 - }, - { - 'x': 1416850470000, - 'y': 0, - 'y0': 42 - }, - { - 'x': 1416850500000, - 'y': 3, - 'y0': 29 - }, - { - 'x': 1416850530000, - 'y': 1, - 'y0': 35 - }, - { - 'x': 1416850560000, - 'y': 3, - 'y0': 40 - }, - { - 'x': 1416850590000, - 'y': 2, - 'y0': 56 - }, - { - 'x': 1416850620000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416850650000, - 'y': 5, - 'y0': 43 - }, - { - 'x': 1416850680000, - 'y': 2, - 'y0': 43 - }, - { - 'x': 1416850710000, - 'y': 1, - 'y0': 34 - }, - { - 'x': 1416850740000, - 'y': 2, - 'y0': 43 - }, - { - 'x': 1416850770000, - 'y': 2, - 'y0': 45 - }, - { - 'x': 1416850800000, - 'y': 1, - 'y0': 44 - }, - { - 'x': 1416850830000, - 'y': 1, - 'y0': 47 - }, - { - 'x': 1416850860000, - 'y': 1, - 'y0': 52 - }, - { - 'x': 1416850890000, - 'y': 1, - 'y0': 29 - }, - { - 'x': 1416850920000, - 'y': 2, - 'y0': 36 - }, - { - 'x': 1416850950000, - 'y': 2, - 'y0': 49 - }, - { - 'x': 1416850980000, - 'y': 0, - 'y0': 44 - }, - { - 'x': 1416851010000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416851040000, - 'y': 2, - 'y0': 37 - }, - { - 'x': 1416851070000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416851100000, - 'y': 2, - 'y0': 45 - }, - { - 'x': 1416851130000, - 'y': 0, - 'y0': 44 - }, - { - 'x': 1416851160000, - 'y': 1, - 'y0': 47 - }, - { - 'x': 1416851190000, - 'y': 2, - 'y0': 49 - }, - { - 'x': 1416851220000, - 'y': 4, - 'y0': 51 - }, - { - 'x': 1416851250000, - 'y': 0, - 'y0': 47 - }, - { - 'x': 1416851280000, - 'y': 3, - 'y0': 47 - }, - { - 'x': 1416851310000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416851340000, - 'y': 2, - 'y0': 39 - }, - { - 'x': 1416851370000, - 'y': 2, - 'y0': 42 - }, - { - 'x': 1416851400000, - 'y': 3, - 'y0': 48 - }, - { - 'x': 1416851430000, - 'y': 1, - 'y0': 45 - }, - { - 'x': 1416851460000, - 'y': 0, - 'y0': 52 - }, - { - 'x': 1416851490000, - 'y': 2, - 'y0': 40 - }, - { - 'x': 1416851520000, - 'y': 1, - 'y0': 40 - }, - { - 'x': 1416851550000, - 'y': 3, - 'y0': 39 - }, - { - 'x': 1416851580000, - 'y': 1, - 'y0': 46 - }, - { - 'x': 1416851610000, - 'y': 2, - 'y0': 41 - }, - { - 'x': 1416851640000, - 'y': 1, - 'y0': 42 - }, - { - 'x': 1416851670000, - 'y': 2, - 'y0': 34 - }, - { - 'x': 1416851700000, - 'y': 3, - 'y0': 37 - }, - { - 'x': 1416851730000, - 'y': 1, - 'y0': 39 - }, - { - 'x': 1416851760000, - 'y': 1, - 'y0': 33 - }, - { - 'x': 1416851790000, - 'y': 1, - 'y0': 38 - }, - { - 'x': 1416851820000, - 'y': 1, - 'y0': 37 - }, - { - 'x': 1416851850000, - 'y': 1, - 'y0': 39 - }, - { - 'x': 1416851880000, - 'y': 1, - 'y0': 31 - }, - { - 'x': 1416851910000, - 'y': 2, - 'y0': 45 - }, - { - 'x': 1416851940000, - 'y': 0, - 'y0': 39 - }, - { - 'x': 1416851970000, - 'y': 0, - 'y0': 60 - }, - { - 'x': 1416852000000, - 'y': 1, - 'y0': 33 - }, - { - 'x': 1416852030000, - 'y': 2, - 'y0': 33 - }, - { - 'x': 1416852060000, - 'y': 1, - 'y0': 46 - }, - { - 'x': 1416852090000, - 'y': 1, - 'y0': 27 - }, - { - 'x': 1416852120000, - 'y': 0, - 'y0': 23 - } - ] - } - ], - 'hits': 2595, - 'xAxisFormatter': function (thing) { - return moment(thing); - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_columns.js deleted file mode 100644 index 4683640725f2a8..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_columns.js +++ /dev/null @@ -1,146 +0,0 @@ -import _ from 'lodash'; - -export default { - 'columns': [ - { - 'label': 'logstash: index', - 'xAxisLabel': 'Top 5 extension', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 110710 - }, - { - 'x': 'css', - 'y': 27376 - }, - { - 'x': 'png', - 'y': 16664 - }, - { - 'x': 'gif', - 'y': 11264 - }, - { - 'x': 'php', - 'y': 5448 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '2014.11.12: index', - 'xAxisLabel': 'Top 5 extension', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 110643 - }, - { - 'x': 'css', - 'y': 27350 - }, - { - 'x': 'png', - 'y': 16648 - }, - { - 'x': 'gif', - 'y': 11257 - }, - { - 'x': 'php', - 'y': 5440 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '2014.11.11: index', - 'xAxisLabel': 'Top 5 extension', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 67 - }, - { - 'x': 'css', - 'y': 26 - }, - { - 'x': 'png', - 'y': 16 - }, - { - 'x': 'gif', - 'y': 7 - }, - { - 'x': 'php', - 'y': 8 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'xAxisOrderedValues': ['jpg', 'css', 'png', 'gif', 'php'], - 'hits': 171462 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_rows.js deleted file mode 100644 index 2b4ee83eca44c7..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_rows.js +++ /dev/null @@ -1,100 +0,0 @@ -import _ from 'lodash'; - -export default { - 'rows': [ - { - 'label': '0.0-1000.0: bytes', - 'xAxisLabel': 'Top 5 extension', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 3378 - }, - { - 'x': 'css', - 'y': 762 - }, - { - 'x': 'png', - 'y': 527 - }, - { - 'x': 'gif', - 'y': 11258 - }, - { - 'x': 'php', - 'y': 653 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - }, - { - 'label': '1000.0-2000.0: bytes', - 'xAxisLabel': 'Top 5 extension', - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 6422 - }, - { - 'x': 'css', - 'y': 1591 - }, - { - 'x': 'png', - 'y': 430 - }, - { - 'x': 'gif', - 'y': 8 - }, - { - 'x': 'php', - 'y': 561 - } - ] - } - ], - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } - } - ], - 'xAxisOrderedValues': ['jpg', 'css', 'png', 'gif', 'php'], - 'hits': 171458 -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_series.js deleted file mode 100644 index f717012d430cf3..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_series.js +++ /dev/null @@ -1,50 +0,0 @@ -import _ from 'lodash'; - -export default { - 'label': '', - 'xAxisLabel': 'Top 5 extension', - 'xAxisOrderedValues': ['jpg', 'css', 'png', 'gif', 'php'], - 'yAxisLabel': 'Count of documents', - 'series': [ - { - 'label': 'Count', - 'values': [ - { - 'x': 'jpg', - 'y': 110710 - }, - { - 'x': 'css', - 'y': 27389 - }, - { - 'x': 'png', - 'y': 16661 - }, - { - 'x': 'gif', - 'y': 11269 - }, - { - 'x': 'php', - 'y': 5447 - } - ] - } - ], - 'hits': 171476, - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js deleted file mode 100644 index 2b86663ab4673e..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js +++ /dev/null @@ -1,72 +0,0 @@ -import _ from 'lodash'; - -export default { - 'xAxisOrderedValues': ['_all'], - 'yAxisLabel': 'Count', - 'zAxisLabel': 'machine.os.raw: Descending', - 'yScale': null, - 'series': [{ - 'label': 'ios', - 'id': '1', - 'yAxisFormatter': _.identity, - 'values': [{ - 'x': '_all', - 'y': 2820, - 'series': 'ios' - }] - }, { - 'label': 'win 7', - 'aggId': '1', - 'yAxisFormatter': _.identity, - 'values': [{ - 'x': '_all', - 'y': 2319, - 'series': 'win 7' - }] - }, { - 'label': 'win 8', - 'id': '1', - 'yAxisFormatter': _.identity, - 'values': [{ - 'x': '_all', - 'y': 1835, - 'series': 'win 8' - }] - }, { - 'label': 'windows xp service pack 2 version 20123452', - 'id': '1', - 'yAxisFormatter': _.identity, - 'values': [{ - 'x': '_all', - 'y': 734, - 'series': 'win xp' - }] - }, { - 'label': 'osx', - 'id': '1', - 'yAxisFormatter': _.identity, - 'values': [{ - 'x': '_all', - 'y': 1352, - 'series': 'osx' - }] - }], - 'hits': 14005, - 'xAxisFormatter': function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } - }, - 'yAxisFormatter': function (val) { - return val; - }, - 'tooltipFormatter': function (d) { - return d; - } -}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js deleted file mode 100644 index 03646d08298dd6..00000000000000 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js +++ /dev/null @@ -1,128 +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 percentileTestdata from './testdata_linechart_percentile.json'; -import percentileTestdataResult from './testdata_linechart_percentile_result.json'; - -import { vislibPointSeriesTypes as pointSeriesConfig } from '../../../lib/types/point_series'; - -describe('Point Series Config Type Class Test Suite', function() { - let parsedConfig; - const histogramConfig = { - type: 'histogram', - addLegend: true, - tooltip: { - show: true, - }, - categoryAxes: [ - { - id: 'CategoryAxis-1', - type: 'category', - title: {}, - }, - ], - valueAxes: [ - { - id: 'ValueAxis-1', - type: 'value', - labels: {}, - title: {}, - }, - ], - }; - - const data = { - get: prop => { - return data[prop] || data.data[prop] || null; - }, - getLabels: () => [], - data: { - hits: 621, - ordered: { - date: true, - interval: 30000, - max: 1408734982458, - min: 1408734082458, - }, - series: [ - { label: 's1', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's2', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's3', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's4', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's5', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's6', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's7', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's8', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's9', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's10', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's11', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's12', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's13', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's14', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's15', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's16', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's17', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's18', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's19', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's20', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's21', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's22', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's23', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's24', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's25', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's26', values: [{ x: 1408734060000, y: 8 }] }, - ], - xAxisLabel: 'Date Histogram', - yAxisLabel: 'series', - yAxisFormatter: () => 'test', - }, - }; - - describe('histogram chart', function() { - beforeEach(function() { - parsedConfig = pointSeriesConfig.column(histogramConfig, data); - }); - it('should not throw an error when more than 25 series are provided', function() { - expect(parsedConfig.error).to.be.undefined; - }); - - it('should set axis title and formatter from data', () => { - expect(parsedConfig.categoryAxes[0].title.text).to.equal(data.data.xAxisLabel); - expect(parsedConfig.valueAxes[0].labels.axisFormatter).to.not.be.undefined; - }); - }); - - describe('line chart', function() { - beforeEach(function() { - const percentileDataObj = { - get: prop => { - return data[prop] || data.data[prop] || null; - }, - getLabels: () => [], - data: percentileTestdata.data, - }; - parsedConfig = pointSeriesConfig.line(percentileTestdata.cfg, percentileDataObj); - }); - it('should render a percentile line chart', function() { - expect(JSON.stringify(parsedConfig)).to.eql(JSON.stringify(percentileTestdataResult)); - }); - }); -}); diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 0d2f3528c90190..02491ff8729812 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -153,6 +153,7 @@ export default class KbnServer { public server: Server; public inject: Server['inject']; public pluginSpecs: any[]; + public uiBundles: any; constructor( settings: Record, diff --git a/src/legacy/ui/public/angular_ui_select.js b/src/legacy/ui/public/angular_ui_select.js index 92b1bf53520ce3..99f92587507c92 100644 --- a/src/legacy/ui/public/angular_ui_select.js +++ b/src/legacy/ui/public/angular_ui_select.js @@ -19,6 +19,7 @@ import 'jquery'; import 'angular'; +// required for `ngSanitize` angular module import 'angular-sanitize'; import 'ui-select/dist/select'; diff --git a/src/legacy/ui/public/i18n/index.test.tsx b/src/legacy/ui/public/i18n/index.test.tsx index c7a778ac18bd37..be8ab4cf8d6962 100644 --- a/src/legacy/ui/public/i18n/index.test.tsx +++ b/src/legacy/ui/public/i18n/index.test.tsx @@ -21,6 +21,7 @@ import { render } from 'enzyme'; import PropTypes from 'prop-types'; import React from 'react'; +jest.mock('angular-sanitize', () => {}); jest.mock('ui/new_platform', () => ({ npStart: { core: { diff --git a/src/legacy/ui/public/i18n/index.tsx b/src/legacy/ui/public/i18n/index.tsx index c918554563fcb7..6f1120dce0c7cf 100644 --- a/src/legacy/ui/public/i18n/index.tsx +++ b/src/legacy/ui/public/i18n/index.tsx @@ -18,6 +18,8 @@ */ import React from 'react'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; // @ts-ignore diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 8a71c6ccb15064..b60442690af3c3 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -29,6 +29,7 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { document.body.innerHTML = err.outerHTML; } + var stylesheetTarget = document.querySelector('head meta[name="add-styles-here"]') function loadStyleSheet(url, cb) { var dom = document.createElement('link'); dom.rel = 'stylesheet'; @@ -36,9 +37,10 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { dom.href = url; dom.addEventListener('error', failure); dom.addEventListener('load', cb); - document.head.appendChild(dom); + document.head.insertBefore(dom, stylesheetTarget); } + var scriptsTarget = document.querySelector('head meta[name="add-scripts-here"]') function loadScript(url, cb) { var dom = document.createElement('script'); {{!-- NOTE: async = false is used to trigger async-download/ordered-execution as outlined here: https://www.html5rocks.com/en/tutorials/speed/script-loading/ --}} @@ -46,7 +48,7 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { dom.src = url; dom.addEventListener('error', failure); dom.addEventListener('load', cb); - document.head.appendChild(dom); + document.head.insertBefore(dom, scriptsTarget); } function load(urls, cb) { diff --git a/src/legacy/ui/ui_render/ui_render_mixin.js b/src/legacy/ui/ui_render/ui_render_mixin.js index 801eecf5b608bd..9b44395fa9c68a 100644 --- a/src/legacy/ui/ui_render/ui_render_mixin.js +++ b/src/legacy/ui/ui_render/ui_render_mixin.js @@ -19,13 +19,15 @@ import { createHash } from 'crypto'; import Boom from 'boom'; -import { resolve } from 'path'; +import Path from 'path'; import { i18n } from '@kbn/i18n'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; import { AppBootstrap } from './bootstrap'; import { getApmConfig } from '../apm'; import { DllCompiler } from '../../../optimize/dynamic_dll_plugin'; +const uniq = (...items) => Array.from(new Set(items)); + /** * @typedef {import('../../server/kbn_server').default} KbnServer * @typedef {import('../../server/kbn_server').ResponseToolkit} ResponseToolkit @@ -39,7 +41,7 @@ import { DllCompiler } from '../../../optimize/dynamic_dll_plugin'; */ export function uiRenderMixin(kbnServer, server, config) { // render all views from ./views - server.setupViews(resolve(__dirname, 'views')); + server.setupViews(Path.resolve(__dirname, 'views')); const translationsCache = { translations: null, hash: null }; server.route({ @@ -94,9 +96,12 @@ export function uiRenderMixin(kbnServer, server, config) { ? await uiSettings.get('theme:darkMode') : false; + const buildHash = server.newPlatform.env.packageInfo.buildNum; const basePath = config.get('server.basePath'); - const regularBundlePath = `${basePath}/bundles`; - const dllBundlePath = `${basePath}/built_assets/dlls`; + + const regularBundlePath = `${basePath}/${buildHash}/bundles`; + const dllBundlePath = `${basePath}/${buildHash}/built_assets/dlls`; + const dllStyleChunks = DllCompiler.getRawDllConfig().chunks.map( chunk => `${dllBundlePath}/vendors${chunk}.style.dll.css` ); @@ -106,15 +111,15 @@ export function uiRenderMixin(kbnServer, server, config) { const styleSheetPaths = [ ...(isCore ? [] : dllStyleChunks), - `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, ...(darkMode ? [ - `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`, + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.darkCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_dark.css`, `${regularBundlePath}/dark_theme.style.css`, ] : [ - `${basePath}/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, + `${regularBundlePath}/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, `${basePath}/node_modules/@kbn/ui-framework/dist/kui_light.css`, `${regularBundlePath}/light_theme.style.css`, ]), @@ -129,13 +134,23 @@ export function uiRenderMixin(kbnServer, server, config) { ) .map(path => path.localPath.endsWith('.scss') - ? `${basePath}/built_assets/css/${path.publicPath}` + ? `${basePath}/${buildHash}/built_assets/css/${path.publicPath}` : `${basePath}/${path.publicPath}` ) .reverse(), ]), ]; + const kpPluginIds = uniq( + // load these plugins first, they are "shared" and other bundles access their + // public/index exports without considering topographic sorting by plugin deps (for now) + 'kibanaUtils', + 'kibanaReact', + 'data', + 'esUiShared', + ...kbnServer.newPlatform.__internals.uiPlugins.public.keys() + ); + const jsDependencyPaths = [ ...UiSharedDeps.jsDepFilenames.map( filename => `${regularBundlePath}/kbn-ui-shared-deps/${filename}` @@ -148,20 +163,22 @@ export function uiRenderMixin(kbnServer, server, config) { ...dllJsChunks, `${regularBundlePath}/commons.bundle.js`, ]), - `${regularBundlePath}/plugin/kibanaUtils/kibanaUtils.plugin.js`, - `${regularBundlePath}/plugin/esUiShared/esUiShared.plugin.js`, - `${regularBundlePath}/plugin/kibanaReact/kibanaReact.plugin.js`, - ]; - const uiPluginIds = [...kbnServer.newPlatform.__internals.uiPlugins.public.keys()]; + ...kpPluginIds.map( + pluginId => `${regularBundlePath}/plugin/${pluginId}/${pluginId}.plugin.js` + ), + ]; // These paths should align with the bundle routes configured in - // src/optimize/bundles_route/bundles_route.js + // src/optimize/bundles_route/bundles_route.ts const publicPathMap = JSON.stringify({ core: `${regularBundlePath}/core/`, 'kbn-ui-shared-deps': `${regularBundlePath}/kbn-ui-shared-deps/`, - ...uiPluginIds.reduce( - (acc, pluginId) => ({ ...acc, [pluginId]: `${regularBundlePath}/plugin/${pluginId}/` }), + ...kpPluginIds.reduce( + (acc, pluginId) => ({ + ...acc, + [pluginId]: `${regularBundlePath}/plugin/${pluginId}/`, + }), {} ), }); diff --git a/src/optimize/bundles_route/__tests__/bundles_route.js b/src/optimize/bundles_route/__tests__/bundles_route.js index 0b2aeda11fb0e4..902fa59b205698 100644 --- a/src/optimize/bundles_route/__tests__/bundles_route.js +++ b/src/optimize/bundles_route/__tests__/bundles_route.js @@ -32,6 +32,7 @@ import { PUBLIC_PATH_PLACEHOLDER } from '../../public_path_placeholder'; const chance = new Chance(); const outputFixture = resolve(__dirname, './fixtures/output'); +const pluginNoPlaceholderFixture = resolve(__dirname, './fixtures/plugin/no_placeholder'); const randomWordsCache = new Set(); const uniqueRandomWord = () => { @@ -58,6 +59,9 @@ describe('optimizer/bundle route', () => { dllBundlesPath = outputFixture, basePublicPath = '', builtCssPath = outputFixture, + npUiPluginPublicDirs = [], + buildHash = '1234', + isDist = false, } = options; const server = new Hapi.Server(); @@ -69,6 +73,9 @@ describe('optimizer/bundle route', () => { dllBundlesPath, basePublicPath, builtCssPath, + npUiPluginPublicDirs, + buildHash, + isDist, }) ); @@ -158,7 +165,7 @@ describe('optimizer/bundle route', () => { it('responds with exact file data', async () => { const server = createServer(); const response = await server.inject({ - url: '/bundles/image.png', + url: '/1234/bundles/image.png', }); expect(response.statusCode).to.be(200); @@ -173,7 +180,7 @@ describe('optimizer/bundle route', () => { it('responds with no content-length and exact file data', async () => { const server = createServer(); const response = await server.inject({ - url: '/bundles/no_placeholder.js', + url: '/1234/bundles/no_placeholder.js', }); expect(response.statusCode).to.be(200); @@ -187,12 +194,12 @@ describe('optimizer/bundle route', () => { }); describe('js file with placeholder', () => { - it('responds with no content-length and modified file data', async () => { + it('responds with no content-length and modifiedfile data ', async () => { const basePublicPath = `/${uniqueRandomWord()}`; const server = createServer({ basePublicPath }); const response = await server.inject({ - url: '/bundles/with_placeholder.js', + url: '/1234/bundles/with_placeholder.js', }); expect(response.statusCode).to.be(200); @@ -204,7 +211,7 @@ describe('optimizer/bundle route', () => { ); expect(response.result.indexOf(source)).to.be(-1); expect(response.result).to.be( - replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/bundles/`) + replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/1234/bundles/`) ); }); }); @@ -213,7 +220,7 @@ describe('optimizer/bundle route', () => { it('responds with no content-length and exact file data', async () => { const server = createServer(); const response = await server.inject({ - url: '/bundles/no_placeholder.css', + url: '/1234/bundles/no_placeholder.css', }); expect(response.statusCode).to.be(200); @@ -231,7 +238,7 @@ describe('optimizer/bundle route', () => { const server = createServer({ basePublicPath }); const response = await server.inject({ - url: '/bundles/with_placeholder.css', + url: '/1234/bundles/with_placeholder.css', }); expect(response.statusCode).to.be(200); @@ -240,7 +247,7 @@ describe('optimizer/bundle route', () => { expect(response.headers).to.have.property('content-type', 'text/css; charset=utf-8'); expect(response.result.indexOf(source)).to.be(-1); expect(response.result).to.be( - replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/bundles/`) + replaceAll(source, PUBLIC_PATH_PLACEHOLDER, `${basePublicPath}/1234/bundles/`) ); }); }); @@ -250,7 +257,7 @@ describe('optimizer/bundle route', () => { const server = createServer(); const response = await server.inject({ - url: '/bundles/../outside_output.js', + url: '/1234/bundles/../outside_output.js', }); expect(response.statusCode).to.be(404); @@ -267,7 +274,7 @@ describe('optimizer/bundle route', () => { const server = createServer(); const response = await server.inject({ - url: '/bundles/non_existent.js', + url: '/1234/bundles/non_existent.js', }); expect(response.statusCode).to.be(404); @@ -286,7 +293,7 @@ describe('optimizer/bundle route', () => { }); const response = await server.inject({ - url: '/bundles/with_placeholder.js', + url: '/1234/bundles/with_placeholder.js', }); expect(response.statusCode).to.be(404); @@ -306,7 +313,7 @@ describe('optimizer/bundle route', () => { sinon.assert.notCalled(createHash); const resp1 = await server.inject({ - url: '/bundles/no_placeholder.js', + url: '/1234/bundles/no_placeholder.js', }); sinon.assert.calledOnce(createHash); @@ -314,23 +321,23 @@ describe('optimizer/bundle route', () => { expect(resp1.statusCode).to.be(200); const resp2 = await server.inject({ - url: '/bundles/no_placeholder.js', + url: '/1234/bundles/no_placeholder.js', }); sinon.assert.notCalled(createHash); expect(resp2.statusCode).to.be(200); }); - it('is unique per basePublicPath although content is the same', async () => { + it('is unique per basePublicPath although content is the same (by default)', async () => { const basePublicPath1 = `/${uniqueRandomWord()}`; const basePublicPath2 = `/${uniqueRandomWord()}`; const [resp1, resp2] = await Promise.all([ createServer({ basePublicPath: basePublicPath1 }).inject({ - url: '/bundles/no_placeholder.js', + url: '/1234/bundles/no_placeholder.js', }), createServer({ basePublicPath: basePublicPath2 }).inject({ - url: '/bundles/no_placeholder.js', + url: '/1234/bundles/no_placeholder.js', }), ]); @@ -349,13 +356,13 @@ describe('optimizer/bundle route', () => { it('responds with 304 when etag and last modified are sent back', async () => { const server = createServer(); const resp = await server.inject({ - url: '/bundles/with_placeholder.js', + url: '/1234/bundles/with_placeholder.js', }); expect(resp.statusCode).to.be(200); const resp2 = await server.inject({ - url: '/bundles/with_placeholder.js', + url: '/1234/bundles/with_placeholder.js', headers: { 'if-modified-since': resp.headers['last-modified'], 'if-none-match': resp.headers.etag, @@ -366,4 +373,80 @@ describe('optimizer/bundle route', () => { expect(resp2.result).to.have.length(0); }); }); + + describe('kibana platform assets', () => { + describe('caching', () => { + describe('for non-distributable mode', () => { + it('uses "etag" header to invalidate cache', async () => { + const basePublicPath = `/${uniqueRandomWord()}`; + + const npUiPluginPublicDirs = [ + { + id: 'no_placeholder', + path: pluginNoPlaceholderFixture, + }, + ]; + const responce = await createServer({ basePublicPath, npUiPluginPublicDirs }).inject({ + url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js', + }); + + expect(responce.statusCode).to.be(200); + + expect(responce.headers.etag).to.be.a('string'); + expect(responce.headers['cache-control']).to.be('must-revalidate'); + }); + + it('creates the same "etag" header for the same content with the same basePath', async () => { + const npUiPluginPublicDirs = [ + { + id: 'no_placeholder', + path: pluginNoPlaceholderFixture, + }, + ]; + const [resp1, resp2] = await Promise.all([ + createServer({ basePublicPath: '', npUiPluginPublicDirs }).inject({ + url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js', + }), + createServer({ basePublicPath: '', npUiPluginPublicDirs }).inject({ + url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js', + }), + ]); + + expect(resp1.statusCode).to.be(200); + expect(resp2.statusCode).to.be(200); + + expect(resp1.rawPayload).to.eql(resp2.rawPayload); + + expect(resp1.headers.etag).to.be.a('string'); + expect(resp2.headers.etag).to.be.a('string'); + expect(resp1.headers.etag).to.eql(resp2.headers.etag); + }); + }); + + describe('for distributable mode', () => { + it('commands to cache assets for each release for a year', async () => { + const basePublicPath = `/${uniqueRandomWord()}`; + + const npUiPluginPublicDirs = [ + { + id: 'no_placeholder', + path: pluginNoPlaceholderFixture, + }, + ]; + const responce = await createServer({ + basePublicPath, + npUiPluginPublicDirs, + isDist: true, + }).inject({ + url: '/1234/bundles/plugin/no_placeholder/no_placeholder.plugin.js', + }); + + expect(responce.statusCode).to.be(200); + + expect(responce.headers.etag).to.be(undefined); + expect(responce.headers['cache-control']).to.be('max-age=31536000'); + }); + }); + }); + }); }); diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.ts similarity index 74% rename from src/optimize/bundles_route/bundles_route.js rename to src/optimize/bundles_route/bundles_route.ts index 4030988c8552c7..e9cfba0130d951 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.ts @@ -18,10 +18,13 @@ */ import { isAbsolute, extname, join } from 'path'; -import LruCache from 'lru-cache'; + +import Hapi from 'hapi'; import * as UiSharedDeps from '@kbn/ui-shared-deps'; + import { createDynamicAssetResponse } from './dynamic_asset_response'; -import { assertIsNpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs'; +import { FileHashCache } from './file_hash_cache'; +import { assertIsNpUiPluginPublicDirs, NpUiPluginPublicDirs } from '../np_ui_plugin_public_dirs'; import { fromRoot } from '../../core/server/utils'; /** @@ -44,11 +47,21 @@ export function createBundlesRoute({ basePublicPath, builtCssPath, npUiPluginPublicDirs = [], + buildHash, + isDist = false, +}: { + regularBundlesPath: string; + dllBundlesPath: string; + basePublicPath: string; + builtCssPath: string; + npUiPluginPublicDirs?: NpUiPluginPublicDirs; + buildHash: string; + isDist?: boolean; }) { // rather than calculate the fileHash on every request, we // provide a cache object to `resolveDynamicAssetResponse()` that // will store the 100 most recently used hashes. - const fileHashCache = new LruCache(100); + const fileHashCache = new FileHashCache(); assertIsNpUiPluginPublicDirs(npUiPluginPublicDirs); if (typeof regularBundlesPath !== 'string' || !isAbsolute(regularBundlesPath)) { @@ -73,45 +86,51 @@ export function createBundlesRoute({ return [ buildRouteForBundles({ - publicPath: `${basePublicPath}/bundles/kbn-ui-shared-deps/`, - routePath: '/bundles/kbn-ui-shared-deps/', + publicPath: `${basePublicPath}/${buildHash}/bundles/kbn-ui-shared-deps/`, + routePath: `/${buildHash}/bundles/kbn-ui-shared-deps/`, bundlesPath: UiSharedDeps.distDir, fileHashCache, replacePublicPath: false, + isDist, }), ...npUiPluginPublicDirs.map(({ id, path }) => buildRouteForBundles({ - publicPath: `${basePublicPath}/bundles/plugin/${id}/`, - routePath: `/bundles/plugin/${id}/`, + publicPath: `${basePublicPath}/${buildHash}/bundles/plugin/${id}/`, + routePath: `/${buildHash}/bundles/plugin/${id}/`, bundlesPath: path, fileHashCache, replacePublicPath: false, + isDist, }) ), buildRouteForBundles({ - publicPath: `${basePublicPath}/bundles/core/`, - routePath: `/bundles/core/`, + publicPath: `${basePublicPath}/${buildHash}/bundles/core/`, + routePath: `/${buildHash}/bundles/core/`, bundlesPath: fromRoot(join('src', 'core', 'target', 'public')), fileHashCache, replacePublicPath: false, + isDist, }), buildRouteForBundles({ - publicPath: `${basePublicPath}/bundles/`, - routePath: '/bundles/', + publicPath: `${basePublicPath}/${buildHash}/bundles/`, + routePath: `/${buildHash}/bundles/`, bundlesPath: regularBundlesPath, fileHashCache, + isDist, }), buildRouteForBundles({ - publicPath: `${basePublicPath}/built_assets/dlls/`, - routePath: '/built_assets/dlls/', + publicPath: `${basePublicPath}/${buildHash}/built_assets/dlls/`, + routePath: `/${buildHash}/built_assets/dlls/`, bundlesPath: dllBundlesPath, fileHashCache, + isDist, }), buildRouteForBundles({ publicPath: `${basePublicPath}/`, - routePath: '/built_assets/css/', + routePath: `/${buildHash}/built_assets/css/`, bundlesPath: builtCssPath, fileHashCache, + isDist, }), ]; } @@ -122,6 +141,14 @@ function buildRouteForBundles({ bundlesPath, fileHashCache, replacePublicPath = true, + isDist, +}: { + publicPath: string; + routePath: string; + bundlesPath: string; + fileHashCache: FileHashCache; + replacePublicPath?: boolean; + isDist: boolean; }) { return { method: 'GET', @@ -130,7 +157,7 @@ function buildRouteForBundles({ auth: false, ext: { onPreHandler: { - method(request, h) { + method(request: Hapi.Request, h: Hapi.ResponseToolkit) { const ext = extname(request.params.path); if (ext !== '.js' && ext !== '.css') { @@ -144,6 +171,7 @@ function buildRouteForBundles({ fileHashCache, publicPath, replacePublicPath, + isDist, }); }, }, diff --git a/src/optimize/bundles_route/dynamic_asset_response.js b/src/optimize/bundles_route/dynamic_asset_response.ts similarity index 67% rename from src/optimize/bundles_route/dynamic_asset_response.js rename to src/optimize/bundles_route/dynamic_asset_response.ts index 80c49a26270fd6..a020c6935eeecf 100644 --- a/src/optimize/bundles_route/dynamic_asset_response.js +++ b/src/optimize/bundles_route/dynamic_asset_response.ts @@ -17,15 +17,26 @@ * under the License. */ +import Fs from 'fs'; import { resolve } from 'path'; -import { open, fstat, createReadStream, close } from 'fs'; +import { promisify } from 'util'; import Boom from 'boom'; -import { fromNode as fcb } from 'bluebird'; +import Hapi from 'hapi'; +import { FileHashCache } from './file_hash_cache'; import { getFileHash } from './file_hash'; +// @ts-ignore import { replacePlaceholder } from '../public_path_placeholder'; +const MINUTE = 60; +const HOUR = 60 * MINUTE; +const DAY = 24 * HOUR; + +const asyncOpen = promisify(Fs.open); +const asyncClose = promisify(Fs.close); +const asyncFstat = promisify(Fs.fstat); + /** * Create a Hapi response for the requested path. This is designed * to replicate a subset of the features provided by Hapi's Inert @@ -44,55 +55,70 @@ import { replacePlaceholder } from '../public_path_placeholder'; * - cached hash/etag is based on the file on disk, but modified * by the public path so that individual public paths have * different etags, but can share a cache - * - * @param {Object} options - * @property {Hapi.Request} options.request - * @property {string} options.bundlesPath - * @property {string} options.publicPath - * @property {LruCache} options.fileHashCache */ -export async function createDynamicAssetResponse(options) { - const { request, h, bundlesPath, publicPath, fileHashCache, replacePublicPath } = options; +export async function createDynamicAssetResponse({ + request, + h, + bundlesPath, + publicPath, + fileHashCache, + replacePublicPath, + isDist, +}: { + request: Hapi.Request; + h: Hapi.ResponseToolkit; + bundlesPath: string; + publicPath: string; + fileHashCache: FileHashCache; + replacePublicPath: boolean; + isDist: boolean; +}) { + let fd: number | undefined; - let fd; try { const path = resolve(bundlesPath, request.params.path); // prevent path traversal, only process paths that resolve within bundlesPath if (!path.startsWith(bundlesPath)) { - throw Boom.forbidden(null, 'EACCES'); + throw Boom.forbidden(undefined, 'EACCES'); } // we use and manage a file descriptor mostly because // that's what Inert does, and since we are accessing // the file 2 or 3 times per request it seems logical - fd = await fcb(cb => open(path, 'r', cb)); + fd = await asyncOpen(path, 'r'); - const stat = await fcb(cb => fstat(fd, cb)); - const hash = await getFileHash(fileHashCache, path, stat, fd); + const stat = await asyncFstat(fd); + const hash = isDist ? undefined : await getFileHash(fileHashCache, path, stat, fd); - const read = createReadStream(null, { + const read = Fs.createReadStream(null as any, { fd, start: 0, autoClose: true, }); - fd = null; // read stream is now responsible for fd + fd = undefined; // read stream is now responsible for fd const content = replacePublicPath ? replacePlaceholder(read, publicPath) : read; - const etag = replacePublicPath ? `${hash}-${publicPath}` : hash; - return h + const response = h .response(content) .takeover() .code(200) - .etag(etag) - .header('cache-control', 'must-revalidate') .type(request.server.mime.path(path).type); + + if (isDist) { + response.header('cache-control', `max-age=${365 * DAY}`); + } else { + response.etag(`${hash}-${publicPath}`); + response.header('cache-control', 'must-revalidate'); + } + + return response; } catch (error) { if (fd) { try { - await fcb(cb => close(fd, cb)); - } catch (error) { + await asyncClose(fd); + } catch (_) { // ignore errors from close, we already have one to report // and it's very likely they are the same } diff --git a/src/optimize/bundles_route/file_hash.js b/src/optimize/bundles_route/file_hash.ts similarity index 73% rename from src/optimize/bundles_route/file_hash.js rename to src/optimize/bundles_route/file_hash.ts index d9464cf05eca17..7b0801098ed10b 100644 --- a/src/optimize/bundles_route/file_hash.js +++ b/src/optimize/bundles_route/file_hash.ts @@ -18,20 +18,17 @@ */ import { createHash } from 'crypto'; -import { createReadStream } from 'fs'; +import Fs from 'fs'; import * as Rx from 'rxjs'; -import { merge, mergeMap, takeUntil } from 'rxjs/operators'; +import { takeUntil, map } from 'rxjs/operators'; + +import { FileHashCache } from './file_hash_cache'; /** * Get the hash of a file via a file descriptor - * @param {LruCache} cache - * @param {string} path - * @param {Fs.Stat} stat - * @param {Fs.FileDescriptor} fd - * @return {Promise} */ -export async function getFileHash(cache, path, stat, fd) { +export async function getFileHash(cache: FileHashCache, path: string, stat: Fs.Stats, fd: number) { const key = `${path}:${stat.ino}:${stat.size}:${stat.mtime.getTime()}`; const cached = cache.get(key); @@ -40,17 +37,21 @@ export async function getFileHash(cache, path, stat, fd) { } const hash = createHash('sha1'); - const read = createReadStream(null, { + const read = Fs.createReadStream(null as any, { fd, start: 0, autoClose: false, }); - const promise = Rx.fromEvent(read, 'data') - .pipe( - merge(Rx.fromEvent(read, 'error').pipe(mergeMap(Rx.throwError))), - takeUntil(Rx.fromEvent(read, 'end')) + const promise = Rx.merge( + Rx.fromEvent(read, 'data'), + Rx.fromEvent(read, 'error').pipe( + map(error => { + throw error; + }) ) + ) + .pipe(takeUntil(Rx.fromEvent(read, 'end'))) .forEach(chunk => hash.update(chunk)) .then(() => hash.digest('hex')) .catch(error => { diff --git a/src/legacy/core_plugins/region_map/public/shim/index.ts b/src/optimize/bundles_route/file_hash_cache.ts similarity index 72% rename from src/legacy/core_plugins/region_map/public/shim/index.ts rename to src/optimize/bundles_route/file_hash_cache.ts index cfc7b62ff4f86d..a7cdabbff13a7b 100644 --- a/src/legacy/core_plugins/region_map/public/shim/index.ts +++ b/src/optimize/bundles_route/file_hash_cache.ts @@ -17,4 +17,20 @@ * under the License. */ -export * from './legacy_dependencies_plugin'; +import LruCache from 'lru-cache'; + +export class FileHashCache { + private lru = new LruCache>(100); + + get(key: string) { + return this.lru.get(key); + } + + set(key: string, value: Promise) { + this.lru.set(key, value); + } + + del(key: string) { + this.lru.del(key); + } +} diff --git a/src/optimize/bundles_route/index.js b/src/optimize/bundles_route/index.ts similarity index 100% rename from src/optimize/bundles_route/index.js rename to src/optimize/bundles_route/index.ts diff --git a/src/optimize/bundles_route/proxy_bundles_route.js b/src/optimize/bundles_route/proxy_bundles_route.ts similarity index 70% rename from src/optimize/bundles_route/proxy_bundles_route.js rename to src/optimize/bundles_route/proxy_bundles_route.ts index fff0ec444d95ba..1d189054324a1c 100644 --- a/src/optimize/bundles_route/proxy_bundles_route.js +++ b/src/optimize/bundles_route/proxy_bundles_route.ts @@ -17,15 +17,23 @@ * under the License. */ -export function createProxyBundlesRoute({ host, port }) { +export function createProxyBundlesRoute({ + host, + port, + buildHash, +}: { + host: string; + port: number; + buildHash: string; +}) { return [ - buildProxyRouteForBundles('/bundles/', host, port), - buildProxyRouteForBundles('/built_assets/dlls/', host, port), - buildProxyRouteForBundles('/built_assets/css/', host, port), + buildProxyRouteForBundles(`/${buildHash}/bundles/`, host, port), + buildProxyRouteForBundles(`/${buildHash}/built_assets/dlls/`, host, port), + buildProxyRouteForBundles(`/${buildHash}/built_assets/css/`, host, port), ]; } -function buildProxyRouteForBundles(routePath, host, port) { +function buildProxyRouteForBundles(routePath: string, host: string, port: number) { return { path: `${routePath}{path*}`, method: 'GET', diff --git a/src/optimize/index.js b/src/optimize/index.js index b7b9f7712358a6..363f81a6a3a967 100644 --- a/src/optimize/index.js +++ b/src/optimize/index.js @@ -17,72 +17,5 @@ * under the License. */ -import FsOptimizer from './fs_optimizer'; -import { createBundlesRoute } from './bundles_route'; -import { DllCompiler } from './dynamic_dll_plugin'; -import { fromRoot } from '../core/server/utils'; -import { getNpUiPluginPublicDirs } from './np_ui_plugin_public_dirs'; - -export default async (kbnServer, server, config) => { - if (!config.get('optimize.enabled')) return; - - // the watch optimizer sets up two threads, one is the server listening - // on 5601 and the other is a server listening on 5602 that builds the - // bundles in a "middleware" style. - // - // the server listening on 5601 may be restarted a number of times, depending - // on the watch setup managed by the cli. It proxies all bundles/* and built_assets/dlls/* - // requests to the other server. The server on 5602 is long running, in order - // to prevent complete rebuilds of the optimize content. - const watch = config.get('optimize.watch'); - if (watch) { - return await kbnServer.mixin(require('./watch/watch')); - } - - const { uiBundles } = kbnServer; - server.route( - createBundlesRoute({ - regularBundlesPath: uiBundles.getWorkingDir(), - dllBundlesPath: DllCompiler.getRawDllConfig().outputPath, - basePublicPath: config.get('server.basePath'), - builtCssPath: fromRoot('built_assets/css'), - npUiPluginPublicDirs: getNpUiPluginPublicDirs(kbnServer), - }) - ); - - // in prod, only bundle when something is missing or invalid - const reuseCache = config.get('optimize.useBundleCache') - ? await uiBundles.areAllBundleCachesValid() - : false; - - // we might not have any work to do - if (reuseCache) { - server.log(['debug', 'optimize'], `All bundles are cached and ready to go!`); - return; - } - - await uiBundles.resetBundleDir(); - - // only require the FsOptimizer when we need to - const optimizer = new FsOptimizer({ - logWithMetadata: (tags, message, metadata) => server.logWithMetadata(tags, message, metadata), - uiBundles, - profile: config.get('optimize.profile'), - sourceMaps: config.get('optimize.sourceMaps'), - workers: config.get('optimize.workers'), - }); - - server.log( - ['info', 'optimize'], - `Optimizing and caching ${uiBundles.getDescription()}. This may take a few minutes` - ); - - const start = Date.now(); - await optimizer.run(); - const seconds = ((Date.now() - start) / 1000).toFixed(2); - - server.log( - ['info', 'optimize'], - `Optimization of ${uiBundles.getDescription()} complete in ${seconds} seconds` - ); -}; +import { optimizeMixin } from './optimize_mixin'; +export default optimizeMixin; diff --git a/src/optimize/np_ui_plugin_public_dirs.js b/src/optimize/np_ui_plugin_public_dirs.ts similarity index 73% rename from src/optimize/np_ui_plugin_public_dirs.js rename to src/optimize/np_ui_plugin_public_dirs.ts index de05fd2b863b88..e7c3207948f6a3 100644 --- a/src/optimize/np_ui_plugin_public_dirs.js +++ b/src/optimize/np_ui_plugin_public_dirs.ts @@ -16,8 +16,14 @@ * specific language governing permissions and limitations * under the License. */ +import KbnServer from '../legacy/server/kbn_server'; -export function getNpUiPluginPublicDirs(kbnServer) { +export type NpUiPluginPublicDirs = Array<{ + id: string; + path: string; +}>; + +export function getNpUiPluginPublicDirs(kbnServer: KbnServer): NpUiPluginPublicDirs { return Array.from(kbnServer.newPlatform.__internals.uiPlugins.internal.entries()).map( ([id, { publicTargetDir }]) => ({ id, @@ -26,17 +32,17 @@ export function getNpUiPluginPublicDirs(kbnServer) { ); } -export function isNpUiPluginPublicDirs(something) { +export function isNpUiPluginPublicDirs(x: any): x is NpUiPluginPublicDirs { return ( - Array.isArray(something) && - something.every( + Array.isArray(x) && + x.every( s => typeof s === 'object' && s && typeof s.id === 'string' && typeof s.path === 'string' ) ); } -export function assertIsNpUiPluginPublicDirs(something) { - if (!isNpUiPluginPublicDirs(something)) { +export function assertIsNpUiPluginPublicDirs(x: any): asserts x is NpUiPluginPublicDirs { + if (!isNpUiPluginPublicDirs(x)) { throw new TypeError( 'npUiPluginPublicDirs must be an array of objects with string `id` and `path` properties' ); diff --git a/src/optimize/optimize_mixin.ts b/src/optimize/optimize_mixin.ts new file mode 100644 index 00000000000000..9a3f08e2f667eb --- /dev/null +++ b/src/optimize/optimize_mixin.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 Hapi from 'hapi'; + +// @ts-ignore not TS yet +import FsOptimizer from './fs_optimizer'; +import { createBundlesRoute } from './bundles_route'; +// @ts-ignore not TS yet +import { DllCompiler } from './dynamic_dll_plugin'; +import { fromRoot } from '../core/server/utils'; +import { getNpUiPluginPublicDirs } from './np_ui_plugin_public_dirs'; +import KbnServer, { KibanaConfig } from '../legacy/server/kbn_server'; + +export const optimizeMixin = async ( + kbnServer: KbnServer, + server: Hapi.Server, + config: KibanaConfig +) => { + if (!config.get('optimize.enabled')) return; + + // the watch optimizer sets up two threads, one is the server listening + // on 5601 and the other is a server listening on 5602 that builds the + // bundles in a "middleware" style. + // + // the server listening on 5601 may be restarted a number of times, depending + // on the watch setup managed by the cli. It proxies all bundles/* and built_assets/dlls/* + // requests to the other server. The server on 5602 is long running, in order + // to prevent complete rebuilds of the optimize content. + const watch = config.get('optimize.watch'); + if (watch) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return await kbnServer.mixin(require('./watch/watch')); + } + + const { uiBundles } = kbnServer; + server.route( + createBundlesRoute({ + regularBundlesPath: uiBundles.getWorkingDir(), + dllBundlesPath: DllCompiler.getRawDllConfig().outputPath, + basePublicPath: config.get('server.basePath'), + builtCssPath: fromRoot('built_assets/css'), + npUiPluginPublicDirs: getNpUiPluginPublicDirs(kbnServer), + buildHash: kbnServer.newPlatform.env.packageInfo.buildNum.toString(), + isDist: kbnServer.newPlatform.env.packageInfo.dist, + }) + ); + + // in prod, only bundle when something is missing or invalid + const reuseCache = config.get('optimize.useBundleCache') + ? await uiBundles.areAllBundleCachesValid() + : false; + + // we might not have any work to do + if (reuseCache) { + server.log(['debug', 'optimize'], `All bundles are cached and ready to go!`); + return; + } + + await uiBundles.resetBundleDir(); + + // only require the FsOptimizer when we need to + const optimizer = new FsOptimizer({ + logWithMetadata: server.logWithMetadata, + uiBundles, + profile: config.get('optimize.profile'), + sourceMaps: config.get('optimize.sourceMaps'), + workers: config.get('optimize.workers'), + }); + + server.log( + ['info', 'optimize'], + `Optimizing and caching ${uiBundles.getDescription()}. This may take a few minutes` + ); + + const start = Date.now(); + await optimizer.run(); + const seconds = ((Date.now() - start) / 1000).toFixed(2); + + server.log( + ['info', 'optimize'], + `Optimization of ${uiBundles.getDescription()} complete in ${seconds} seconds` + ); +}; diff --git a/src/optimize/public_path_placeholder.js b/src/optimize/public_path_placeholder.ts similarity index 76% rename from src/optimize/public_path_placeholder.js rename to src/optimize/public_path_placeholder.ts index ef05d9e5ae704e..1ec2b4a431aa62 100644 --- a/src/optimize/public_path_placeholder.js +++ b/src/optimize/public_path_placeholder.ts @@ -17,14 +17,20 @@ * under the License. */ -import { createReplaceStream } from '../legacy/utils'; +import Stream from 'stream'; +import Fs from 'fs'; import * as Rx from 'rxjs'; import { take, takeUntil } from 'rxjs/operators'; +import { createReplaceStream } from '../legacy/utils'; export const PUBLIC_PATH_PLACEHOLDER = '__REPLACE_WITH_PUBLIC_PATH__'; -export function replacePlaceholder(read, replacement) { +interface ClosableTransform extends Stream.Transform { + close(): void; +} + +export function replacePlaceholder(read: Stream.Readable, replacement: string) { const replace = createReplaceStream(PUBLIC_PATH_PLACEHOLDER, replacement); // handle errors on the read stream by proxying them @@ -37,13 +43,15 @@ export function replacePlaceholder(read, replacement) { replace.end(); }); - replace.close = () => { - read.unpipe(); + const closableReplace: ClosableTransform = Object.assign(replace, { + close: () => { + read.unpipe(); - if (read.close) { - read.close(); - } - }; + if ('close' in read) { + (read as Fs.ReadStream).close(); + } + }, + }); - return read.pipe(replace); + return read.pipe(closableReplace); } diff --git a/src/optimize/watch/optmzr_role.js b/src/optimize/watch/optmzr_role.js index a31ef7229e5da3..1f6107996277cb 100644 --- a/src/optimize/watch/optmzr_role.js +++ b/src/optimize/watch/optmzr_role.js @@ -49,7 +49,8 @@ export default async (kbnServer, kibanaHapiServer, config) => { config.get('optimize.watchPort'), config.get('server.basePath'), watchOptimizer, - getNpUiPluginPublicDirs(kbnServer) + getNpUiPluginPublicDirs(kbnServer), + kbnServer.newPlatform.env.packageInfo.buildNum.toString() ); watchOptimizer.status$.subscribe({ diff --git a/src/optimize/watch/proxy_role.js b/src/optimize/watch/proxy_role.js index 6093658ae1a2d8..0f6f3b2d4b6226 100644 --- a/src/optimize/watch/proxy_role.js +++ b/src/optimize/watch/proxy_role.js @@ -26,6 +26,7 @@ export default (kbnServer, server, config) => { createProxyBundlesRoute({ host: config.get('optimize.watchHost'), port: config.get('optimize.watchPort'), + buildHash: kbnServer.newPlatform.env.packageInfo.buildNum.toString(), }) ); diff --git a/src/optimize/watch/watch_optimizer.js b/src/optimize/watch/watch_optimizer.js index 6c20f21c7768e9..cdff57a00c2e09 100644 --- a/src/optimize/watch/watch_optimizer.js +++ b/src/optimize/watch/watch_optimizer.js @@ -106,7 +106,7 @@ export default class WatchOptimizer extends BaseOptimizer { }); } - bindToServer(server, basePath, npUiPluginPublicDirs) { + bindToServer(server, basePath, npUiPluginPublicDirs, buildHash) { // pause all requests received while the compiler is running // and continue once an outcome is reached (aborting the request // with an error if it was a failure). @@ -118,6 +118,7 @@ export default class WatchOptimizer extends BaseOptimizer { server.route( createBundlesRoute({ npUiPluginPublicDirs: npUiPluginPublicDirs, + buildHash, regularBundlesPath: this.compiler.outputPath, dllBundlesPath: DllCompiler.getRawDllConfig().outputPath, basePublicPath: basePath, diff --git a/src/optimize/watch/watch_server.js b/src/optimize/watch/watch_server.js index 74a96dc8aea6e6..81e04a5b83956a 100644 --- a/src/optimize/watch/watch_server.js +++ b/src/optimize/watch/watch_server.js @@ -21,10 +21,11 @@ import { Server } from 'hapi'; import { registerHapiPlugins } from '../../legacy/server/http/register_hapi_plugins'; export default class WatchServer { - constructor(host, port, basePath, optimizer, npUiPluginPublicDirs) { + constructor(host, port, basePath, optimizer, npUiPluginPublicDirs, buildHash) { this.basePath = basePath; this.optimizer = optimizer; this.npUiPluginPublicDirs = npUiPluginPublicDirs; + this.buildHash = buildHash; this.server = new Server({ host: host, port: port, @@ -35,7 +36,12 @@ export default class WatchServer { async init() { await this.optimizer.init(); - this.optimizer.bindToServer(this.server, this.basePath, this.npUiPluginPublicDirs); + this.optimizer.bindToServer( + this.server, + this.basePath, + this.npUiPluginPublicDirs, + this.buildHash + ); await this.server.start(); } } diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts index a1696298117b0b..37f014d8360751 100644 --- a/src/plugins/dashboard/public/application/application.ts +++ b/src/plugins/dashboard/public/application/application.ts @@ -21,6 +21,8 @@ import './index.scss'; import { EuiIcon } from '@elastic/eui'; import angular, { IModule } from 'angular'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { AppMountContext, diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 5f6b67ee6ad202..7de054f2eaa9c8 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -141,10 +141,14 @@ export class DashboardPlugin if (share) { share.urlGenerators.registerUrlGenerator( - createDirectAccessDashboardLinkGenerator(async () => ({ - appBasePath: (await startServices)[0].application.getUrlForApp('dashboard'), - useHashedUrl: (await startServices)[0].uiSettings.get('state:storeInSessionStorage'), - })) + createDirectAccessDashboardLinkGenerator(async () => { + const [coreStart, , selfStart] = await startServices; + return { + appBasePath: coreStart.application.getUrlForApp('dashboard'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + savedDashboardLoader: selfStart.getSavedDashboardLoader(), + }; + }) ); } diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts index d48aacc1d8c1e4..248a3f991d6cbf 100644 --- a/src/plugins/dashboard/public/url_generator.test.ts +++ b/src/plugins/dashboard/public/url_generator.test.ts @@ -21,10 +21,33 @@ import { createDirectAccessDashboardLinkGenerator } from './url_generator'; import { hashedItemStore } from '../../kibana_utils/public'; // eslint-disable-next-line import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock'; -import { esFilters } from '../../data/public'; +import { esFilters, Filter } from '../../data/public'; +import { SavedObjectLoader } from '../../saved_objects/public'; const APP_BASE_PATH: string = 'xyz/app/kibana'; +const createMockDashboardLoader = ( + dashboardToFilters: { + [dashboardId: string]: () => Filter[]; + } = {} +) => { + return { + get: async (dashboardId: string) => { + return { + searchSource: { + getField: (field: string) => { + if (field === 'filter') + return dashboardToFilters[dashboardId] ? dashboardToFilters[dashboardId]() : []; + throw new Error( + `createMockDashboardLoader > searchSource > getField > ${field} is not mocked` + ); + }, + }, + }; + }, + } as SavedObjectLoader; +}; + describe('dashboard url generator', () => { beforeEach(() => { // @ts-ignore @@ -33,7 +56,11 @@ describe('dashboard url generator', () => { test('creates a link to a saved dashboard', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({}); expect(url).toMatchInlineSnapshot(`"xyz/app/kibana#/dashboard?_a=()&_g=()"`); @@ -41,7 +68,11 @@ describe('dashboard url generator', () => { test('creates a link with global time range set up', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -53,7 +84,11 @@ describe('dashboard url generator', () => { test('creates a link with filters, time range, refresh interval and query to a saved object', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -89,7 +124,11 @@ describe('dashboard url generator', () => { test('if no useHash setting is given, uses the one was start services', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: true, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -99,7 +138,11 @@ describe('dashboard url generator', () => { test('can override a false useHash ui setting', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -110,7 +153,11 @@ describe('dashboard url generator', () => { test('can override a true useHash ui setting', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: true, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -118,4 +165,150 @@ describe('dashboard url generator', () => { }); expect(url.indexOf('relative')).toBeGreaterThan(1); }); + + describe('preserving saved filters', () => { + const savedFilter1 = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'savedfilter1' }, + }; + + const savedFilter2 = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'savedfilter2' }, + }; + + const appliedFilter = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'appliedfilter' }, + }; + + test('attaches filters from destination dashboard', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + ['dashboard2']: () => [savedFilter2], + }), + }) + ); + + const urlToDashboard1 = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + }); + + expect(urlToDashboard1).toEqual(expect.stringContaining('query:savedfilter1')); + expect(urlToDashboard1).toEqual(expect.stringContaining('query:appliedfilter')); + + const urlToDashboard2 = await generator.createUrl!({ + dashboardId: 'dashboard2', + filters: [appliedFilter], + }); + + expect(urlToDashboard2).toEqual(expect.stringContaining('query:savedfilter2')); + expect(urlToDashboard2).toEqual(expect.stringContaining('query:appliedfilter')); + }); + + test("doesn't fail if can't retrieve filters from destination dashboard", async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => { + throw new Error('Not found'); + }, + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + }); + + expect(url).not.toEqual(expect.stringContaining('query:savedfilter1')); + expect(url).toEqual(expect.stringContaining('query:appliedfilter')); + }); + + test('can enforce empty filters', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [], + preserveSavedFilters: false, + }); + + expect(url).not.toEqual(expect.stringContaining('query:savedfilter1')); + expect(url).not.toEqual(expect.stringContaining('query:appliedfilter')); + expect(url).toMatchInlineSnapshot( + `"xyz/app/kibana#/dashboard/dashboard1?_a=(filters:!())&_g=(filters:!())"` + ); + }); + + test('no filters in result url if no filters applied', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + }); + expect(url).not.toEqual(expect.stringContaining('filters')); + expect(url).toMatchInlineSnapshot(`"xyz/app/kibana#/dashboard/dashboard1?_a=()&_g=()"`); + }); + + test('can turn off preserving filters', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + const urlWithPreservedFiltersTurnedOff = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + preserveSavedFilters: false, + }); + + expect(urlWithPreservedFiltersTurnedOff).not.toEqual( + expect.stringContaining('query:savedfilter1') + ); + expect(urlWithPreservedFiltersTurnedOff).toEqual( + expect.stringContaining('query:appliedfilter') + ); + }); + }); }); diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts index 0fdf395e75bcaa..6f121ceb2d3731 100644 --- a/src/plugins/dashboard/public/url_generator.ts +++ b/src/plugins/dashboard/public/url_generator.ts @@ -27,6 +27,7 @@ import { } from '../../data/public'; import { setStateToKbnUrl } from '../../kibana_utils/public'; import { UrlGeneratorsDefinition, UrlGeneratorState } from '../../share/public'; +import { SavedObjectLoader } from '../../saved_objects/public'; export const STATE_STORAGE_KEY = '_a'; export const GLOBAL_STATE_STORAGE_KEY = '_g'; @@ -64,10 +65,22 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{ * whether to hash the data in the url to avoid url length issues. */ useHash?: boolean; + + /** + * When `true` filters from saved filters from destination dashboard as merged with applied filters + * When `false` applied filters take precedence and override saved filters + * + * true is default + */ + preserveSavedFilters?: boolean; }>; export const createDirectAccessDashboardLinkGenerator = ( - getStartServices: () => Promise<{ appBasePath: string; useHashedUrl: boolean }> + getStartServices: () => Promise<{ + appBasePath: string; + useHashedUrl: boolean; + savedDashboardLoader: SavedObjectLoader; + }> ): UrlGeneratorsDefinition => ({ id: DASHBOARD_APP_URL_GENERATOR, createUrl: async state => { @@ -76,6 +89,19 @@ export const createDirectAccessDashboardLinkGenerator = ( const appBasePath = startServices.appBasePath; const hash = state.dashboardId ? `dashboard/${state.dashboardId}` : `dashboard`; + const getSavedFiltersFromDestinationDashboardIfNeeded = async (): Promise => { + if (state.preserveSavedFilters === false) return []; + if (!state.dashboardId) return []; + try { + const dashboard = await startServices.savedDashboardLoader.get(state.dashboardId); + return dashboard?.searchSource?.getField('filter') ?? []; + } catch (e) { + // in case dashboard is missing, built the url without those filters + // dashboard app will handle redirect to landing page with toast message + return []; + } + }; + const cleanEmptyKeys = (stateObj: Record) => { Object.keys(stateObj).forEach(key => { if (stateObj[key] === undefined) { @@ -85,11 +111,18 @@ export const createDirectAccessDashboardLinkGenerator = ( return stateObj; }; + // leave filters `undefined` if no filters was applied + // in this case dashboard will restore saved filters on its own + const filters = state.filters && [ + ...(await getSavedFiltersFromDestinationDashboardIfNeeded()), + ...state.filters, + ]; + const appStateUrl = setStateToKbnUrl( STATE_STORAGE_KEY, cleanEmptyKeys({ query: state.query, - filters: state.filters?.filter(f => !esFilters.isFilterPinned(f)), + filters: filters?.filter(f => !esFilters.isFilterPinned(f)), }), { useHash }, `${appBasePath}#/${hash}` @@ -99,7 +132,7 @@ export const createDirectAccessDashboardLinkGenerator = ( GLOBAL_STATE_STORAGE_KEY, cleanEmptyKeys({ time: state.timeRange, - filters: state.filters?.filter(f => esFilters.isFilterPinned(f)), + filters: filters?.filter(f => esFilters.isFilterPinned(f)), refreshInterval: state.refreshInterval, }), { useHash }, diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts index 9829498118cc0a..22ed18f75c652a 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts @@ -18,14 +18,17 @@ */ import { SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { savedObjectsServiceMock } from '../../../../core/server/mocks'; import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations'; +const contextMock = savedObjectsServiceMock.createMigrationContext(); + describe('dashboard', () => { describe('7.0.0', () => { const migration = migrations['7.0.0']; test('skips error on empty object', () => { - expect(migration({} as SavedObjectUnsanitizedDoc)).toMatchInlineSnapshot(` + expect(migration({} as SavedObjectUnsanitizedDoc, contextMock)).toMatchInlineSnapshot(` Object { "references": Array [], } @@ -44,7 +47,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -83,7 +86,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -122,7 +125,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "kibanaSavedObjectMeta": Object { @@ -160,7 +163,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "kibanaSavedObjectMeta": Object { @@ -198,7 +201,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -237,7 +240,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -291,7 +294,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { @@ -331,7 +334,7 @@ Object { panelsJSON: 123, }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": 123, @@ -349,7 +352,7 @@ Object { panelsJSON: '{123abc}', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "{123abc}", @@ -367,7 +370,7 @@ Object { panelsJSON: '{}', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "{}", @@ -385,7 +388,7 @@ Object { panelsJSON: '[{"id":"123"}]', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "[{\\"id\\":\\"123\\"}]", @@ -403,7 +406,7 @@ Object { panelsJSON: '[{"type":"visualization"}]', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "[{\\"type\\":\\"visualization\\"}]", @@ -422,7 +425,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, } as SavedObjectUnsanitizedDoc; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts index 7c1d0568cd3d71..4f7945d6dd6017 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts @@ -19,7 +19,7 @@ import { get, flow } from 'lodash'; -import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { SavedObjectMigrationFn } from 'kibana/server'; import { migrations730 } from './migrations_730'; import { migrateMatchAllQuery } from './migrate_match_all_query'; import { DashboardDoc700To720 } from '../../common'; @@ -62,7 +62,7 @@ function migrateIndexPattern(doc: DashboardDoc700To720) { doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); } -const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { +const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { // Set new "references" attribute doc.references = doc.references || []; @@ -111,7 +111,7 @@ export const dashboardSavedObjectTypeMigrations = { * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 * only contained the 6.7.2 migration and not the 7.0.1 migration. */ - '6.7.2': flow(migrateMatchAllQuery), - '7.0.0': flow<(doc: SavedObjectUnsanitizedDoc) => DashboardDoc700To720>(migrations700), - '7.3.0': flow(migrations730), + '6.7.2': flow>(migrateMatchAllQuery), + '7.0.0': flow>(migrations700), + '7.3.0': flow>(migrations730), }; 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 5b8582bf821ef5..db2fbeb2788023 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,7 +21,7 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { get } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; -export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { diff --git a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts index aa744324428a41..a58df547fa5224 100644 --- a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts @@ -17,19 +17,13 @@ * under the License. */ +import { savedObjectsServiceMock } from '../../../../core/server/mocks'; import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations'; import { migrations730 } from './migrations_730'; import { DashboardDoc700To720, DashboardDoc730ToLatest, DashboardDocPre700 } from '../../common'; import { RawSavedDashboardPanel730ToLatest } from '../../common'; -const mockContext = { - log: { - warning: () => {}, - warn: () => {}, - debug: () => {}, - info: () => {}, - }, -}; +const mockContext = savedObjectsServiceMock.createMigrationContext(); test('dashboard migration 7.3.0 migrates filters to query on search source', () => { const doc: DashboardDoc700To720 = { @@ -95,7 +89,7 @@ test('dashboard migration 7.3.0 migrates filters to query on search source when }, }; - const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc); + const doc700 = migrations['7.0.0'](doc, mockContext); const newDoc = migrations['7.3.0'](doc700, mockContext); const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON); @@ -127,7 +121,7 @@ test('dashboard migration works when panelsJSON is missing panelIndex', () => { }, }; - const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc); + const doc700 = migrations['7.0.0'](doc, mockContext); const newDoc = migrations['7.3.0'](doc700, mockContext); const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 698edbf9cd6a8e..e21d27a70e02ad 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -26,6 +26,7 @@ export interface IIndexPattern { id?: string; type?: string; timeFieldName?: string; + getTimeField?(): IFieldType | undefined; fieldFormatMap?: Record< string, { diff --git a/src/plugins/data/common/utils/abort_utils.ts b/src/plugins/data/common/utils/abort_utils.ts index 5051515f3a8265..9aec787170840f 100644 --- a/src/plugins/data/common/utils/abort_utils.ts +++ b/src/plugins/data/common/utils/abort_utils.ts @@ -33,19 +33,31 @@ export class AbortError extends Error { * Returns a `Promise` corresponding with when the given `AbortSignal` is aborted. Useful for * situations when you might need to `Promise.race` multiple `AbortSignal`s, or an `AbortSignal` * with any other expected errors (or completions). + * * @param signal The `AbortSignal` to generate the `Promise` from * @param shouldReject If `false`, the promise will be resolved, otherwise it will be rejected */ -export function toPromise(signal: AbortSignal, shouldReject = false) { - return new Promise((resolve, reject) => { +export function toPromise(signal: AbortSignal, shouldReject?: false): Promise; +export function toPromise(signal: AbortSignal, shouldReject?: true): Promise; +export function toPromise(signal: AbortSignal, shouldReject: boolean = false) { + const promise = new Promise((resolve, reject) => { const action = shouldReject ? reject : resolve; if (signal.aborted) action(); signal.addEventListener('abort', action); }); + + /** + * Below is to make sure we don't have unhandled promise rejections. Otherwise + * Jest tests fail. + */ + promise.catch(() => {}); + + return promise; } /** * Returns an `AbortSignal` that will be aborted when the first of the given signals aborts. + * * @param signals */ export function getCombinedSignal(signals: AbortSignal[]) { diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 86560b3ccf7b12..91dea66f06a94b 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -699,7 +699,10 @@ export function getSearchErrorType({ message }: Pick): " // 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) -export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, forceNow?: Date): import("../..").RangeFilter | undefined; +export function getTime(indexPattern: IIndexPattern | undefined, timeRange: TimeRange, options?: { + forceNow?: Date; + fieldName?: string; +}): import("../..").RangeFilter | undefined; // Warning: (ae-missing-release-tag) "IAggConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -842,6 +845,8 @@ export interface IIndexPattern { // (undocumented) fields: IFieldType[]; // (undocumented) + getTimeField?(): IFieldType | undefined; + // (undocumented) id?: string; // (undocumented) timeFieldName?: string; diff --git a/src/plugins/data/public/query/timefilter/get_time.test.ts b/src/plugins/data/public/query/timefilter/get_time.test.ts index a8eb3a3fe81023..4dba157a6f5546 100644 --- a/src/plugins/data/public/query/timefilter/get_time.test.ts +++ b/src/plugins/data/public/query/timefilter/get_time.test.ts @@ -51,5 +51,43 @@ describe('get_time', () => { }); clock.restore(); }); + + test('build range filter for non-primary field', () => { + const clock = sinon.useFakeTimers(moment.utc([2000, 1, 1, 0, 0, 0, 0]).valueOf()); + + const filter = getTime( + { + id: 'test', + title: 'test', + timeFieldName: 'date', + fields: [ + { + name: 'date', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + filterable: true, + }, + { + name: 'myCustomDate', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + filterable: true, + }, + ], + } as any, + { from: 'now-60y', to: 'now' }, + { fieldName: 'myCustomDate' } + ); + expect(filter!.range.myCustomDate).toEqual({ + gte: '1940-02-01T00:00:00.000Z', + lte: '2000-02-01T00:00:00.000Z', + format: 'strict_date_optional_time', + }); + clock.restore(); + }); }); }); diff --git a/src/plugins/data/public/query/timefilter/get_time.ts b/src/plugins/data/public/query/timefilter/get_time.ts index fa154061890412..9cdd25d3213cee 100644 --- a/src/plugins/data/public/query/timefilter/get_time.ts +++ b/src/plugins/data/public/query/timefilter/get_time.ts @@ -19,7 +19,7 @@ import dateMath from '@elastic/datemath'; import { IIndexPattern } from '../..'; -import { TimeRange, IFieldType, buildRangeFilter } from '../../../common'; +import { TimeRange, buildRangeFilter } from '../../../common'; interface CalculateBoundsOptions { forceNow?: Date; @@ -35,18 +35,27 @@ export function calculateBounds(timeRange: TimeRange, options: CalculateBoundsOp export function getTime( indexPattern: IIndexPattern | undefined, timeRange: TimeRange, + options?: { forceNow?: Date; fieldName?: string } +) { + return createTimeRangeFilter( + indexPattern, + timeRange, + options?.fieldName || indexPattern?.timeFieldName, + options?.forceNow + ); +} + +function createTimeRangeFilter( + indexPattern: IIndexPattern | undefined, + timeRange: TimeRange, + fieldName?: string, forceNow?: Date ) { if (!indexPattern) { - // in CI, we sometimes seem to fail here. return; } - - const timefield: IFieldType | undefined = indexPattern.fields.find( - field => field.name === indexPattern.timeFieldName - ); - - if (!timefield) { + const field = indexPattern.fields.find(f => f.name === (fieldName || indexPattern.timeFieldName)); + if (!field) { return; } @@ -55,7 +64,7 @@ export function getTime( return; } return buildRangeFilter( - timefield, + field, { ...(bounds.min && { gte: bounds.min.toISOString() }), ...(bounds.max && { lte: bounds.max.toISOString() }), diff --git a/src/plugins/data/public/query/timefilter/index.ts b/src/plugins/data/public/query/timefilter/index.ts index a6260e782c12ff..034af03842ab8d 100644 --- a/src/plugins/data/public/query/timefilter/index.ts +++ b/src/plugins/data/public/query/timefilter/index.ts @@ -22,6 +22,6 @@ export { TimefilterService, TimefilterSetup } from './timefilter_service'; export * from './types'; export { Timefilter, TimefilterContract } from './timefilter'; export { TimeHistory, TimeHistoryContract } from './time_history'; -export { getTime } from './get_time'; +export { getTime, calculateBounds } from './get_time'; export { changeTimeFilter } from './lib/change_time_filter'; export { extractTimeFilter } from './lib/extract_time_filter'; diff --git a/src/plugins/data/public/query/timefilter/timefilter.ts b/src/plugins/data/public/query/timefilter/timefilter.ts index 4fbdac47fb3b02..86ef69be572a9c 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.ts @@ -164,7 +164,9 @@ export class Timefilter { }; public createFilter = (indexPattern: IndexPattern, timeRange?: TimeRange) => { - return getTime(indexPattern, timeRange ? timeRange : this._time, this.getForceNow()); + return getTime(indexPattern, timeRange ? timeRange : this._time, { + forceNow: this.getForceNow(), + }); }; public getBounds(): TimeRangeBounds { diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts index 57f3aa85ad9443..3ecdc17cb57f3f 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -45,7 +45,7 @@ const updateTimeBuckets = ( customBuckets?: IBucketDateHistogramAggConfig['buckets'] ) => { const bounds = - agg.params.timeRange && agg.fieldIsTimeField() + agg.params.timeRange && (agg.fieldIsTimeField() || agg.params.interval === 'auto') ? timefilter.calculateBounds(agg.params.timeRange) : undefined; const buckets = customBuckets || agg.buckets; diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 087b83127079f9..eec75b08411330 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -32,8 +32,15 @@ import { Adapters } from '../../../../../plugins/inspector/public'; import { IAggConfigs } from '../aggs'; import { ISearchSource } from '../search_source'; import { tabifyAggResponse } from '../tabify'; -import { Filter, Query, serializeFieldFormat, TimeRange } from '../../../common'; -import { FilterManager, getTime } from '../../query'; +import { + Filter, + Query, + serializeFieldFormat, + TimeRange, + IIndexPattern, + isRangeFilter, +} from '../../../common'; +import { FilterManager, calculateBounds, getTime } from '../../query'; import { getSearchService, getQueryService, getIndexPatterns } from '../../services'; import { buildTabularInspectorData } from './build_tabular_inspector_data'; import { getRequestInspectorStats, getResponseInspectorStats, serializeAggConfig } from './utils'; @@ -42,6 +49,8 @@ export interface RequestHandlerParams { searchSource: ISearchSource; aggs: IAggConfigs; timeRange?: TimeRange; + timeFields?: string[]; + indexPattern?: IIndexPattern; query?: Query; filters?: Filter[]; forceFetch: boolean; @@ -65,12 +74,15 @@ interface Arguments { partialRows: boolean; includeFormatHints: boolean; aggConfigs: string; + timeFields?: string[]; } const handleCourierRequest = async ({ searchSource, aggs, timeRange, + timeFields, + indexPattern, query, filters, forceFetch, @@ -111,9 +123,19 @@ const handleCourierRequest = async ({ return aggs.onSearchRequestStart(paramSearchSource, options); }); - if (timeRange) { + // If timeFields have been specified, use the specified ones, otherwise use primary time field of index + // pattern if it's available. + const defaultTimeField = indexPattern?.getTimeField?.(); + const defaultTimeFields = defaultTimeField ? [defaultTimeField.name] : []; + const allTimeFields = timeFields && timeFields.length > 0 ? timeFields : defaultTimeFields; + + // If a timeRange has been specified and we had at least one timeField available, create range + // filters for that those time fields + if (timeRange && allTimeFields.length > 0) { timeFilterSearchSource.setField('filter', () => { - return getTime(searchSource.getField('index'), timeRange); + return allTimeFields + .map(fieldName => getTime(indexPattern, timeRange, { fieldName })) + .filter(isRangeFilter); }); } @@ -181,11 +203,13 @@ const handleCourierRequest = async ({ (searchSource as any).finalResponse = resp; - const parsedTimeRange = timeRange ? getTime(aggs.indexPattern, timeRange) : null; + const parsedTimeRange = timeRange ? calculateBounds(timeRange) : null; const tabifyParams = { metricsAtAllLevels, partialRows, - timeRange: parsedTimeRange ? parsedTimeRange.range : undefined, + timeRange: parsedTimeRange + ? { from: parsedTimeRange.min, to: parsedTimeRange.max, timeFields: allTimeFields } + : undefined, }; const tabifyCacheHash = calculateObjectHash({ tabifyAggs: aggs, ...tabifyParams }); @@ -242,6 +266,11 @@ export const esaggs = (): ExpressionFunctionDefinition { const check = (aggResp: any, count: number, keys: string[]) => { @@ -187,9 +192,9 @@ describe('Buckets wrapper', () => { }, }; const timeRange = { - gte: 150, - lte: 350, - name: 'date', + from: moment(150), + to: moment(350), + timeFields: ['date'], }; const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); @@ -204,9 +209,9 @@ describe('Buckets wrapper', () => { }, }; const timeRange = { - gte: 150, - lte: 350, - name: 'date', + from: moment(150), + to: moment(350), + timeFields: ['date'], }; const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); @@ -221,9 +226,9 @@ describe('Buckets wrapper', () => { }, }; const timeRange = { - gte: 100, - lte: 400, - name: 'date', + from: moment(100), + to: moment(400), + timeFields: ['date'], }; const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); @@ -238,13 +243,47 @@ describe('Buckets wrapper', () => { }, }; const timeRange = { - gte: 150, - lte: 350, - name: 'date', + from: moment(150), + to: moment(350), + timeFields: ['date'], }; const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); expect(buckets).toHaveLength(4); }); + + test('does drop bucket when multiple time fields specified', () => { + const aggParams = { + drop_partials: true, + field: { + name: 'date', + }, + }; + const timeRange = { + from: moment(100), + to: moment(350), + timeFields: ['date', 'other_datefield'], + }; + const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); + + expect(buckets.buckets.map((b: Bucket) => b.key)).toEqual([100, 200]); + }); + + test('does not drop bucket when no timeFields have been specified', () => { + const aggParams = { + drop_partials: true, + field: { + name: 'date', + }, + }; + const timeRange = { + from: moment(100), + to: moment(350), + timeFields: [], + }; + const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); + + expect(buckets.buckets.map((b: Bucket) => b.key)).toEqual([0, 100, 200, 300]); + }); }); }); diff --git a/src/plugins/data/public/search/tabify/buckets.ts b/src/plugins/data/public/search/tabify/buckets.ts index 971e820ac6ddf6..cd52a09caeaade 100644 --- a/src/plugins/data/public/search/tabify/buckets.ts +++ b/src/plugins/data/public/search/tabify/buckets.ts @@ -20,7 +20,7 @@ import { get, isPlainObject, keys, findKey } from 'lodash'; import moment from 'moment'; import { IAggConfig } from '../aggs'; -import { AggResponseBucket, TabbedRangeFilterParams } from './types'; +import { AggResponseBucket, TabbedRangeFilterParams, TimeRangeInformation } from './types'; type AggParams = IAggConfig['params'] & { drop_partials: boolean; @@ -36,7 +36,7 @@ export class TabifyBuckets { buckets: any; _keys: any[] = []; - constructor(aggResp: any, aggParams?: AggParams, timeRange?: TabbedRangeFilterParams) { + constructor(aggResp: any, aggParams?: AggParams, timeRange?: TimeRangeInformation) { if (aggResp && aggResp.buckets) { this.buckets = aggResp.buckets; } else if (aggResp) { @@ -107,12 +107,12 @@ export class TabifyBuckets { // dropPartials should only be called if the aggParam setting is enabled, // and the agg field is the same as the Time Range. - private dropPartials(params: AggParams, timeRange?: TabbedRangeFilterParams) { + private dropPartials(params: AggParams, timeRange?: TimeRangeInformation) { if ( !timeRange || this.buckets.length <= 1 || this.objectMode || - params.field.name !== timeRange.name + !timeRange.timeFields.includes(params.field.name) ) { return; } @@ -120,10 +120,10 @@ export class TabifyBuckets { const interval = this.buckets[1].key - this.buckets[0].key; this.buckets = this.buckets.filter((bucket: AggResponseBucket) => { - if (moment(bucket.key).isBefore(timeRange.gte)) { + if (moment(bucket.key).isBefore(timeRange.from)) { return false; } - if (moment(bucket.key + interval).isAfter(timeRange.lte)) { + if (moment(bucket.key + interval).isAfter(timeRange.to)) { return false; } return true; diff --git a/src/plugins/data/public/search/tabify/tabify.ts b/src/plugins/data/public/search/tabify/tabify.ts index e93e9890342529..9cb55f94537c56 100644 --- a/src/plugins/data/public/search/tabify/tabify.ts +++ b/src/plugins/data/public/search/tabify/tabify.ts @@ -20,7 +20,7 @@ import { get } from 'lodash'; import { TabbedAggResponseWriter } from './response_writer'; import { TabifyBuckets } from './buckets'; -import { TabbedResponseWriterOptions, TabbedRangeFilterParams } from './types'; +import { TabbedResponseWriterOptions } from './types'; import { AggResponseBucket } from './types'; import { AggGroupNames, IAggConfigs } from '../aggs'; @@ -54,7 +54,7 @@ export function tabifyAggResponse( switch (agg.type.type) { case AggGroupNames.Buckets: const aggBucket = get(bucket, agg.id); - const tabifyBuckets = new TabifyBuckets(aggBucket, agg.params, timeRange); + const tabifyBuckets = new TabifyBuckets(aggBucket, agg.params, respOpts?.timeRange); if (tabifyBuckets.length) { tabifyBuckets.forEach((subBucket, tabifyBucketKey) => { @@ -153,20 +153,6 @@ export function tabifyAggResponse( doc_count: esResponse.hits.total, }; - let timeRange: TabbedRangeFilterParams | undefined; - - // Extract the time range object if provided - if (respOpts && respOpts.timeRange) { - const [timeRangeKey] = Object.keys(respOpts.timeRange); - - if (timeRangeKey) { - timeRange = { - name: timeRangeKey, - ...respOpts.timeRange[timeRangeKey], - }; - } - } - collectBucket(aggConfigs, write, topLevelBucket, '', 1); return write.response(); diff --git a/src/plugins/data/public/search/tabify/types.ts b/src/plugins/data/public/search/tabify/types.ts index 1e051880d3f19f..72e91eb58c8a97 100644 --- a/src/plugins/data/public/search/tabify/types.ts +++ b/src/plugins/data/public/search/tabify/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { Moment } from 'moment'; import { RangeFilterParams } from '../../../common'; import { IAggConfig } from '../aggs'; @@ -25,11 +26,18 @@ export interface TabbedRangeFilterParams extends RangeFilterParams { name: string; } +/** @internal */ +export interface TimeRangeInformation { + from?: Moment; + to?: Moment; + timeFields: string[]; +} + /** @internal **/ export interface TabbedResponseWriterOptions { metricsAtAllLevels: boolean; partialRows: boolean; - timeRange?: { [key: string]: RangeFilterParams }; + timeRange?: TimeRangeInformation; } /** @internal */ diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts index 7a16386ea484c8..c64f7361a8cf4f 100644 --- a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts @@ -20,7 +20,7 @@ import { flow, omit } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; -const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ +const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ ...doc, attributes: { ...doc.attributes, @@ -29,7 +29,7 @@ const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => }, }); -const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { +const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { if (!doc.attributes.fields) return doc; const fieldsString = doc.attributes.fields; diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts index 45fa5e11e2a3d7..c8ded51193c92b 100644 --- a/src/plugins/data/server/saved_objects/search_migrations.ts +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -21,7 +21,7 @@ import { flow, get } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; import { DEFAULT_QUERY_LANGUAGE } from '../../common'; -const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { @@ -55,7 +55,7 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { return doc; }; -const migrateIndexPattern: SavedObjectMigrationFn = doc => { +const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (typeof searchSourceJSON !== 'string') { return doc; @@ -97,13 +97,13 @@ const migrateIndexPattern: SavedObjectMigrationFn = doc => { return doc; }; -const setNewReferences: SavedObjectMigrationFn = (doc, context) => { +const setNewReferences: SavedObjectMigrationFn = (doc, context) => { doc.references = doc.references || []; // Migrate index pattern return migrateIndexPattern(doc, context); }; -const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { +const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { const sort = get(doc, 'attributes.sort'); if (!sort) return doc; @@ -122,7 +122,7 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { }; export const searchSavedObjectTypeMigrations = { - '6.7.2': flow(migrateMatchAllQuery), - '7.0.0': flow(setNewReferences), - '7.4.0': flow(migrateSearchSortToNestedArray), + '6.7.2': flow>(migrateMatchAllQuery), + '7.0.0': flow>(setNewReferences), + '7.4.0': flow>(migrateSearchSortToNestedArray), }; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 5d94b6516c2bab..df4ba23244b4dc 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -408,6 +408,8 @@ export interface IIndexPattern { // (undocumented) fields: IFieldType[]; // (undocumented) + getTimeField?(): IFieldType | undefined; + // (undocumented) id?: string; // (undocumented) timeFieldName?: string; diff --git a/src/plugins/expressions/common/execution/execution.abortion.test.ts b/src/plugins/expressions/common/execution/execution.abortion.test.ts new file mode 100644 index 00000000000000..ecbf94eceae642 --- /dev/null +++ b/src/plugins/expressions/common/execution/execution.abortion.test.ts @@ -0,0 +1,96 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { Execution } from './execution'; +import { parseExpression } from '../ast'; +import { createUnitTestExecutor } from '../test_helpers'; + +jest.useFakeTimers(); + +beforeEach(() => { + jest.clearAllTimers(); +}); + +const createExecution = ( + expression: string = 'foo bar=123', + context: Record = {}, + debug: boolean = false +) => { + const executor = createUnitTestExecutor(); + const execution = new Execution({ + executor, + ast: parseExpression(expression), + context, + debug, + }); + return execution; +}; + +describe('Execution abortion tests', () => { + test('can abort an expression immediately', async () => { + const execution = createExecution('sleep 10'); + + execution.start(); + execution.cancel(); + + const result = await execution.result; + + expect(result).toMatchObject({ + type: 'error', + error: { + message: 'The expression was aborted.', + name: 'AbortError', + }, + }); + }); + + test('can abort an expression which has function running mid flight', async () => { + const execution = createExecution('sleep 300'); + + execution.start(); + jest.advanceTimersByTime(100); + execution.cancel(); + + const result = await execution.result; + + expect(result).toMatchObject({ + type: 'error', + error: { + message: 'The expression was aborted.', + name: 'AbortError', + }, + }); + }); + + test('cancelling execution after it completed has no effect', async () => { + jest.useRealTimers(); + + const execution = createExecution('sleep 1'); + + execution.start(); + + const result = await execution.result; + + execution.cancel(); + + expect(result).toBe(null); + + jest.useFakeTimers(); + }); +}); diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index d0ab178296408a..6ee12d97a64226 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -22,7 +22,7 @@ import { Executor } from '../executor'; import { createExecutionContainer, ExecutionContainer } from './container'; import { createError } from '../util'; import { Defer, now } from '../../../kibana_utils/common'; -import { AbortError } from '../../../data/common'; +import { toPromise } from '../../../data/common/utils/abort_utils'; import { RequestAdapter, DataAdapter } from '../../../inspector/common'; import { isExpressionValueError, ExpressionValueError } from '../expression_types/specs/error'; import { @@ -38,6 +38,12 @@ import { ArgumentType, ExpressionFunction } from '../expression_functions'; import { getByAlias } from '../util/get_by_alias'; import { ExecutionContract } from './execution_contract'; +const createAbortErrorValue = () => + createError({ + message: 'The expression was aborted.', + name: 'AbortError', + }); + export interface ExecutionParams< ExtraContext extends Record = Record > { @@ -70,7 +76,7 @@ export class Execution< /** * Dynamic state of the execution. */ - public readonly state: ExecutionContainer; + public readonly state: ExecutionContainer; /** * Initial input of the execution. @@ -91,6 +97,18 @@ export class Execution< */ private readonly abortController = new AbortController(); + /** + * Promise that rejects if/when abort controller sends "abort" signal. + */ + private readonly abortRejection = toPromise(this.abortController.signal, true); + + /** + * Races a given promise against the "abort" event of `abortController`. + */ + private race(promise: Promise): Promise { + return Promise.race([this.abortRejection, promise]); + } + /** * Whether .start() method has been called. */ @@ -99,7 +117,7 @@ export class Execution< /** * Future that tracks result or error of this execution. */ - private readonly firstResultFuture = new Defer(); + private readonly firstResultFuture = new Defer(); /** * Contract is a public representation of `Execution` instances. Contract we @@ -114,7 +132,7 @@ export class Execution< public readonly expression: string; - public get result(): Promise { + public get result(): Promise { return this.firstResultFuture.promise; } @@ -134,7 +152,7 @@ export class Execution< this.expression = params.expression || formatExpression(params.ast!); const ast = params.ast || parseExpression(this.expression); - this.state = createExecutionContainer({ + this.state = createExecutionContainer({ ...executor.state.get(), state: 'not-started', ast, @@ -173,7 +191,12 @@ export class Execution< this.state.transitions.start(); const { resolve, reject } = this.firstResultFuture; - this.invokeChain(this.state.get().ast.chain, input).then(resolve, reject); + const chainPromise = this.invokeChain(this.state.get().ast.chain, input); + + this.race(chainPromise).then(resolve, error => { + if (this.abortController.signal.aborted) resolve(createAbortErrorValue()); + else reject(error); + }); this.firstResultFuture.promise.then( result => { @@ -189,11 +212,6 @@ export class Execution< if (!chainArr.length) return input; for (const link of chainArr) { - // if execution was aborted return error - if (this.context.abortSignal && this.context.abortSignal.aborted) { - return createError(new AbortError('The expression was aborted.')); - } - const { function: fnName, arguments: fnArgs } = link; const fn = getByAlias(this.state.get().functions, fnName); @@ -207,10 +225,10 @@ export class Execution< try { // `resolveArgs` returns an object because the arguments themselves might // actually have a `then` function which would be treated as a `Promise`. - const { resolvedArgs } = await this.resolveArgs(fn, input, fnArgs); + const { resolvedArgs } = await this.race(this.resolveArgs(fn, input, fnArgs)); args = resolvedArgs; timeStart = this.params.debug ? now() : 0; - const output = await this.invokeFunction(fn, input, resolvedArgs); + const output = await this.race(this.invokeFunction(fn, input, resolvedArgs)); if (this.params.debug) { const timeEnd: number = now(); @@ -256,7 +274,7 @@ export class Execution< args: Record ): Promise { const normalizedInput = this.cast(input, fn.inputTypes); - const output = await fn.fn(normalizedInput, args, this.context); + const output = await this.race(fn.fn(normalizedInput, args, this.context)); // Validate that the function returned the type it said it would. // This isn't required, but it keeps function developers honest. diff --git a/src/plugins/expressions/common/expression_types/specs/error.ts b/src/plugins/expressions/common/expression_types/specs/error.ts index 4b255a0f967b26..35554954d08282 100644 --- a/src/plugins/expressions/common/expression_types/specs/error.ts +++ b/src/plugins/expressions/common/expression_types/specs/error.ts @@ -31,7 +31,7 @@ export type ExpressionValueError = ExpressionValueBoxed< name?: string; stack?: string; }; - info: unknown; + info?: unknown; } >; diff --git a/src/plugins/expressions/common/util/create_error.ts b/src/plugins/expressions/common/util/create_error.ts index 8236ff8709a823..bc27b0eda49598 100644 --- a/src/plugins/expressions/common/util/create_error.ts +++ b/src/plugins/expressions/common/util/create_error.ts @@ -17,9 +17,11 @@ * under the License. */ +import { ExpressionValueError } from '../../public'; + type ErrorLike = Partial>; -export const createError = (err: string | ErrorLike) => ({ +export const createError = (err: string | ErrorLike): ExpressionValueError => ({ type: 'error', error: { stack: @@ -28,7 +30,7 @@ export const createError = (err: string | ErrorLike) => ({ : typeof err === 'object' ? err.stack : undefined, - message: typeof err === 'string' ? err : err.message, + message: typeof err === 'string' ? err : String(err.message), name: typeof err === 'object' ? err.name || 'Error' : 'Error', }, }); diff --git a/src/plugins/maps_legacy/public/__tests__/map/service_settings.js b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js index 4cbe098501c675..822378163a7ebc 100644 --- a/src/plugins/maps_legacy/public/__tests__/map/service_settings.js +++ b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js @@ -26,7 +26,7 @@ import EMS_TILES from './ems_mocks/sample_tiles.json'; import EMS_STYLE_ROAD_MAP_BRIGHT from './ems_mocks/sample_style_bright'; import EMS_STYLE_ROAD_MAP_DESATURATED from './ems_mocks/sample_style_desaturated'; import EMS_STYLE_DARK_MAP from './ems_mocks/sample_style_dark'; -import { ORIGIN } from '../../common/origin'; +import { ORIGIN } from '../../common/constants/origin'; describe('service_settings (FKA tilemaptest)', function() { let serviceSettings; diff --git a/src/plugins/maps_legacy/public/common/origin.ts b/src/plugins/maps_legacy/public/common/constants/origin.ts similarity index 100% rename from src/plugins/maps_legacy/public/common/origin.ts rename to src/plugins/maps_legacy/public/common/constants/origin.ts diff --git a/src/legacy/core_plugins/tile_map/public/types.ts b/src/plugins/maps_legacy/public/common/types/external_basemap_types.ts similarity index 95% rename from src/legacy/core_plugins/tile_map/public/types.ts rename to src/plugins/maps_legacy/public/common/types/external_basemap_types.ts index e1b4c27319123a..be9c4d0d9c37bb 100644 --- a/src/legacy/core_plugins/tile_map/public/types.ts +++ b/src/plugins/maps_legacy/public/common/types/external_basemap_types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { TmsLayer } from '../../../../plugins/maps_legacy/public'; +import { TmsLayer } from '../../index'; import { MapTypes } from './map_types'; export interface WMSOptions { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/index.js b/src/plugins/maps_legacy/public/common/types/index.ts similarity index 79% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/index.js rename to src/plugins/maps_legacy/public/common/types/index.ts index b2559c085aeab3..e6cabdde82cd9a 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/index.js +++ b/src/plugins/maps_legacy/public/common/types/index.ts @@ -17,7 +17,10 @@ * under the License. */ -import { VislibProvider } from './vislib'; - -// eslint-disable-next-line import/no-default-export -export default VislibProvider; +/** + * Use * syntax so that these exports do not break when internal + * types are stripped. + */ +export * from './external_basemap_types'; +export * from './map_types'; +export * from './region_map_types'; diff --git a/src/legacy/core_plugins/tile_map/public/map_types.ts b/src/plugins/maps_legacy/public/common/types/map_types.ts similarity index 100% rename from src/legacy/core_plugins/tile_map/public/map_types.ts rename to src/plugins/maps_legacy/public/common/types/map_types.ts diff --git a/src/legacy/core_plugins/region_map/public/types.ts b/src/plugins/maps_legacy/public/common/types/region_map_types.ts similarity index 89% rename from src/legacy/core_plugins/region_map/public/types.ts rename to src/plugins/maps_legacy/public/common/types/region_map_types.ts index 8585bf720e0cf7..0da597068f11e0 100644 --- a/src/legacy/core_plugins/region_map/public/types.ts +++ b/src/plugins/maps_legacy/public/common/types/region_map_types.ts @@ -17,8 +17,8 @@ * under the License. */ -import { VectorLayer, FileLayerField } from '../../../../plugins/maps_legacy/public'; -import { WMSOptions } from '../../tile_map/public/types'; +import { VectorLayer, FileLayerField } from '../../index'; +import { WMSOptions } from './external_basemap_types'; export interface RegionMapVisParams { readonly addTooltip: true; diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx similarity index 77% rename from src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx rename to src/plugins/maps_legacy/public/components/wms_internal_options.tsx index 47f5b8f31e62be..d1def8153d1a84 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx @@ -21,8 +21,8 @@ import React from 'react'; import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { TextInputOption } from '../../../../../plugins/charts/public'; -import { WMSOptions } from '../types'; +import { TextInputOption } from '../../../charts/public'; +import { WMSOptions } from '../common/types/external_basemap_types'; interface WmsInternalOptions { wms: WMSOptions; @@ -32,14 +32,14 @@ interface WmsInternalOptions { function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { const wmsLink = ( - + ); const footnoteText = ( <> @@ -64,7 +64,7 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { @@ -74,14 +74,14 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { - + } helpText={ <> {footnote} @@ -95,14 +95,17 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { - + } helpText={ <> {footnote} @@ -117,7 +120,7 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { label={ <> @@ -126,7 +129,7 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { helpText={ <> {footnote} @@ -140,14 +143,17 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { - + } helpText={ <> {footnote} @@ -161,13 +167,13 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { } helpText={ } @@ -179,14 +185,17 @@ function WmsInternalOptions({ wms, setValue }: WmsInternalOptions) { - + } helpText={ <> {footnote} diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx b/src/plugins/maps_legacy/public/components/wms_options.tsx similarity index 82% rename from src/legacy/core_plugins/tile_map/public/components/wms_options.tsx rename to src/plugins/maps_legacy/public/components/wms_options.tsx index e74c260d3b8e5c..4892463bb9f856 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_options.tsx @@ -21,12 +21,12 @@ import React, { useMemo } from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { TmsLayer } from '../../../../../plugins/maps_legacy/public'; -import { Vis } from '../../../../../plugins/visualizations/public'; -import { RegionMapVisParams } from '../../../region_map/public/types'; -import { SelectOption, SwitchOption } from '../../../../../plugins/charts/public'; +import { TmsLayer } from '../index'; +import { Vis } from '../../../visualizations/public'; +import { RegionMapVisParams } from '../common/types/region_map_types'; +import { SelectOption, SwitchOption } from '../../../charts/public'; import { WmsInternalOptions } from './wms_internal_options'; -import { WMSOptions, TileMapVisParams } from '../types'; +import { WMSOptions, TileMapVisParams } from '../common/types/external_basemap_types'; interface Props { stateParams: TileMapVisParams | RegionMapVisParams; @@ -59,7 +59,7 @@ function WmsOptions({ stateParams, setValue, vis }: Props) {

@@ -67,10 +67,10 @@ function WmsOptions({ stateParams, setValue, vis }: Props) { new KibanaMap(...args); +} + +export function getBaseMapsVis(core: CoreSetup, serviceSettings: IServiceSettings) { + const getKibanaMap = getKibanaMapFactoryProvider(core); + return new BaseMapsVisualizationProvider(getKibanaMap, serviceSettings); +} + +export * from './common/types'; +export { ORIGIN } from './common/constants/origin'; + +export { WmsOptions } from './components/wms_options'; + export type MapsLegacyPluginSetup = ReturnType; export type MapsLegacyPluginStart = ReturnType; diff --git a/src/legacy/core_plugins/tile_map/public/base_maps_visualization.js b/src/plugins/maps_legacy/public/map/base_maps_visualization.js similarity index 89% rename from src/legacy/core_plugins/tile_map/public/base_maps_visualization.js rename to src/plugins/maps_legacy/public/map/base_maps_visualization.js index 1dac4607280cc7..c4ac671a5187c6 100644 --- a/src/legacy/core_plugins/tile_map/public/base_maps_visualization.js +++ b/src/plugins/maps_legacy/public/map/base_maps_visualization.js @@ -19,16 +19,14 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { KibanaMap } from '../../../../plugins/maps_legacy/public'; import * as Rx from 'rxjs'; import { filter, first } from 'rxjs/operators'; -import { toastNotifications } from 'ui/notify'; -import chrome from 'ui/chrome'; +import { getInjectedVarFunc, getUiSettings, getToasts } from '../kibana_services'; const WMS_MINZOOM = 0; const WMS_MAXZOOM = 22; //increase this to 22. Better for WMS -export function BaseMapsVisualizationProvider(mapServiceSettings, notificationService) { +export function BaseMapsVisualizationProvider(getKibanaMap, mapServiceSettings) { /** * Abstract base class for a visualization consisting of a map with a single baselayer. * @class BaseMapsVisualization @@ -36,7 +34,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe */ const serviceSettings = mapServiceSettings; - const toastService = notificationService; + const toastService = getToasts(); return class BaseMapsVisualization { constructor(element, vis) { @@ -99,7 +97,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe options.center = centerFromUIState ? centerFromUIState : this.vis.params.mapCenter; const services = { toastService }; - this._kibanaMap = new KibanaMap(this._container, options, services); + this._kibanaMap = getKibanaMap(this._container, options, services); this._kibanaMap.setMinZoom(WMS_MINZOOM); //use a default this._kibanaMap.setMaxZoom(WMS_MAXZOOM); //use a default @@ -118,20 +116,20 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe _tmsConfigured() { const { wms } = this._getMapsParams(); - const hasTmsBaseLayer = !!wms.selectedTmsLayer; + const hasTmsBaseLayer = wms && !!wms.selectedTmsLayer; return hasTmsBaseLayer; } _wmsConfigured() { const { wms } = this._getMapsParams(); - const hasWmsBaseLayer = !!wms.enabled; + const hasWmsBaseLayer = wms && !!wms.enabled; return hasWmsBaseLayer; } async _updateBaseLayer() { - const emsTileLayerId = chrome.getInjected('emsTileLayerId', true); + const emsTileLayerId = getInjectedVarFunc()('emsTileLayerId', true); if (!this._kibanaMap) { return; @@ -149,7 +147,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe this._setTmsLayer(initBasemapLayer); } } catch (e) { - toastNotifications.addWarning(e.message); + toastService.addWarning(e.message); return; } return; @@ -176,7 +174,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe this._setTmsLayer(selectedTmsLayer); } } catch (tmsLoadingError) { - toastNotifications.addWarning(tmsLoadingError.message); + toastService.addWarning(tmsLoadingError.message); } } @@ -190,7 +188,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe if (typeof isDesaturated !== 'boolean') { isDesaturated = true; } - const isDarkMode = chrome.getUiSettingsClient().get('theme:darkMode'); + const isDarkMode = getUiSettings().get('theme:darkMode'); const meta = await serviceSettings.getAttributesForTMSLayer( tmsLayer, isDesaturated, @@ -208,7 +206,7 @@ export function BaseMapsVisualizationProvider(mapServiceSettings, notificationSe async _updateData() { throw new Error( - i18n.translate('tileMap.baseMapsVisualization.childShouldImplementMethodErrorMessage', { + i18n.translate('maps_legacy.baseMapsVisualization.childShouldImplementMethodErrorMessage', { defaultMessage: 'Child should implement this method to respond to data-update', }) ); diff --git a/src/plugins/maps_legacy/public/map/kibana_map.js b/src/plugins/maps_legacy/public/map/kibana_map.js index 1c4d0882cb7da4..c7cec1b14159aa 100644 --- a/src/plugins/maps_legacy/public/map/kibana_map.js +++ b/src/plugins/maps_legacy/public/map/kibana_map.js @@ -24,7 +24,8 @@ import $ from 'jquery'; import _ from 'lodash'; import { zoomToPrecision } from './zoom_to_precision'; import { i18n } from '@kbn/i18n'; -import { ORIGIN } from '../common/origin'; +import { ORIGIN } from '../common/constants/origin'; +import { getToasts } from '../kibana_services'; function makeFitControl(fitContainer, kibanaMap) { const FitControl = L.Control.extend({ @@ -101,7 +102,7 @@ function makeLegendControl(container, kibanaMap, position) { * Serves as simple abstraction for leaflet as well. */ export class KibanaMap extends EventEmitter { - constructor(containerNode, options, services) { + constructor(containerNode, options) { super(); this._containerNode = containerNode; this._leafletBaseLayer = null; @@ -116,7 +117,6 @@ export class KibanaMap extends EventEmitter { this._layers = []; this._listeners = []; this._showTooltip = false; - this.toastService = services ? services.toastService : null; const leafletOptions = { minZoom: options.minZoom, @@ -483,21 +483,19 @@ export class KibanaMap extends EventEmitter { } _addMaxZoomMessage = layer => { - if (this.toastService) { - const zoomWarningMsg = createZoomWarningMsg( - this.toastService, - this.getZoomLevel, - this.getMaxZoomLevel - ); + const zoomWarningMsg = createZoomWarningMsg( + getToasts(), + this.getZoomLevel, + this.getMaxZoomLevel + ); - this._leafletMap.on('zoomend', zoomWarningMsg); - this._containerNode.setAttribute('data-test-subj', 'zoomWarningEnabled'); + this._leafletMap.on('zoomend', zoomWarningMsg); + this._containerNode.setAttribute('data-test-subj', 'zoomWarningEnabled'); - layer.on('remove', () => { - this._leafletMap.off('zoomend', zoomWarningMsg); - this._containerNode.removeAttribute('data-test-subj'); - }); - } + layer.on('remove', () => { + this._leafletMap.off('zoomend', zoomWarningMsg); + this._containerNode.removeAttribute('data-test-subj'); + }); }; setLegendPosition(position) { diff --git a/src/plugins/maps_legacy/public/map/service_settings.js b/src/plugins/maps_legacy/public/map/service_settings.js index f4f0d66ee20ded..8e3a0648e99d4b 100644 --- a/src/plugins/maps_legacy/public/map/service_settings.js +++ b/src/plugins/maps_legacy/public/map/service_settings.js @@ -22,7 +22,7 @@ import MarkdownIt from 'markdown-it'; import { EMSClient } from '@elastic/ems-client'; import { i18n } from '@kbn/i18n'; import { getInjectedVarFunc } from '../kibana_services'; -import { ORIGIN } from '../common/origin'; +import { ORIGIN } from '../common/constants/origin'; const TMS_IN_YML_ID = 'TMS in config/kibana.yml'; diff --git a/src/plugins/maps_legacy/public/plugin.ts b/src/plugins/maps_legacy/public/plugin.ts index 751be65e1dbf6e..acc7655a5e2636 100644 --- a/src/plugins/maps_legacy/public/plugin.ts +++ b/src/plugins/maps_legacy/public/plugin.ts @@ -33,18 +33,20 @@ import { MapsLegacyPluginSetup, MapsLegacyPluginStart } from './index'; * @public */ +export const bindSetupCoreAndPlugins = (core: CoreSetup) => { + setToasts(core.notifications.toasts); + setUiSettings(core.uiSettings); + setInjectedVarFunc(core.injectedMetadata.getInjectedVar); +}; + // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsLegacySetupDependencies {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsLegacyStartDependencies {} export class MapsLegacyPlugin implements Plugin { - constructor() {} - public setup(core: CoreSetup, plugins: MapsLegacySetupDependencies) { - setToasts(core.notifications.toasts); - setUiSettings(core.uiSettings); - setInjectedVarFunc(core.injectedMetadata.getInjectedVar); + bindSetupCoreAndPlugins(core); return { serviceSettings: new ServiceSettings(), diff --git a/src/legacy/core_plugins/region_map/public/shim/legacy_dependencies_plugin.ts b/src/plugins/maps_legacy/public/tooltip_provider.js similarity index 56% rename from src/legacy/core_plugins/region_map/public/shim/legacy_dependencies_plugin.ts rename to src/plugins/maps_legacy/public/tooltip_provider.js index 3a7615e83f2817..85631408169971 100644 --- a/src/legacy/core_plugins/region_map/public/shim/legacy_dependencies_plugin.ts +++ b/src/plugins/maps_legacy/public/tooltip_provider.js @@ -17,26 +17,27 @@ * under the License. */ -import chrome from 'ui/chrome'; -import { CoreStart, Plugin } from 'kibana/public'; +import React from 'react'; +import ReactDOMServer from 'react-dom/server'; -/** @internal */ -export interface LegacyDependenciesPluginSetup { - $injector: any; - serviceSettings: any; +function getToolTipContent(details) { + return ReactDOMServer.renderToStaticMarkup( + + + {details.map((detail, i) => ( + + + + + ))} + +
{detail.label}{detail.value}
+ ); } -export class LegacyDependenciesPlugin - implements Plugin, void> { - public async setup() { - const $injector = await chrome.dangerouslyGetActiveInjector(); - - return { - $injector, - } as LegacyDependenciesPluginSetup; - } - - public start(core: CoreStart) { - // nothing to do here yet - } +export function mapTooltipProvider(element, formatter) { + return (...args) => { + const details = formatter(...args); + return details && getToolTipContent(details); + }; } diff --git a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx index fe3150fc0bb076..c1daf3445219f9 100644 --- a/src/plugins/saved_objects_management/public/management_section/mount_section.tsx +++ b/src/plugins/saved_objects_management/public/management_section/mount_section.tsx @@ -17,23 +17,15 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React, { lazy, Suspense } from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter, Switch, Route, useParams, useLocation } from 'react-router-dom'; -import { parse } from 'query-string'; -import { get } from 'lodash'; -import { i18n } from '@kbn/i18n'; +import { HashRouter, Switch, Route } from 'react-router-dom'; import { I18nProvider } from '@kbn/i18n/react'; -import { CoreSetup, CoreStart, ChromeBreadcrumb, Capabilities } from 'src/core/public'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { CoreSetup, Capabilities } from 'src/core/public'; import { ManagementAppMountParams } from '../../../management/public'; -import { DataPublicPluginStart } from '../../../data/public'; import { StartDependencies, SavedObjectsManagementPluginStart } from '../plugin'; -import { - ISavedObjectsManagementServiceRegistry, - SavedObjectsManagementActionServiceStart, -} from '../services'; -import { SavedObjectsTable } from './objects_table'; -import { SavedObjectEdition } from './object_view'; +import { ISavedObjectsManagementServiceRegistry } from '../services'; import { getAllowedTypes } from './../lib'; interface MountParams { @@ -44,6 +36,8 @@ interface MountParams { let allowedObjectTypes: string[] | undefined; +const SavedObjectsEditionPage = lazy(() => import('./saved_objects_edition_page')); +const SavedObjectsTablePage = lazy(() => import('./saved_objects_table_page')); export const mountManagementSection = async ({ core, mountParams, @@ -63,23 +57,27 @@ export const mountManagementSection = async ({ - + }> + + - + }> + + @@ -103,110 +101,3 @@ const RedirectToHomeIfUnauthorized: React.FunctionComponent<{ } return children! as React.ReactElement; }; - -const SavedObjectsEditionPage = ({ - coreStart, - serviceRegistry, - setBreadcrumbs, -}: { - coreStart: CoreStart; - serviceRegistry: ISavedObjectsManagementServiceRegistry; - setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; -}) => { - const { service: serviceName, id } = useParams<{ service: string; id: string }>(); - const capabilities = coreStart.application.capabilities; - - const { search } = useLocation(); - const query = parse(search); - const service = serviceRegistry.get(serviceName); - - useEffect(() => { - setBreadcrumbs([ - { - text: i18n.translate('savedObjectsManagement.breadcrumb.index', { - defaultMessage: 'Saved objects', - }), - href: '#/management/kibana/objects', - }, - { - text: i18n.translate('savedObjectsManagement.breadcrumb.edit', { - defaultMessage: 'Edit {savedObjectType}', - values: { savedObjectType: service?.service.type ?? 'object' }, - }), - }, - ]); - }, [setBreadcrumbs, service]); - - return ( - - ); -}; - -const SavedObjectsTablePage = ({ - coreStart, - dataStart, - allowedTypes, - serviceRegistry, - actionRegistry, - setBreadcrumbs, -}: { - coreStart: CoreStart; - dataStart: DataPublicPluginStart; - allowedTypes: string[]; - serviceRegistry: ISavedObjectsManagementServiceRegistry; - actionRegistry: SavedObjectsManagementActionServiceStart; - setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; -}) => { - const capabilities = coreStart.application.capabilities; - const itemsPerPage = coreStart.uiSettings.get('savedObjects:perPage', 50); - - useEffect(() => { - setBreadcrumbs([ - { - text: i18n.translate('savedObjectsManagement.breadcrumb.index', { - defaultMessage: 'Saved objects', - }), - href: '#/management/kibana/objects', - }, - ]); - }, [setBreadcrumbs]); - - return ( - { - const { editUrl } = savedObject.meta; - if (editUrl) { - // previously, kbnUrl.change(object.meta.editUrl); was used. - // using direct access to location.hash seems the only option for now, - // as using react-router-dom will prefix the url with the router's basename - // which should be ignored there. - window.location.hash = editUrl; - } - }} - canGoInApp={savedObject => { - const { inAppUrl } = savedObject.meta; - return inAppUrl ? get(capabilities, inAppUrl.uiCapabilitiesPath) : false; - }} - /> - ); -}; diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx new file mode 100644 index 00000000000000..5ac6e8e103c47c --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx @@ -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 React, { useEffect } from 'react'; +import { useParams, useLocation } from 'react-router-dom'; +import { parse } from 'query-string'; +import { i18n } from '@kbn/i18n'; +import { CoreStart, ChromeBreadcrumb } from 'src/core/public'; +import { ISavedObjectsManagementServiceRegistry } from '../services'; +import { SavedObjectEdition } from './object_view'; + +const SavedObjectsEditionPage = ({ + coreStart, + serviceRegistry, + setBreadcrumbs, +}: { + coreStart: CoreStart; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; +}) => { + const { service: serviceName, id } = useParams<{ service: string; id: string }>(); + const capabilities = coreStart.application.capabilities; + + const { search } = useLocation(); + const query = parse(search); + const service = serviceRegistry.get(serviceName); + + useEffect(() => { + setBreadcrumbs([ + { + text: i18n.translate('savedObjectsManagement.breadcrumb.index', { + defaultMessage: 'Saved objects', + }), + href: '#/management/kibana/objects', + }, + { + text: i18n.translate('savedObjectsManagement.breadcrumb.edit', { + defaultMessage: 'Edit {savedObjectType}', + values: { savedObjectType: service?.service.type ?? 'object' }, + }), + }, + ]); + }, [setBreadcrumbs, service]); + + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { SavedObjectsEditionPage as default }; diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx new file mode 100644 index 00000000000000..7660d17f91c5bb --- /dev/null +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_table_page.tsx @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 } from 'react'; +import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { CoreStart, ChromeBreadcrumb } from 'src/core/public'; +import { DataPublicPluginStart } from '../../../data/public'; +import { + ISavedObjectsManagementServiceRegistry, + SavedObjectsManagementActionServiceStart, +} from '../services'; +import { SavedObjectsTable } from './objects_table'; + +const SavedObjectsTablePage = ({ + coreStart, + dataStart, + allowedTypes, + serviceRegistry, + actionRegistry, + setBreadcrumbs, +}: { + coreStart: CoreStart; + dataStart: DataPublicPluginStart; + allowedTypes: string[]; + serviceRegistry: ISavedObjectsManagementServiceRegistry; + actionRegistry: SavedObjectsManagementActionServiceStart; + setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; +}) => { + const capabilities = coreStart.application.capabilities; + const itemsPerPage = coreStart.uiSettings.get('savedObjects:perPage', 50); + + useEffect(() => { + setBreadcrumbs([ + { + text: i18n.translate('savedObjectsManagement.breadcrumb.index', { + defaultMessage: 'Saved objects', + }), + href: '#/management/kibana/objects', + }, + ]); + }, [setBreadcrumbs]); + + return ( + { + const { editUrl } = savedObject.meta; + if (editUrl) { + // previously, kbnUrl.change(object.meta.editUrl); was used. + // using direct access to location.hash seems the only option for now, + // as using react-router-dom will prefix the url with the router's basename + // which should be ignored there. + window.location.hash = editUrl; + } + }} + canGoInApp={savedObject => { + const { inAppUrl } = savedObject.meta; + return inAppUrl ? get(capabilities, inAppUrl.uiCapabilitiesPath) : false; + }} + /> + ); +}; +// eslint-disable-next-line import/no-default-export +export { SavedObjectsTablePage as default }; 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 new file mode 100644 index 00000000000000..8c0117e5a72664 --- /dev/null +++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap @@ -0,0 +1,492 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TelemetryManagementSectionComponent renders as expected 1`] = ` + + + + + + +

+ +

+
+
+
+ + + + , + } + } + /> +

+ } + /> + + +

+ + + , + } + } + /> +

+

+ + + +

+ , + "displayName": "Provide usage statistics", + "name": "telemetry:enabled", + "type": "boolean", + "value": true, + } + } + toasts={null} + /> +
+
+
+`; + +exports[`TelemetryManagementSectionComponent renders null because allowChangingOptInStatus is false 1`] = ` + +`; + +exports[`TelemetryManagementSectionComponent renders null because query does not match the SEARCH_TERMS 1`] = ` + +`; + +exports[`TelemetryManagementSectionComponent test the wrapper (for coverage purposes) 1`] = `""`; 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 new file mode 100644 index 00000000000000..d0c2bd13f802d7 --- /dev/null +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx @@ -0,0 +1,284 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { 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 { telemetryManagementSectionWrapper } from './telemetry_management_section_wrapper'; + +describe('TelemetryManagementSectionComponent', () => { + const coreStart = coreMock.createStart(); + const coreSetup = coreMock.createSetup(); + + it('renders as expected', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: true, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + expect( + shallowWithIntl( + + ) + ).toMatchSnapshot(); + }); + + it('renders null because query does not match the SEARCH_TERMS', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + + ); + try { + expect( + component.setProps({ ...component.props(), query: { text: 'asssdasdsad' } }) + ).toMatchSnapshot(); + expect(onQueryMatchChange).toHaveBeenCalledWith(false); + expect(onQueryMatchChange).toHaveBeenCalledTimes(1); + } finally { + component.unmount(); + } + }); + + it('renders because query matches the SEARCH_TERMS', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + + ); + try { + expect( + component.setProps({ ...component.props(), query: { text: 'TeLEMetry' } }).html() + ).not.toBe(''); // Renders something. + // I can't check against snapshot because of https://github.com/facebook/jest/issues/8618 + // expect(component).toMatchSnapshot(); + + // It should also render if there is no query at all. + expect(component.setProps({ ...component.props(), query: {} }).html()).not.toBe(''); + expect(onQueryMatchChange).toHaveBeenCalledWith(true); + + // Should only be called once because the second time does not change the result + expect(onQueryMatchChange).toHaveBeenCalledTimes(1); + } finally { + component.unmount(); + } + }); + + it('renders null because allowChangingOptInStatus is false', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: false, + optIn: true, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + + ); + try { + expect(component).toMatchSnapshot(); + component.setProps({ ...component.props(), query: { text: 'TeLEMetry' } }); + expect(onQueryMatchChange).toHaveBeenCalledWith(false); + } finally { + component.unmount(); + } + }); + + it('shows the OptInExampleFlyout', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + + ); + try { + const toggleExampleComponent = component.find('p > EuiLink[onClick]'); + const updatedView = toggleExampleComponent.simulate('click'); + updatedView.find('OptInExampleFlyout'); + updatedView.simulate('close'); + } finally { + component.unmount(); + } + }); + + it('toggles the OptIn button', async () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + + ); + try { + const toggleOptInComponent = component.find('Field'); + await expect( + toggleOptInComponent.prop('handleChange')() + ).resolves.toBe(true); + expect((component.state() as any).enabled).toBe(true); + await expect( + toggleOptInComponent.prop('handleChange')() + ).resolves.toBe(true); + expect((component.state() as any).enabled).toBe(false); + telemetryService.setOptIn = jest.fn().mockRejectedValue(Error('test-error')); + await expect( + toggleOptInComponent.prop('handleChange')() + ).rejects.toStrictEqual(Error('test-error')); + } finally { + component.unmount(); + } + }); + + it('test the wrapper (for coverage purposes)', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: false, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + const Wrapper = telemetryManagementSectionWrapper(telemetryService); + expect( + shallowWithIntl( + + ).html() + ).toMatchSnapshot(); + }); +}); 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 26e075b666593e..361c5ff719c54e 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 @@ -69,7 +69,9 @@ export class TelemetryManagementSection extends Component { const { query } = nextProps; const searchTerm = (query.text || '').toLowerCase(); - const searchTermMatches = SEARCH_TERMS.some(term => term.indexOf(searchTerm) >= 0); + const searchTermMatches = + this.props.telemetryService.getCanChangeOptInStatus() && + SEARCH_TERMS.some(term => term.indexOf(searchTerm) >= 0); if (searchTermMatches !== this.state.queryMatches) { this.setState( diff --git a/src/plugins/vis_type_table/public/get_inner_angular.ts b/src/plugins/vis_type_table/public/get_inner_angular.ts index d69b9bba31b032..e8404f918d6092 100644 --- a/src/plugins/vis_type_table/public/get_inner_angular.ts +++ b/src/plugins/vis_type_table/public/get_inner_angular.ts @@ -21,6 +21,8 @@ // these are necessary to bootstrap the local angular. // They can stay even after NP cutover import angular from 'angular'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import 'angular-recursion'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public'; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js index 7075e86eb56bf1..9fdb8ccc919b79 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor.js @@ -240,3 +240,7 @@ VisEditor.propTypes = { timeRange: PropTypes.object, appState: PropTypes.object, }; + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { VisEditor as default }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx new file mode 100644 index 00000000000000..d81bd95d8d7710 --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_editor_lazy.tsx @@ -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 React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +// @ts-ignore +const VisEditorComponent = lazy(() => import('./vis_editor')); + +export const VisEditor = (props: any) => ( + }> + + +); diff --git a/src/plugins/vis_type_timeseries/public/application/editor_controller.js b/src/plugins/vis_type_timeseries/public/application/editor_controller.js index af50d3a06d1fc6..f21b5f947bca79 100644 --- a/src/plugins/vis_type_timeseries/public/application/editor_controller.js +++ b/src/plugins/vis_type_timeseries/public/application/editor_controller.js @@ -21,7 +21,7 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { fetchIndexPatternFields } from './lib/fetch_fields'; import { getSavedObjectsClient, getUISettings, getI18n } from '../services'; -import { VisEditor } from './components/vis_editor'; +import { VisEditor } from './components/vis_editor_lazy'; export class EditorController { constructor(el, vis, eventEmitter, embeddableHandler) { diff --git a/src/plugins/vis_type_timeseries/public/metrics_fn.ts b/src/plugins/vis_type_timeseries/public/metrics_fn.ts index 008b13cce65659..b573225feaab1d 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_fn.ts +++ b/src/plugins/vis_type_timeseries/public/metrics_fn.ts @@ -20,7 +20,6 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, KibanaContext, Render } from '../../expressions/public'; -import { PersistedState } from '../../visualizations/public'; // @ts-ignore import { metricsRequestHandler } from './request_handler'; @@ -76,6 +75,7 @@ export const createMetricsFn = (): ExpressionFunctionDefinition< const params = JSON.parse(args.params); const uiStateParams = JSON.parse(args.uiState); const savedObjectId = args.savedObjectId; + const { PersistedState } = await import('../../visualizations/public'); const uiState = new PersistedState(uiStateParams); const response = await metricsRequestHandler({ diff --git a/src/plugins/vis_type_timeseries/public/metrics_type.ts b/src/plugins/vis_type_timeseries/public/metrics_type.ts index c525ce7fa0b3bd..2b0734ceb4d4d5 100644 --- a/src/plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/plugins/vis_type_timeseries/public/metrics_type.ts @@ -25,6 +25,7 @@ import { EditorController } from './application'; // @ts-ignore import { PANEL_TYPES } from '../common/panel_types'; import { defaultFeedbackMessage } from '../../kibana_utils/public'; +import { VisEditor } from './application/components/vis_editor_lazy'; export const metricsVisDefinition = { name: 'metrics', @@ -69,7 +70,7 @@ export const metricsVisDefinition = { show_legend: 1, show_grid: 1, }, - component: require('./application/components/vis_editor').VisEditor, + component: VisEditor, }, editor: EditorController, options: { diff --git a/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts index 34922976f22ff5..1e5508b44ee0ec 100644 --- a/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts +++ b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts @@ -20,7 +20,7 @@ import { flow } from 'lodash'; import { SavedObjectMigrationFn, SavedObjectsType } from 'kibana/server'; -const resetCount: SavedObjectMigrationFn = doc => ({ +const resetCount: SavedObjectMigrationFn = doc => ({ ...doc, attributes: { ...doc.attributes, diff --git a/src/plugins/vis_type_vega/public/plugin.ts b/src/plugins/vis_type_vega/public/plugin.ts index c312705194cde2..b52dcfbd914f9a 100644 --- a/src/plugins/vis_type_vega/public/plugin.ts +++ b/src/plugins/vis_type_vega/public/plugin.ts @@ -26,14 +26,14 @@ import { setSavedObjects, setInjectedVars, setUISettings, + setKibanaMapFactory, } from './services'; import { createVegaFn } from './vega_fn'; import { createVegaTypeDefinition } from './vega_type'; -import { IServiceSettings } from '../../maps_legacy/public'; -import { ConfigSchema } from '../config'; - +import { getKibanaMapFactoryProvider, IServiceSettings } from '../../maps_legacy/public'; import './index.scss'; +import { ConfigSchema } from '../config'; /** @internal */ export interface VegaVisualizationDependencies { @@ -75,6 +75,7 @@ export class VegaPlugin implements Plugin, void> { emsTileLayerId: core.injectedMetadata.getInjectedVar('emsTileLayerId', true), }); setUISettings(core.uiSettings); + setKibanaMapFactory(getKibanaMapFactoryProvider(core)); const visualizationDependencies: Readonly = { core, diff --git a/src/plugins/vis_type_vega/public/services.ts b/src/plugins/vis_type_vega/public/services.ts index e349cfbdc0024d..f81f87d7ad2e17 100644 --- a/src/plugins/vis_type_vega/public/services.ts +++ b/src/plugins/vis_type_vega/public/services.ts @@ -27,6 +27,9 @@ export const [getData, setData] = createGetterSetter('Dat export const [getNotifications, setNotifications] = createGetterSetter( 'Notifications' ); +export const [getKibanaMapFactory, setKibanaMapFactory] = createGetterSetter( + 'KibanaMapFactory' +); export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js index bd6652a597203b..895d496a896aa3 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_map_view.js @@ -21,13 +21,11 @@ import * as vega from 'vega-lib'; import { i18n } from '@kbn/i18n'; import { VegaBaseView } from './vega_base_view'; import { VegaMapLayer } from './vega_map_layer'; -import { KibanaMap } from '../../../maps_legacy/public'; -import { getEmsTileLayerId, getUISettings } from '../services'; +import { getEmsTileLayerId, getUISettings, getKibanaMapFactory } from '../services'; export class VegaMapView extends VegaBaseView { - constructor(opts, services) { + constructor(opts) { super(opts); - this.services = services; } async _initViewCustomizations() { @@ -107,18 +105,14 @@ export class VegaMapView extends VegaBaseView { // maxBounds = L.latLngBounds(L.latLng(b[1], b[0]), L.latLng(b[3], b[2])); // } - this._kibanaMap = new KibanaMap( - this._$container.get(0), - { - zoom, - minZoom, - maxZoom, - center: [mapConfig.latitude, mapConfig.longitude], - zoomControl: mapConfig.zoomControl, - scrollWheelZoom: mapConfig.scrollWheelZoom, - }, - this.services - ); + this._kibanaMap = getKibanaMapFactory()(this._$container.get(0), { + zoom, + minZoom, + maxZoom, + center: [mapConfig.latitude, mapConfig.longitude], + zoomControl: mapConfig.zoomControl, + scrollWheelZoom: mapConfig.scrollWheelZoom, + }); if (baseMapOpts) { this._kibanaMap.setBaseLayer({ diff --git a/src/plugins/vis_type_vislib/kibana.json b/src/plugins/vis_type_vislib/kibana.json new file mode 100644 index 00000000000000..5b3088b399ebff --- /dev/null +++ b/src/plugins/vis_type_vislib/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "visTypeVislib", + "version": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["charts", "data", "expressions", "visualizations"], + "optionalPlugins": ["visTypeXy"] +} diff --git a/src/legacy/core_plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap b/src/plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap rename to src/plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/area.ts b/src/plugins/vis_type_vislib/public/area.ts similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/area.ts rename to src/plugins/vis_type_vislib/public/area.ts index 8a196da64fc4bc..c42962ad50a4b0 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/area.ts +++ b/src/plugins/vis_type_vislib/public/area.ts @@ -23,8 +23,8 @@ import { palettes } from '@elastic/eui/lib/services'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; import { Positions, ChartTypes, @@ -39,7 +39,7 @@ import { import { getAreaOptionTabs, countLabel } from './utils/common_config'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; -import { Rotates } from '../../../../plugins/charts/public'; +import { Rotates } from '../../charts/public'; export const createAreaVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'area', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/index.ts b/src/plugins/vis_type_vislib/public/components/common/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/common/index.ts rename to src/plugins/vis_type_vislib/public/components/common/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx b/src/plugins/vis_type_vislib/public/components/common/truncate_labels.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx rename to src/plugins/vis_type_vislib/public/components/common/truncate_labels.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx b/src/plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx rename to src/plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/index.ts b/src/plugins/vis_type_vislib/public/components/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/index.ts rename to src/plugins/vis_type_vislib/public/components/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx b/src/plugins/vis_type_vislib/public/components/options/gauge/index.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx rename to src/plugins/vis_type_vislib/public/components/options/gauge/index.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx index 3fca9dc8adc08e..0bd5694f710213 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx @@ -22,8 +22,8 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SwitchOption, TextInputOption } from '../../../../../../../plugins/charts/public'; -import { GaugeOptionsInternalProps } from '.'; +import { SwitchOption, TextInputOption } from '../../../../../charts/public'; +import { GaugeOptionsInternalProps } from '../gauge'; function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInternalProps) { return ( diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx index 433cc4edeb47b3..c297fb08e4b68b 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx @@ -29,8 +29,8 @@ import { SetColorRangeValue, SwitchOption, ColorSchemas, -} from '../../../../../../../plugins/charts/public'; -import { GaugeOptionsInternalProps } from '.'; +} from '../../../../../charts/public'; +import { GaugeOptionsInternalProps } from '../gauge'; import { Gauge } from '../../../gauge'; function RangesPanel({ diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx similarity index 91% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx index 48711de7d171af..b299b2e86ca403 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx @@ -22,9 +22,9 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption } from '../../../../../../../plugins/charts/public'; -import { GaugeOptionsInternalProps } from '.'; -import { AggGroupNames } from '../../../../../../../plugins/data/public'; +import { SelectOption } from '../../../../../charts/public'; +import { GaugeOptionsInternalProps } from '../gauge'; +import { AggGroupNames } from '../../../../../data/public'; function StylePanel({ aggs, setGaugeValue, stateParams, vis }: GaugeOptionsInternalProps) { const diasableAlignment = diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/components/options/heatmap/index.tsx similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx rename to src/plugins/vis_type_vislib/public/components/options/heatmap/index.tsx index dc207ad89286f2..7a89496d9441e8 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/heatmap/index.tsx @@ -33,7 +33,7 @@ import { SwitchOption, SetColorSchemaOptionsValue, SetColorRangeValue, -} from '../../../../../../../plugins/charts/public'; +} from '../../../../../charts/public'; import { HeatmapVisParams } from '../../../heatmap'; import { ValueAxis } from '../../../types'; import { LabelsPanel } from './labels_panel'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx index 3d1629740df2c8..8d5f529ce0fc76 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx @@ -26,7 +26,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { ValueAxis } from '../../../types'; import { HeatmapVisParams } from '../../../heatmap'; -import { SwitchOption } from '../../../../../../../plugins/charts/public'; +import { SwitchOption } from '../../../../../charts/public'; const VERTICAL_ROTATION = 270; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/index.ts b/src/plugins/vis_type_vislib/public/components/options/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/index.ts rename to src/plugins/vis_type_vislib/public/components/options/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx index 91cdcd0f456b15..44ed0d5aeddabd 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx @@ -25,8 +25,6 @@ import { Positions } from '../../../utils/collections'; import { LabelOptions } from './label_options'; import { categoryAxis, vis } from './mocks'; -jest.mock('ui/new_platform'); - describe('CategoryAxisPanel component', () => { let setCategoryAxis: jest.Mock; let onPositionChanged: jest.Mock; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx index 246c20a14807c5..468fb1f8c315af 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx @@ -25,7 +25,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { Axis } from '../../../types'; -import { SelectOption, SwitchOption } from '../../../../../../../plugins/charts/public'; +import { SelectOption, SwitchOption } from '../../../../../charts/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { Positions } from '../../../utils/collections'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx index c913fd4f35713d..e2d4a0db9f1f9c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx @@ -25,8 +25,6 @@ import { LineOptions } from './line_options'; import { ChartTypes, ChartModes } from '../../../utils/collections'; import { valueAxis, seriesParam, vis } from './mocks'; -jest.mock('ui/new_platform'); - describe('ChartOptions component', () => { let setParamByIndex: jest.Mock; let changeValueAxis: jest.Mock; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx index 89aab3a19c5897..623a8d1f348e93 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx @@ -22,12 +22,12 @@ import React, { useMemo, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { SeriesParam, ValueAxis } from '../../../types'; import { ChartTypes } from '../../../utils/collections'; -import { SelectOption } from '../../../../../../../plugins/charts/public'; +import { SelectOption } from '../../../../../charts/public'; import { LineOptions } from './line_options'; -import { SetParamByIndex, ChangeValueAxis } from './'; +import { SetParamByIndex, ChangeValueAxis } from '.'; export type SetChart = (paramName: T, value: SeriesParam[T]) => void; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx index a93ee454a7afd1..4798c67928f7f6 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx @@ -28,8 +28,6 @@ const DEFAULT_Y_EXTENTS = 'defaultYExtents'; const SCALE = 'scale'; const SET_Y_EXTENTS = 'setYExtents'; -jest.mock('ui/new_platform'); - describe('CustomExtentsOptions component', () => { let setValueAxis: jest.Mock; let setValueAxisScale: jest.Mock; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx index a3a97df9e35ae2..634d6b3f0641cd 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx @@ -21,7 +21,7 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { ValueAxis } from '../../../types'; -import { NumberInputOption, SwitchOption } from '../../../../../../../plugins/charts/public'; +import { NumberInputOption, SwitchOption } from '../../../../../charts/public'; import { YExtents } from './y_extents'; import { SetScale } from './value_axis_options'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx index 48fcbdf8f9082e..f500b7e58e9fdb 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx @@ -23,8 +23,6 @@ import { LabelOptions, LabelOptionsProps } from './label_options'; import { TruncateLabelsOption } from '../../common'; import { valueAxis } from './mocks'; -jest.mock('ui/new_platform'); - const FILTER = 'filter'; const ROTATE = 'rotate'; const DISABLED = 'disabled'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx index bc687e56646f63..14e1da6ebcc701 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx @@ -26,7 +26,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { Axis } from '../../../types'; import { TruncateLabelsOption } from '../../common'; import { getRotateOptions } from '../../../utils/collections'; -import { SelectOption, SwitchOption } from '../../../../../../../plugins/charts/public'; +import { SelectOption, SwitchOption } from '../../../../../charts/public'; export type SetAxisLabel = ( paramName: T, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx index 5354bc9c033e6e..e90c96146ec2cd 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx @@ -20,11 +20,9 @@ import React from 'react'; import { shallow } from 'enzyme'; import { LineOptions, LineOptionsParams } from './line_options'; -import { NumberInputOption } from '../../../../../../../plugins/charts/public'; +import { NumberInputOption } from '../../../../../charts/public'; import { seriesParam, vis } from './mocks'; -jest.mock('ui/new_platform'); - const LINE_WIDTH = 'lineWidth'; const DRAW_LINES = 'drawLinesBetweenPoints'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx index 76f95bd93caf8c..4b0cce94267f12 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx @@ -22,13 +22,9 @@ import React, { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { SeriesParam } from '../../../types'; -import { - NumberInputOption, - SelectOption, - SwitchOption, -} from '../../../../../../../plugins/charts/public'; +import { NumberInputOption, SelectOption, SwitchOption } from '../../../../../charts/public'; import { SetChart } from './chart_options'; export interface LineOptionsParams { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts index a296281375daca..277fcf0cdbc3de 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { Axis, ValueAxis, SeriesParam } from '../../../types'; import { ChartTypes, @@ -31,7 +31,7 @@ import { getPositions, getInterpolationModes, } from '../../../utils/collections'; -import { Style } from '../../../../../../../plugins/charts/public'; +import { Style } from '../../../../../charts/public'; const defaultValueAxisId = 'ValueAxis-1'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx index 22a726b53363b0..27c423860972c0 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx @@ -23,10 +23,10 @@ import { EuiPanel, EuiTitle, EuiSpacer, EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { ValueAxis, SeriesParam } from '../../../types'; import { ChartOptions } from './chart_options'; -import { SetParamByIndex, ChangeValueAxis } from './'; +import { SetParamByIndex, ChangeValueAxis } from '.'; export interface SeriesPanelProps { changeValueAxis: ChangeValueAxis; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx index 141273fa6bc3f8..2f7dd4071b52cd 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx @@ -25,8 +25,6 @@ import { Positions } from '../../../utils/collections'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { valueAxis, seriesParam, vis } from './mocks'; -jest.mock('ui/new_platform'); - describe('ValueAxesPanel component', () => { let setParamByIndex: jest.Mock; let onValueAxisPositionChanged: jest.Mock; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx index 912c3b904b110b..b17f67b81d2b04 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx @@ -31,10 +31,10 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { SeriesParam, ValueAxis } from '../../../types'; import { ValueAxisOptions } from './value_axis_options'; -import { SetParamByIndex } from './'; +import { SetParamByIndex } from '.'; export interface ValueAxesPanelProps { isCategoryAxisHorizontal: boolean; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx index 876a6917ee0b4c..1977bdba6eadfc 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx @@ -21,13 +21,11 @@ import React from 'react'; import { shallow } from 'enzyme'; import { ValueAxisOptions, ValueAxisOptionsParams } from './value_axis_options'; import { ValueAxis } from '../../../types'; -import { TextInputOption } from '../../../../../../../plugins/charts/public'; +import { TextInputOption } from '../../../../../charts/public'; import { LabelOptions } from './label_options'; import { ScaleTypes, Positions } from '../../../utils/collections'; import { valueAxis, vis } from './mocks'; -jest.mock('ui/new_platform'); - const POSITION = 'position'; interface PositionOption { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx index 1b89a766d05913..52962fe813b446 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx @@ -21,18 +21,14 @@ import React, { useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiAccordion, EuiHorizontalRule } from '@elastic/eui'; -import { Vis } from '../../../../../../../plugins/visualizations/public'; +import { Vis } from '../../../../../visualizations/public'; import { ValueAxis } from '../../../types'; import { Positions } from '../../../utils/collections'; -import { - SelectOption, - SwitchOption, - TextInputOption, -} from '../../../../../../../plugins/charts/public'; +import { SelectOption, SwitchOption, TextInputOption } from '../../../../../charts/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { CustomExtentsOptions } from './custom_extents_options'; import { isAxisHorizontal } from './utils'; -import { SetParamByIndex } from './'; +import { SetParamByIndex } from '.'; export type SetScale = ( paramName: T, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx index b5ed475ca8e319..3bacb0be34d135 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx @@ -21,9 +21,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { YExtents, YExtentsProps } from './y_extents'; import { ScaleTypes } from '../../../utils/collections'; -import { NumberInputOption } from '../../../../../../../plugins/charts/public'; - -jest.mock('ui/new_platform'); +import { NumberInputOption } from '../../../../../charts/public'; describe('YExtents component', () => { let setMultipleValidity: jest.Mock; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx rename to src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx index faeb6069b51264..c2aa917dd3a6f6 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { Scale } from '../../../types'; import { ScaleTypes } from '../../../utils/collections'; -import { NumberInputOption } from '../../../../../../../plugins/charts/public'; +import { NumberInputOption } from '../../../../../charts/public'; import { SetScale } from './value_axis_options'; const rangeError = i18n.translate('visTypeVislib.controls.pointSeries.valueAxes.minErrorMessage', { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx b/src/plugins/vis_type_vislib/public/components/options/pie.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx rename to src/plugins/vis_type_vislib/public/components/options/pie.tsx index f6be9cd0bd8fed..54ba307982967e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/pie.tsx @@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { TruncateLabelsOption } from '../common'; -import { BasicOptions, SwitchOption } from '../../../../../../plugins/charts/public'; +import { BasicOptions, SwitchOption } from '../../../../charts/public'; import { PieVisParams } from '../../pie'; function PieOptions(props: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx index 392d180d2c5f26..0126dce37c9f24 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { SelectOption, SwitchOption } from '../../../../../../../plugins/charts/public'; +import { SelectOption, SwitchOption } from '../../../../../charts/public'; import { BasicVislibParams, ValueAxis } from '../../../types'; function GridPanel({ stateParams, setValue, hasHistogramAgg }: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/index.ts b/src/plugins/vis_type_vislib/public/components/options/point_series/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/index.ts rename to src/plugins/vis_type_vislib/public/components/options/point_series/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx b/src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx rename to src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx index 903c1917751d9e..60458b1f5c41f0 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ValidationVisOptionsProps } from '../../common'; -import { BasicOptions, SwitchOption } from '../../../../../../../plugins/charts/public'; +import { BasicOptions, SwitchOption } from '../../../../../charts/public'; import { GridPanel } from './grid_panel'; import { ThresholdPanel } from './threshold_panel'; import { BasicVislibParams } from '../../../types'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx b/src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx rename to src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx index 12f058ec7dd1fc..08231803007563 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx +++ b/src/plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx @@ -26,7 +26,7 @@ import { SelectOption, SwitchOption, RequiredNumberInputOption, -} from '../../../../../../../plugins/charts/public'; +} from '../../../../../charts/public'; import { BasicVislibParams } from '../../../types'; function ThresholdPanel({ diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_normal.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_normal.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_percentage.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_config_percentage.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_d3.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_d3.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_data_point.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_bar_chart_data_point.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_config.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_config.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_d3.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_d3.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json b/src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_data_point.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json rename to src/plugins/vis_type_vislib/public/fixtures/dispatch_heatmap_data_point.json diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns.js new file mode 100644 index 00000000000000..ff8538021d2756 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_columns.js @@ -0,0 +1,319 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + columns: [ + { + label: '200: response', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1415826608440, + max: 1415827508440, + }, + yAxisLabel: 'Count of documents', + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + series: [ + { + label: 'Count', + values: [ + { + x: 1415826600000, + y: 4, + }, + { + x: 1415826630000, + y: 8, + }, + { + x: 1415826660000, + y: 7, + }, + { + x: 1415826690000, + y: 5, + }, + { + x: 1415826720000, + y: 5, + }, + { + x: 1415826750000, + y: 4, + }, + { + x: 1415826780000, + y: 10, + }, + { + x: 1415826810000, + y: 7, + }, + { + x: 1415826840000, + y: 9, + }, + { + x: 1415826870000, + y: 8, + }, + { + x: 1415826900000, + y: 9, + }, + { + x: 1415826930000, + y: 8, + }, + { + x: 1415826960000, + y: 3, + }, + { + x: 1415826990000, + y: 9, + }, + { + x: 1415827020000, + y: 6, + }, + { + x: 1415827050000, + y: 8, + }, + { + x: 1415827080000, + y: 7, + }, + { + x: 1415827110000, + y: 4, + }, + { + x: 1415827140000, + y: 6, + }, + { + x: 1415827170000, + y: 10, + }, + { + x: 1415827200000, + y: 2, + }, + { + x: 1415827230000, + y: 8, + }, + { + x: 1415827260000, + y: 5, + }, + { + x: 1415827290000, + y: 6, + }, + { + x: 1415827320000, + y: 6, + }, + { + x: 1415827350000, + y: 10, + }, + { + x: 1415827380000, + y: 6, + }, + { + x: 1415827410000, + y: 6, + }, + { + x: 1415827440000, + y: 12, + }, + { + x: 1415827470000, + y: 9, + }, + { + x: 1415827500000, + y: 1, + }, + ], + }, + ], + }, + { + label: '503: response', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1415826608440, + max: 1415827508440, + }, + yAxisLabel: 'Count of documents', + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + series: [ + { + label: 'Count', + values: [ + { + x: 1415826630000, + y: 1, + }, + { + x: 1415826660000, + y: 1, + }, + { + x: 1415826720000, + y: 1, + }, + { + x: 1415826780000, + y: 1, + }, + { + x: 1415826900000, + y: 1, + }, + { + x: 1415827020000, + y: 1, + }, + { + x: 1415827080000, + y: 1, + }, + { + x: 1415827110000, + y: 2, + }, + ], + }, + ], + }, + { + label: '404: response', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1415826608440, + max: 1415827508440, + }, + yAxisLabel: 'Count of documents', + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + series: [ + { + label: 'Count', + values: [ + { + x: 1415826660000, + y: 1, + }, + { + x: 1415826720000, + y: 1, + }, + { + x: 1415826810000, + y: 1, + }, + { + x: 1415826960000, + y: 1, + }, + { + x: 1415827050000, + y: 1, + }, + { + x: 1415827260000, + y: 1, + }, + { + x: 1415827380000, + y: 1, + }, + { + x: 1415827410000, + y: 1, + }, + ], + }, + ], + }, + ], + xAxisOrderedValues: [ + 1415826600000, + 1415826630000, + 1415826660000, + 1415826690000, + 1415826720000, + 1415826750000, + 1415826780000, + 1415826810000, + 1415826840000, + 1415826870000, + 1415826900000, + 1415826930000, + 1415826960000, + 1415826990000, + 1415827020000, + 1415827050000, + 1415827080000, + 1415827110000, + 1415827140000, + 1415827170000, + 1415827200000, + 1415827230000, + 1415827260000, + 1415827290000, + 1415827320000, + 1415827350000, + 1415827380000, + 1415827410000, + 1415827440000, + 1415827470000, + 1415827500000, + ], + hits: 225, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows.js new file mode 100644 index 00000000000000..6367197acdecea --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows.js @@ -0,0 +1,1697 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + rows: [ + { + label: '0.0-1000.0: bytes', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1415826260456, + max: 1415827160456, + }, + yAxisLabel: 'Count of documents', + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + series: [ + { + label: 'jpg', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 1, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 1, + y0: 0, + }, + { + x: 1415826660000, + y: 0, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 1, + y0: 0, + }, + { + x: 1415826810000, + y: 0, + y0: 0, + }, + { + x: 1415826840000, + y: 0, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 1, + y0: 0, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 1, + y0: 0, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 1, + y0: 0, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'css', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 1, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 1, + }, + { + x: 1415826660000, + y: 0, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 1, + }, + { + x: 1415826810000, + y: 0, + y0: 0, + }, + { + x: 1415826840000, + y: 0, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 1, + y0: 0, + }, + { + x: 1415827110000, + y: 0, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'png', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 1, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 1, + }, + { + x: 1415826660000, + y: 0, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 1, + }, + { + x: 1415826810000, + y: 0, + y0: 0, + }, + { + x: 1415826840000, + y: 0, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 1, + }, + { + x: 1415827110000, + y: 1, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'php', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 1, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 1, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 1, + }, + { + x: 1415826660000, + y: 0, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 1, + }, + { + x: 1415826810000, + y: 0, + y0: 0, + }, + { + x: 1415826840000, + y: 0, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 1, + }, + { + x: 1415827110000, + y: 0, + y0: 2, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'gif', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 1, + }, + { + x: 1415826330000, + y: 0, + y0: 1, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 1, + y0: 0, + }, + { + x: 1415826480000, + y: 1, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 3, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 1, + }, + { + x: 1415826660000, + y: 1, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 1, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 1, + y0: 1, + }, + { + x: 1415826810000, + y: 0, + y0: 0, + }, + { + x: 1415826840000, + y: 0, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 1, + y0: 1, + }, + { + x: 1415827110000, + y: 1, + y0: 2, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + ], + }, + { + label: '1000.0-2000.0: bytes', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1415826260457, + max: 1415827160457, + }, + yAxisLabel: 'Count of documents', + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + series: [ + { + label: 'jpg', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 1, + y0: 0, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 0, + }, + { + x: 1415826660000, + y: 1, + y0: 0, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 0, + }, + { + x: 1415826810000, + y: 2, + y0: 0, + }, + { + x: 1415826840000, + y: 1, + y0: 0, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 1, + y0: 0, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 1, + y0: 0, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 1, + y0: 0, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'css', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 1, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 0, + }, + { + x: 1415826660000, + y: 0, + y0: 1, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 0, + }, + { + x: 1415826810000, + y: 0, + y0: 2, + }, + { + x: 1415826840000, + y: 0, + y0: 1, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 0, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'png', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 1, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 0, + }, + { + x: 1415826660000, + y: 0, + y0: 1, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 0, + }, + { + x: 1415826810000, + y: 0, + y0: 2, + }, + { + x: 1415826840000, + y: 0, + y0: 1, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 1, + y0: 0, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 0, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'php', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 1, + y0: 0, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 1, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 0, + }, + { + x: 1415826660000, + y: 0, + y0: 1, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 0, + }, + { + x: 1415826810000, + y: 0, + y0: 2, + }, + { + x: 1415826840000, + y: 0, + y0: 1, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 1, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 0, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + { + label: 'gif', + values: [ + { + x: 1415826240000, + y: 0, + y0: 0, + }, + { + x: 1415826270000, + y: 0, + y0: 0, + }, + { + x: 1415826300000, + y: 0, + y0: 0, + }, + { + x: 1415826330000, + y: 0, + y0: 0, + }, + { + x: 1415826360000, + y: 0, + y0: 0, + }, + { + x: 1415826390000, + y: 0, + y0: 0, + }, + { + x: 1415826420000, + y: 0, + y0: 0, + }, + { + x: 1415826450000, + y: 0, + y0: 1, + }, + { + x: 1415826480000, + y: 0, + y0: 0, + }, + { + x: 1415826510000, + y: 0, + y0: 0, + }, + { + x: 1415826540000, + y: 0, + y0: 0, + }, + { + x: 1415826570000, + y: 0, + y0: 1, + }, + { + x: 1415826600000, + y: 0, + y0: 0, + }, + { + x: 1415826630000, + y: 0, + y0: 0, + }, + { + x: 1415826660000, + y: 0, + y0: 1, + }, + { + x: 1415826690000, + y: 0, + y0: 0, + }, + { + x: 1415826720000, + y: 0, + y0: 0, + }, + { + x: 1415826750000, + y: 0, + y0: 0, + }, + { + x: 1415826780000, + y: 0, + y0: 0, + }, + { + x: 1415826810000, + y: 0, + y0: 2, + }, + { + x: 1415826840000, + y: 0, + y0: 1, + }, + { + x: 1415826870000, + y: 0, + y0: 0, + }, + { + x: 1415826900000, + y: 0, + y0: 1, + }, + { + x: 1415826930000, + y: 0, + y0: 0, + }, + { + x: 1415826960000, + y: 0, + y0: 0, + }, + { + x: 1415826990000, + y: 0, + y0: 1, + }, + { + x: 1415827020000, + y: 0, + y0: 1, + }, + { + x: 1415827050000, + y: 0, + y0: 0, + }, + { + x: 1415827080000, + y: 0, + y0: 0, + }, + { + x: 1415827110000, + y: 0, + y0: 1, + }, + { + x: 1415827140000, + y: 0, + y0: 0, + }, + ], + }, + ], + }, + ], + xAxisOrderedValues: [ + 1415826240000, + 1415826270000, + 1415826300000, + 1415826330000, + 1415826360000, + 1415826390000, + 1415826420000, + 1415826450000, + 1415826480000, + 1415826510000, + 1415826540000, + 1415826570000, + 1415826600000, + 1415826630000, + 1415826660000, + 1415826690000, + 1415826720000, + 1415826750000, + 1415826780000, + 1415826810000, + 1415826840000, + 1415826870000, + 1415826900000, + 1415826930000, + 1415826960000, + 1415826990000, + 1415827020000, + 1415827050000, + 1415827080000, + 1415827110000, + 1415827140000, + ], + hits: 236, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js new file mode 100644 index 00000000000000..ba0d8bf251c6fa --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_rows_series_with_holes.js @@ -0,0 +1,142 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export const rowsSeriesWithHoles = { + rows: [ + { + label: '', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + min: 1411761457636, + max: 1411762357636, + interval: 30000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 1411761450000, + y: 41, + }, + { + x: 1411761510000, + y: 22, + }, + { + x: 1411761540000, + y: 17, + }, + { + x: 1411761840000, + y: 20, + }, + { + x: 1411761870000, + y: 20, + }, + { + x: 1411761900000, + y: 21, + }, + { + x: 1411761930000, + y: 17, + }, + { + x: 1411761960000, + y: 20, + }, + { + x: 1411761990000, + y: 13, + }, + { + x: 1411762020000, + y: 14, + }, + { + x: 1411762050000, + y: 25, + }, + { + x: 1411762080000, + y: 17, + }, + { + x: 1411762110000, + y: 14, + }, + { + x: 1411762140000, + y: 22, + }, + { + x: 1411762170000, + y: 14, + }, + { + x: 1411762200000, + y: 19, + }, + { + x: 1411762320000, + y: 15, + }, + { + x: 1411762350000, + y: 4, + }, + ], + }, + ], + hits: 533, + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + xAxisOrderedValues: [ + 1411761450000, + 1411761510000, + 1411761540000, + 1411761840000, + 1411761870000, + 1411761900000, + 1411761930000, + 1411761960000, + 1411761990000, + 1411762020000, + 1411762050000, + 1411762080000, + 1411762110000, + 1411762140000, + 1411762170000, + 1411762200000, + 1411762320000, + 1411762350000, + ], +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series.js new file mode 100644 index 00000000000000..89e4f9a32cee16 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series.js @@ -0,0 +1,203 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + label: '', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + min: 1411761457636, + max: 1411762357636, + interval: 30000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 1411761450000, + y: 41, + }, + { + x: 1411761480000, + y: 18, + }, + { + x: 1411761510000, + y: 22, + }, + { + x: 1411761540000, + y: 17, + }, + { + x: 1411761570000, + y: 17, + }, + { + x: 1411761600000, + y: 21, + }, + { + x: 1411761630000, + y: 16, + }, + { + x: 1411761660000, + y: 17, + }, + { + x: 1411761690000, + y: 15, + }, + { + x: 1411761720000, + y: 19, + }, + { + x: 1411761750000, + y: 11, + }, + { + x: 1411761780000, + y: 13, + }, + { + x: 1411761810000, + y: 24, + }, + { + x: 1411761840000, + y: 20, + }, + { + x: 1411761870000, + y: 20, + }, + { + x: 1411761900000, + y: 21, + }, + { + x: 1411761930000, + y: 17, + }, + { + x: 1411761960000, + y: 20, + }, + { + x: 1411761990000, + y: 13, + }, + { + x: 1411762020000, + y: 14, + }, + { + x: 1411762050000, + y: 25, + }, + { + x: 1411762080000, + y: 17, + }, + { + x: 1411762110000, + y: 14, + }, + { + x: 1411762140000, + y: 22, + }, + { + x: 1411762170000, + y: 14, + }, + { + x: 1411762200000, + y: 19, + }, + { + x: 1411762230000, + y: 22, + }, + { + x: 1411762260000, + y: 17, + }, + { + x: 1411762290000, + y: 8, + }, + { + x: 1411762320000, + y: 15, + }, + { + x: 1411762350000, + y: 4, + }, + ], + }, + ], + hits: 533, + xAxisOrderedValues: [ + 1411761450000, + 1411761480000, + 1411761510000, + 1411761540000, + 1411761570000, + 1411761600000, + 1411761630000, + 1411761660000, + 1411761690000, + 1411761720000, + 1411761750000, + 1411761780000, + 1411761810000, + 1411761840000, + 1411761870000, + 1411761900000, + 1411761930000, + 1411761960000, + 1411761990000, + 1411762020000, + 1411762050000, + 1411762080000, + 1411762110000, + 1411762140000, + 1411762170000, + 1411762200000, + 1411762230000, + 1411762260000, + 1411762290000, + 1411762320000, + 1411762350000, + ], + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js new file mode 100644 index 00000000000000..85078a2ec15af2 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_monthly_interval.js @@ -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 moment from 'moment'; + +export const seriesMonthlyInterval = { + label: '', + xAxisLabel: '@timestamp per month', + ordered: { + date: true, + min: 1451631600000, + max: 1483254000000, + interval: 2678000000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 1451631600000, + y: 10220, + }, + { + x: 1454310000000, + y: 9997, + }, + { + x: 1456815600000, + y: 10792, + }, + { + x: 1459490400000, + y: 10262, + }, + { + x: 1462082400000, + y: 10080, + }, + { + x: 1464760800000, + y: 11161, + }, + { + x: 1467352800000, + y: 9933, + }, + { + x: 1470031200000, + y: 10342, + }, + { + x: 1472709600000, + y: 10887, + }, + { + x: 1475301600000, + y: 9666, + }, + { + x: 1477980000000, + y: 9556, + }, + { + x: 1480575600000, + y: 11644, + }, + ], + }, + ], + hits: 533, + xAxisOrderedValues: [ + 1451631600000, + 1454310000000, + 1456815600000, + 1459490400000, + 1462082400000, + 1464760800000, + 1467352800000, + 1470031200000, + 1472709600000, + 1475301600000, + 1477980000000, + 1480575600000, + ], + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg.js new file mode 100644 index 00000000000000..821c04685d22e5 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_neg.js @@ -0,0 +1,203 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + label: '', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + min: 1411761457636, + max: 1411762357636, + interval: 30000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 1411761450000, + y: -41, + }, + { + x: 1411761480000, + y: -18, + }, + { + x: 1411761510000, + y: -22, + }, + { + x: 1411761540000, + y: -17, + }, + { + x: 1411761570000, + y: -17, + }, + { + x: 1411761600000, + y: -21, + }, + { + x: 1411761630000, + y: -16, + }, + { + x: 1411761660000, + y: -17, + }, + { + x: 1411761690000, + y: -15, + }, + { + x: 1411761720000, + y: -19, + }, + { + x: 1411761750000, + y: -11, + }, + { + x: 1411761780000, + y: -13, + }, + { + x: 1411761810000, + y: -24, + }, + { + x: 1411761840000, + y: -20, + }, + { + x: 1411761870000, + y: -20, + }, + { + x: 1411761900000, + y: -21, + }, + { + x: 1411761930000, + y: -17, + }, + { + x: 1411761960000, + y: -20, + }, + { + x: 1411761990000, + y: -13, + }, + { + x: 1411762020000, + y: -14, + }, + { + x: 1411762050000, + y: -25, + }, + { + x: 1411762080000, + y: -17, + }, + { + x: 1411762110000, + y: -14, + }, + { + x: 1411762140000, + y: -22, + }, + { + x: 1411762170000, + y: -14, + }, + { + x: 1411762200000, + y: -19, + }, + { + x: 1411762230000, + y: -22, + }, + { + x: 1411762260000, + y: -17, + }, + { + x: 1411762290000, + y: -8, + }, + { + x: 1411762320000, + y: -15, + }, + { + x: 1411762350000, + y: -4, + }, + ], + }, + ], + hits: 533, + xAxisOrderedValues: [ + 1411761450000, + 1411761480000, + 1411761510000, + 1411761540000, + 1411761570000, + 1411761600000, + 1411761630000, + 1411761660000, + 1411761690000, + 1411761720000, + 1411761750000, + 1411761780000, + 1411761810000, + 1411761840000, + 1411761870000, + 1411761900000, + 1411761930000, + 1411761960000, + 1411761990000, + 1411762020000, + 1411762050000, + 1411762080000, + 1411762110000, + 1411762140000, + 1411762170000, + 1411762200000, + 1411762230000, + 1411762260000, + 1411762290000, + 1411762320000, + 1411762350000, + ], + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js new file mode 100644 index 00000000000000..65821ac58eb0db --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_series_pos_neg.js @@ -0,0 +1,203 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + label: '', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + min: 1411761457636, + max: 1411762357636, + interval: 30000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 1411761450000, + y: 41, + }, + { + x: 1411761480000, + y: 18, + }, + { + x: 1411761510000, + y: -22, + }, + { + x: 1411761540000, + y: -17, + }, + { + x: 1411761570000, + y: -17, + }, + { + x: 1411761600000, + y: -21, + }, + { + x: 1411761630000, + y: -16, + }, + { + x: 1411761660000, + y: 17, + }, + { + x: 1411761690000, + y: 15, + }, + { + x: 1411761720000, + y: 19, + }, + { + x: 1411761750000, + y: 11, + }, + { + x: 1411761780000, + y: -13, + }, + { + x: 1411761810000, + y: -24, + }, + { + x: 1411761840000, + y: -20, + }, + { + x: 1411761870000, + y: -20, + }, + { + x: 1411761900000, + y: -21, + }, + { + x: 1411761930000, + y: 17, + }, + { + x: 1411761960000, + y: 20, + }, + { + x: 1411761990000, + y: -13, + }, + { + x: 1411762020000, + y: -14, + }, + { + x: 1411762050000, + y: 25, + }, + { + x: 1411762080000, + y: -17, + }, + { + x: 1411762110000, + y: -14, + }, + { + x: 1411762140000, + y: -22, + }, + { + x: 1411762170000, + y: -14, + }, + { + x: 1411762200000, + y: 19, + }, + { + x: 1411762230000, + y: 22, + }, + { + x: 1411762260000, + y: 17, + }, + { + x: 1411762290000, + y: 8, + }, + { + x: 1411762320000, + y: -15, + }, + { + x: 1411762350000, + y: -4, + }, + ], + }, + ], + hits: 533, + xAxisOrderedValues: [ + 1411761450000, + 1411761480000, + 1411761510000, + 1411761540000, + 1411761570000, + 1411761600000, + 1411761630000, + 1411761660000, + 1411761690000, + 1411761720000, + 1411761750000, + 1411761780000, + 1411761810000, + 1411761840000, + 1411761870000, + 1411761900000, + 1411761930000, + 1411761960000, + 1411761990000, + 1411762020000, + 1411762050000, + 1411762080000, + 1411762110000, + 1411762140000, + 1411762170000, + 1411762200000, + 1411762230000, + 1411762260000, + 1411762290000, + 1411762320000, + 1411762350000, + ], + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js new file mode 100644 index 00000000000000..b6f731c9655d41 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/date_histogram/_stacked_series.js @@ -0,0 +1,1576 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + label: '', + xAxisLabel: '@timestamp per 10 min', + ordered: { + date: true, + min: 1413544140087, + max: 1413587340087, + interval: 600000, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'html', + values: [ + { + x: 1413543600000, + y: 140, + }, + { + x: 1413544200000, + y: 1388, + }, + { + x: 1413544800000, + y: 1308, + }, + { + x: 1413545400000, + y: 1356, + }, + { + x: 1413546000000, + y: 1314, + }, + { + x: 1413546600000, + y: 1343, + }, + { + x: 1413547200000, + y: 1353, + }, + { + x: 1413547800000, + y: 1353, + }, + { + x: 1413548400000, + y: 1334, + }, + { + x: 1413549000000, + y: 1433, + }, + { + x: 1413549600000, + y: 1331, + }, + { + x: 1413550200000, + y: 1349, + }, + { + x: 1413550800000, + y: 1323, + }, + { + x: 1413551400000, + y: 1203, + }, + { + x: 1413552000000, + y: 1231, + }, + { + x: 1413552600000, + y: 1227, + }, + { + x: 1413553200000, + y: 1187, + }, + { + x: 1413553800000, + y: 1119, + }, + { + x: 1413554400000, + y: 1159, + }, + { + x: 1413555000000, + y: 1117, + }, + { + x: 1413555600000, + y: 1152, + }, + { + x: 1413556200000, + y: 1057, + }, + { + x: 1413556800000, + y: 1009, + }, + { + x: 1413557400000, + y: 979, + }, + { + x: 1413558000000, + y: 975, + }, + { + x: 1413558600000, + y: 848, + }, + { + x: 1413559200000, + y: 873, + }, + { + x: 1413559800000, + y: 808, + }, + { + x: 1413560400000, + y: 784, + }, + { + x: 1413561000000, + y: 799, + }, + { + x: 1413561600000, + y: 684, + }, + { + x: 1413562200000, + y: 727, + }, + { + x: 1413562800000, + y: 621, + }, + { + x: 1413563400000, + y: 615, + }, + { + x: 1413564000000, + y: 569, + }, + { + x: 1413564600000, + y: 523, + }, + { + x: 1413565200000, + y: 474, + }, + { + x: 1413565800000, + y: 470, + }, + { + x: 1413566400000, + y: 466, + }, + { + x: 1413567000000, + y: 394, + }, + { + x: 1413567600000, + y: 404, + }, + { + x: 1413568200000, + y: 389, + }, + { + x: 1413568800000, + y: 312, + }, + { + x: 1413569400000, + y: 274, + }, + { + x: 1413570000000, + y: 285, + }, + { + x: 1413570600000, + y: 299, + }, + { + x: 1413571200000, + y: 207, + }, + { + x: 1413571800000, + y: 213, + }, + { + x: 1413572400000, + y: 119, + }, + { + x: 1413573600000, + y: 122, + }, + { + x: 1413574200000, + y: 169, + }, + { + x: 1413574800000, + y: 151, + }, + { + x: 1413575400000, + y: 152, + }, + { + x: 1413576000000, + y: 115, + }, + { + x: 1413576600000, + y: 117, + }, + { + x: 1413577200000, + y: 108, + }, + { + x: 1413577800000, + y: 100, + }, + { + x: 1413578400000, + y: 78, + }, + { + x: 1413579000000, + y: 88, + }, + { + x: 1413579600000, + y: 63, + }, + { + x: 1413580200000, + y: 58, + }, + { + x: 1413580800000, + y: 45, + }, + { + x: 1413581400000, + y: 57, + }, + { + x: 1413582000000, + y: 34, + }, + { + x: 1413582600000, + y: 41, + }, + { + x: 1413583200000, + y: 24, + }, + { + x: 1413583800000, + y: 27, + }, + { + x: 1413584400000, + y: 19, + }, + { + x: 1413585000000, + y: 24, + }, + { + x: 1413585600000, + y: 18, + }, + { + x: 1413586200000, + y: 17, + }, + { + x: 1413586800000, + y: 14, + }, + ], + }, + { + label: 'php', + values: [ + { + x: 1413543600000, + y: 90, + }, + { + x: 1413544200000, + y: 949, + }, + { + x: 1413544800000, + y: 1012, + }, + { + x: 1413545400000, + y: 1027, + }, + { + x: 1413546000000, + y: 1073, + }, + { + x: 1413546600000, + y: 992, + }, + { + x: 1413547200000, + y: 1005, + }, + { + x: 1413547800000, + y: 1014, + }, + { + x: 1413548400000, + y: 987, + }, + { + x: 1413549000000, + y: 982, + }, + { + x: 1413549600000, + y: 1086, + }, + { + x: 1413550200000, + y: 998, + }, + { + x: 1413550800000, + y: 935, + }, + { + x: 1413551400000, + y: 995, + }, + { + x: 1413552000000, + y: 926, + }, + { + x: 1413552600000, + y: 897, + }, + { + x: 1413553200000, + y: 873, + }, + { + x: 1413553800000, + y: 885, + }, + { + x: 1413554400000, + y: 859, + }, + { + x: 1413555000000, + y: 852, + }, + { + x: 1413555600000, + y: 779, + }, + { + x: 1413556200000, + y: 739, + }, + { + x: 1413556800000, + y: 783, + }, + { + x: 1413557400000, + y: 784, + }, + { + x: 1413558000000, + y: 687, + }, + { + x: 1413558600000, + y: 660, + }, + { + x: 1413559200000, + y: 672, + }, + { + x: 1413559800000, + y: 600, + }, + { + x: 1413560400000, + y: 659, + }, + { + x: 1413561000000, + y: 540, + }, + { + x: 1413561600000, + y: 539, + }, + { + x: 1413562200000, + y: 481, + }, + { + x: 1413562800000, + y: 498, + }, + { + x: 1413563400000, + y: 444, + }, + { + x: 1413564000000, + y: 452, + }, + { + x: 1413564600000, + y: 408, + }, + { + x: 1413565200000, + y: 358, + }, + { + x: 1413565800000, + y: 321, + }, + { + x: 1413566400000, + y: 305, + }, + { + x: 1413567000000, + y: 292, + }, + { + x: 1413567600000, + y: 289, + }, + { + x: 1413568200000, + y: 239, + }, + { + x: 1413568800000, + y: 256, + }, + { + x: 1413569400000, + y: 220, + }, + { + x: 1413570000000, + y: 205, + }, + { + x: 1413570600000, + y: 201, + }, + { + x: 1413571200000, + y: 183, + }, + { + x: 1413571800000, + y: 172, + }, + { + x: 1413572400000, + y: 73, + }, + { + x: 1413573600000, + y: 90, + }, + { + x: 1413574200000, + y: 130, + }, + { + x: 1413574800000, + y: 104, + }, + { + x: 1413575400000, + y: 108, + }, + { + x: 1413576000000, + y: 92, + }, + { + x: 1413576600000, + y: 79, + }, + { + x: 1413577200000, + y: 90, + }, + { + x: 1413577800000, + y: 72, + }, + { + x: 1413578400000, + y: 68, + }, + { + x: 1413579000000, + y: 52, + }, + { + x: 1413579600000, + y: 60, + }, + { + x: 1413580200000, + y: 51, + }, + { + x: 1413580800000, + y: 32, + }, + { + x: 1413581400000, + y: 37, + }, + { + x: 1413582000000, + y: 30, + }, + { + x: 1413582600000, + y: 29, + }, + { + x: 1413583200000, + y: 24, + }, + { + x: 1413583800000, + y: 16, + }, + { + x: 1413584400000, + y: 15, + }, + { + x: 1413585000000, + y: 15, + }, + { + x: 1413585600000, + y: 10, + }, + { + x: 1413586200000, + y: 9, + }, + { + x: 1413586800000, + y: 9, + }, + ], + }, + { + label: 'png', + values: [ + { + x: 1413543600000, + y: 44, + }, + { + x: 1413544200000, + y: 495, + }, + { + x: 1413544800000, + y: 489, + }, + { + x: 1413545400000, + y: 492, + }, + { + x: 1413546000000, + y: 556, + }, + { + x: 1413546600000, + y: 536, + }, + { + x: 1413547200000, + y: 511, + }, + { + x: 1413547800000, + y: 479, + }, + { + x: 1413548400000, + y: 544, + }, + { + x: 1413549000000, + y: 513, + }, + { + x: 1413549600000, + y: 501, + }, + { + x: 1413550200000, + y: 532, + }, + { + x: 1413550800000, + y: 440, + }, + { + x: 1413551400000, + y: 455, + }, + { + x: 1413552000000, + y: 455, + }, + { + x: 1413552600000, + y: 471, + }, + { + x: 1413553200000, + y: 428, + }, + { + x: 1413553800000, + y: 457, + }, + { + x: 1413554400000, + y: 450, + }, + { + x: 1413555000000, + y: 418, + }, + { + x: 1413555600000, + y: 398, + }, + { + x: 1413556200000, + y: 397, + }, + { + x: 1413556800000, + y: 359, + }, + { + x: 1413557400000, + y: 398, + }, + { + x: 1413558000000, + y: 339, + }, + { + x: 1413558600000, + y: 363, + }, + { + x: 1413559200000, + y: 297, + }, + { + x: 1413559800000, + y: 323, + }, + { + x: 1413560400000, + y: 302, + }, + { + x: 1413561000000, + y: 260, + }, + { + x: 1413561600000, + y: 276, + }, + { + x: 1413562200000, + y: 249, + }, + { + x: 1413562800000, + y: 248, + }, + { + x: 1413563400000, + y: 235, + }, + { + x: 1413564000000, + y: 234, + }, + { + x: 1413564600000, + y: 188, + }, + { + x: 1413565200000, + y: 192, + }, + { + x: 1413565800000, + y: 173, + }, + { + x: 1413566400000, + y: 160, + }, + { + x: 1413567000000, + y: 137, + }, + { + x: 1413567600000, + y: 158, + }, + { + x: 1413568200000, + y: 111, + }, + { + x: 1413568800000, + y: 145, + }, + { + x: 1413569400000, + y: 118, + }, + { + x: 1413570000000, + y: 104, + }, + { + x: 1413570600000, + y: 80, + }, + { + x: 1413571200000, + y: 79, + }, + { + x: 1413571800000, + y: 86, + }, + { + x: 1413572400000, + y: 47, + }, + { + x: 1413573600000, + y: 49, + }, + { + x: 1413574200000, + y: 68, + }, + { + x: 1413574800000, + y: 78, + }, + { + x: 1413575400000, + y: 77, + }, + { + x: 1413576000000, + y: 50, + }, + { + x: 1413576600000, + y: 51, + }, + { + x: 1413577200000, + y: 40, + }, + { + x: 1413577800000, + y: 42, + }, + { + x: 1413578400000, + y: 29, + }, + { + x: 1413579000000, + y: 24, + }, + { + x: 1413579600000, + y: 30, + }, + { + x: 1413580200000, + y: 18, + }, + { + x: 1413580800000, + y: 15, + }, + { + x: 1413581400000, + y: 19, + }, + { + x: 1413582000000, + y: 18, + }, + { + x: 1413582600000, + y: 13, + }, + { + x: 1413583200000, + y: 11, + }, + { + x: 1413583800000, + y: 11, + }, + { + x: 1413584400000, + y: 13, + }, + { + x: 1413585000000, + y: 9, + }, + { + x: 1413585600000, + y: 9, + }, + { + x: 1413586200000, + y: 9, + }, + { + x: 1413586800000, + y: 3, + }, + ], + }, + { + label: 'css', + values: [ + { + x: 1413543600000, + y: 35, + }, + { + x: 1413544200000, + y: 360, + }, + { + x: 1413544800000, + y: 343, + }, + { + x: 1413545400000, + y: 329, + }, + { + x: 1413546000000, + y: 345, + }, + { + x: 1413546600000, + y: 336, + }, + { + x: 1413547200000, + y: 330, + }, + { + x: 1413547800000, + y: 334, + }, + { + x: 1413548400000, + y: 326, + }, + { + x: 1413549000000, + y: 351, + }, + { + x: 1413549600000, + y: 334, + }, + { + x: 1413550200000, + y: 351, + }, + { + x: 1413550800000, + y: 337, + }, + { + x: 1413551400000, + y: 306, + }, + { + x: 1413552000000, + y: 346, + }, + { + x: 1413552600000, + y: 317, + }, + { + x: 1413553200000, + y: 298, + }, + { + x: 1413553800000, + y: 288, + }, + { + x: 1413554400000, + y: 283, + }, + { + x: 1413555000000, + y: 262, + }, + { + x: 1413555600000, + y: 245, + }, + { + x: 1413556200000, + y: 259, + }, + { + x: 1413556800000, + y: 267, + }, + { + x: 1413557400000, + y: 230, + }, + { + x: 1413558000000, + y: 218, + }, + { + x: 1413558600000, + y: 241, + }, + { + x: 1413559200000, + y: 213, + }, + { + x: 1413559800000, + y: 239, + }, + { + x: 1413560400000, + y: 208, + }, + { + x: 1413561000000, + y: 187, + }, + { + x: 1413561600000, + y: 166, + }, + { + x: 1413562200000, + y: 154, + }, + { + x: 1413562800000, + y: 184, + }, + { + x: 1413563400000, + y: 148, + }, + { + x: 1413564000000, + y: 153, + }, + { + x: 1413564600000, + y: 149, + }, + { + x: 1413565200000, + y: 102, + }, + { + x: 1413565800000, + y: 110, + }, + { + x: 1413566400000, + y: 121, + }, + { + x: 1413567000000, + y: 120, + }, + { + x: 1413567600000, + y: 86, + }, + { + x: 1413568200000, + y: 96, + }, + { + x: 1413568800000, + y: 71, + }, + { + x: 1413569400000, + y: 92, + }, + { + x: 1413570000000, + y: 65, + }, + { + x: 1413570600000, + y: 54, + }, + { + x: 1413571200000, + y: 68, + }, + { + x: 1413571800000, + y: 57, + }, + { + x: 1413572400000, + y: 33, + }, + { + x: 1413573600000, + y: 47, + }, + { + x: 1413574200000, + y: 42, + }, + { + x: 1413574800000, + y: 39, + }, + { + x: 1413575400000, + y: 25, + }, + { + x: 1413576000000, + y: 31, + }, + { + x: 1413576600000, + y: 37, + }, + { + x: 1413577200000, + y: 35, + }, + { + x: 1413577800000, + y: 19, + }, + { + x: 1413578400000, + y: 15, + }, + { + x: 1413579000000, + y: 21, + }, + { + x: 1413579600000, + y: 16, + }, + { + x: 1413580200000, + y: 18, + }, + { + x: 1413580800000, + y: 10, + }, + { + x: 1413581400000, + y: 13, + }, + { + x: 1413582000000, + y: 14, + }, + { + x: 1413582600000, + y: 11, + }, + { + x: 1413583200000, + y: 4, + }, + { + x: 1413583800000, + y: 6, + }, + { + x: 1413584400000, + y: 3, + }, + { + x: 1413585000000, + y: 6, + }, + { + x: 1413585600000, + y: 6, + }, + { + x: 1413586200000, + y: 2, + }, + { + x: 1413586800000, + y: 3, + }, + ], + }, + { + label: 'gif', + values: [ + { + x: 1413543600000, + y: 21, + }, + { + x: 1413544200000, + y: 191, + }, + { + x: 1413544800000, + y: 176, + }, + { + x: 1413545400000, + y: 166, + }, + { + x: 1413546000000, + y: 183, + }, + { + x: 1413546600000, + y: 170, + }, + { + x: 1413547200000, + y: 153, + }, + { + x: 1413547800000, + y: 202, + }, + { + x: 1413548400000, + y: 175, + }, + { + x: 1413549000000, + y: 161, + }, + { + x: 1413549600000, + y: 174, + }, + { + x: 1413550200000, + y: 167, + }, + { + x: 1413550800000, + y: 171, + }, + { + x: 1413551400000, + y: 176, + }, + { + x: 1413552000000, + y: 139, + }, + { + x: 1413552600000, + y: 145, + }, + { + x: 1413553200000, + y: 157, + }, + { + x: 1413553800000, + y: 148, + }, + { + x: 1413554400000, + y: 149, + }, + { + x: 1413555000000, + y: 135, + }, + { + x: 1413555600000, + y: 118, + }, + { + x: 1413556200000, + y: 142, + }, + { + x: 1413556800000, + y: 141, + }, + { + x: 1413557400000, + y: 146, + }, + { + x: 1413558000000, + y: 114, + }, + { + x: 1413558600000, + y: 115, + }, + { + x: 1413559200000, + y: 136, + }, + { + x: 1413559800000, + y: 106, + }, + { + x: 1413560400000, + y: 92, + }, + { + x: 1413561000000, + y: 97, + }, + { + x: 1413561600000, + y: 90, + }, + { + x: 1413562200000, + y: 69, + }, + { + x: 1413562800000, + y: 66, + }, + { + x: 1413563400000, + y: 93, + }, + { + x: 1413564000000, + y: 75, + }, + { + x: 1413564600000, + y: 68, + }, + { + x: 1413565200000, + y: 55, + }, + { + x: 1413565800000, + y: 73, + }, + { + x: 1413566400000, + y: 57, + }, + { + x: 1413567000000, + y: 48, + }, + { + x: 1413567600000, + y: 41, + }, + { + x: 1413568200000, + y: 39, + }, + { + x: 1413568800000, + y: 32, + }, + { + x: 1413569400000, + y: 33, + }, + { + x: 1413570000000, + y: 39, + }, + { + x: 1413570600000, + y: 35, + }, + { + x: 1413571200000, + y: 25, + }, + { + x: 1413571800000, + y: 28, + }, + { + x: 1413572400000, + y: 8, + }, + { + x: 1413573600000, + y: 13, + }, + { + x: 1413574200000, + y: 23, + }, + { + x: 1413574800000, + y: 19, + }, + { + x: 1413575400000, + y: 16, + }, + { + x: 1413576000000, + y: 22, + }, + { + x: 1413576600000, + y: 13, + }, + { + x: 1413577200000, + y: 21, + }, + { + x: 1413577800000, + y: 11, + }, + { + x: 1413578400000, + y: 12, + }, + { + x: 1413579000000, + y: 10, + }, + { + x: 1413579600000, + y: 7, + }, + { + x: 1413580200000, + y: 4, + }, + { + x: 1413580800000, + y: 5, + }, + { + x: 1413581400000, + y: 7, + }, + { + x: 1413582000000, + y: 9, + }, + { + x: 1413582600000, + y: 2, + }, + { + x: 1413583200000, + y: 2, + }, + { + x: 1413583800000, + y: 4, + }, + { + x: 1413584400000, + y: 6, + }, + { + x: 1413585600000, + y: 2, + }, + { + x: 1413586200000, + y: 4, + }, + { + x: 1413586800000, + y: 4, + }, + ], + }, + ], + hits: 108970, + xAxisOrderedValues: [ + 1413543600000, + 1413544200000, + 1413544800000, + 1413545400000, + 1413546000000, + 1413546600000, + 1413547200000, + 1413547800000, + 1413548400000, + 1413549000000, + 1413549600000, + 1413550200000, + 1413550800000, + 1413551400000, + 1413552000000, + 1413552600000, + 1413553200000, + 1413553800000, + 1413554400000, + 1413555000000, + 1413555600000, + 1413556200000, + 1413556800000, + 1413557400000, + 1413558000000, + 1413558600000, + 1413559200000, + 1413559800000, + 1413560400000, + 1413561000000, + 1413561600000, + 1413562200000, + 1413562800000, + 1413563400000, + 1413564000000, + 1413564600000, + 1413565200000, + 1413565800000, + 1413566400000, + 1413567000000, + 1413567600000, + 1413568200000, + 1413568800000, + 1413569400000, + 1413570000000, + 1413570600000, + 1413571200000, + 1413571800000, + 1413572400000, + 1413573600000, + 1413574200000, + 1413574800000, + 1413575400000, + 1413576000000, + 1413576600000, + 1413577200000, + 1413577800000, + 1413578400000, + 1413579000000, + 1413579600000, + 1413580200000, + 1413580800000, + 1413581400000, + 1413582000000, + 1413582600000, + 1413583200000, + 1413583800000, + 1413584400000, + 1413585000000, + 1413585600000, + 1413586200000, + 1413586800000, + ], + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_columns.js new file mode 100644 index 00000000000000..8144a996e3424b --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_columns.js @@ -0,0 +1,127 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + columns: [ + { + label: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1: agent.raw', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 10379, + }, + { + x: 'png', + y: 6395, + }, + ], + }, + ], + xAxisOrderedValues: ['css', 'png'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24: agent.raw', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 9253, + }, + { + x: 'png', + y: 5571, + }, + ], + }, + ], + xAxisOrderedValues: ['css', 'png'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: + 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322): agent.raw', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 7740, + }, + { + x: 'png', + y: 4697, + }, + ], + }, + ], + xAxisOrderedValues: ['css', 'png'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + hits: 171443, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_rows.js new file mode 100644 index 00000000000000..e783246972e4a6 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_rows.js @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + label: '200: response', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 25260, + }, + { + x: 'png', + y: 15311, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '404: response', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 1352, + }, + { + x: 'png', + y: 826, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '503: response', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 761, + }, + { + x: 'png', + y: 527, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + hits: 171443, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_series.js new file mode 100644 index 00000000000000..71ee039f989383 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/filters/_series.js @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import _ from 'lodash'; + +export default { + label: '', + xAxisLabel: 'filters', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'css', + y: 27374, + }, + { + x: 'html', + y: 0, + }, + { + x: 'png', + y: 16663, + }, + ], + }, + ], + hits: 171454, + xAxisOrderedValues: ['css', 'html', 'png'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_columns.js new file mode 100644 index 00000000000000..c1044160c0e7a6 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_columns.js @@ -0,0 +1,2918 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + columns: [ + { + title: 'Top 2 geo.dest: CN', + valueFormatter: _.identity, + geoJson: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 22.5], + }, + properties: { + value: 42, + geohash: 's', + center: [22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 's', + value: 's', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 42, + value: 42, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 0], + [45, 0], + [45, 45], + [0, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 22.5], + }, + properties: { + value: 31, + geohash: 'd', + center: [-67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'd', + value: 'd', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 31, + value: 31, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 0], + [-45, 0], + [-45, 45], + [-90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 22.5], + }, + properties: { + value: 30, + geohash: 'w', + center: [112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'w', + value: 'w', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 30, + value: 30, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 0], + [135, 0], + [135, 45], + [90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 22.5], + }, + properties: { + value: 25, + geohash: '9', + center: [-112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '9', + value: '9', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 25, + value: 25, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 0], + [-90, 0], + [-90, 45], + [-135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 22.5], + }, + properties: { + value: 22, + geohash: 't', + center: [67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 't', + value: 't', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 22, + value: 22, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 0], + [90, 0], + [90, 45], + [45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -22.5], + }, + properties: { + value: 22, + geohash: 'k', + center: [22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'k', + value: 'k', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 22, + value: 22, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -45], + [45, -45], + [45, 0], + [0, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -22.5], + }, + properties: { + value: 21, + geohash: '6', + center: [-67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '6', + value: '6', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 21, + value: 21, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -45], + [-45, -45], + [-45, 0], + [-90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 67.5], + }, + properties: { + value: 19, + geohash: 'u', + center: [22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'u', + value: 'u', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 19, + value: 19, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 45], + [45, 45], + [45, 90], + [0, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 67.5], + }, + properties: { + value: 18, + geohash: 'v', + center: [67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'v', + value: 'v', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 18, + value: 18, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 45], + [90, 45], + [90, 90], + [45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 67.5], + }, + properties: { + value: 11, + geohash: 'c', + center: [-112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'c', + value: 'c', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 11, + value: 11, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 45], + [-90, 45], + [-90, 90], + [-135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -22.5], + }, + properties: { + value: 10, + geohash: 'r', + center: [157.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'r', + value: 'r', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 10, + value: 10, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -45], + [180, -45], + [180, 0], + [135, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 67.5], + }, + properties: { + value: 9, + geohash: 'y', + center: [112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'y', + value: 'y', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 9, + value: 9, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 45], + [135, 45], + [135, 90], + [90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 22.5], + }, + properties: { + value: 9, + geohash: 'e', + center: [-22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'e', + value: 'e', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 9, + value: 9, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 0], + [0, 0], + [0, 45], + [-45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 67.5], + }, + properties: { + value: 8, + geohash: 'f', + center: [-67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'f', + value: 'f', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 8, + value: 8, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 45], + [-45, 45], + [-45, 90], + [-90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -22.5], + }, + properties: { + value: 8, + geohash: '7', + center: [-22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '7', + value: '7', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 8, + value: 8, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -45], + [0, -45], + [0, 0], + [-45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -22.5], + }, + properties: { + value: 6, + geohash: 'q', + center: [112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'q', + value: 'q', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, -45], + [135, -45], + [135, 0], + [90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 67.5], + }, + properties: { + value: 6, + geohash: 'g', + center: [-22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'g', + value: 'g', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 45], + [0, 45], + [0, 90], + [-45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 22.5], + }, + properties: { + value: 4, + geohash: 'x', + center: [157.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'x', + value: 'x', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 4, + value: 4, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 0], + [180, 0], + [180, 45], + [135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, 67.5], + }, + properties: { + value: 3, + geohash: 'b', + center: [-157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'b', + value: 'b', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 3, + value: 3, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-180, 45], + [-135, 45], + [-135, 90], + [-180, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 67.5], + }, + properties: { + value: 2, + geohash: 'z', + center: [157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'z', + value: 'z', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 45], + [180, 45], + [180, 90], + [135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, -22.5], + }, + properties: { + value: 1, + geohash: 'm', + center: [67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'm', + value: 'm', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -45], + [90, -45], + [90, 0], + [45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -67.5], + }, + properties: { + value: 1, + geohash: '5', + center: [-22.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '5', + value: '5', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -90], + [0, -90], + [0, -45], + [-45, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -67.5], + }, + properties: { + value: 1, + geohash: '4', + center: [-67.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '4', + value: '4', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -90], + [-45, -90], + [-45, -45], + [-90, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, -22.5], + }, + properties: { + value: 1, + geohash: '3', + center: [-112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '3', + value: '3', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, -45], + [-90, -45], + [-90, 0], + [-135, 0], + ], + }, + }, + ], + properties: { + min: 1, + max: 42, + }, + }, + }, + { + label: 'Top 2 geo.dest: IN', + valueFormatter: _.identity, + geoJson: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 22.5], + }, + properties: { + value: 32, + geohash: 's', + center: [22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 's', + value: 's', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 32, + value: 32, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 0], + [45, 0], + [45, 45], + [0, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -22.5], + }, + properties: { + value: 31, + geohash: '6', + center: [-67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '6', + value: '6', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 31, + value: 31, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -45], + [-45, -45], + [-45, 0], + [-90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 22.5], + }, + properties: { + value: 28, + geohash: 'd', + center: [-67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'd', + value: 'd', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 28, + value: 28, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 0], + [-45, 0], + [-45, 45], + [-90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 22.5], + }, + properties: { + value: 27, + geohash: 'w', + center: [112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'w', + value: 'w', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 27, + value: 27, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 0], + [135, 0], + [135, 45], + [90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 22.5], + }, + properties: { + value: 24, + geohash: 't', + center: [67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 't', + value: 't', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 24, + value: 24, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 0], + [90, 0], + [90, 45], + [45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -22.5], + }, + properties: { + value: 23, + geohash: 'k', + center: [22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'k', + value: 'k', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 23, + value: 23, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -45], + [45, -45], + [45, 0], + [0, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 67.5], + }, + properties: { + value: 17, + geohash: 'u', + center: [22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'u', + value: 'u', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 17, + value: 17, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 45], + [45, 45], + [45, 90], + [0, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 22.5], + }, + properties: { + value: 16, + geohash: '9', + center: [-112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '9', + value: '9', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 16, + value: 16, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 0], + [-90, 0], + [-90, 45], + [-135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 67.5], + }, + properties: { + value: 14, + geohash: 'v', + center: [67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'v', + value: 'v', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 14, + value: 14, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 45], + [90, 45], + [90, 90], + [45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 22.5], + }, + properties: { + value: 13, + geohash: 'e', + center: [-22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'e', + value: 'e', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 13, + value: 13, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 0], + [0, 0], + [0, 45], + [-45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -22.5], + }, + properties: { + value: 9, + geohash: 'r', + center: [157.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'r', + value: 'r', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 9, + value: 9, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -45], + [180, -45], + [180, 0], + [135, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 67.5], + }, + properties: { + value: 6, + geohash: 'y', + center: [112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'y', + value: 'y', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 45], + [135, 45], + [135, 90], + [90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 67.5], + }, + properties: { + value: 6, + geohash: 'g', + center: [-22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'g', + value: 'g', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 45], + [0, 45], + [0, 90], + [-45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 67.5], + }, + properties: { + value: 6, + geohash: 'f', + center: [-67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'f', + value: 'f', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 45], + [-45, 45], + [-45, 90], + [-90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 67.5], + }, + properties: { + value: 5, + geohash: 'c', + center: [-112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'c', + value: 'c', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 5, + value: 5, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 45], + [-90, 45], + [-90, 90], + [-135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, 67.5], + }, + properties: { + value: 4, + geohash: 'b', + center: [-157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'b', + value: 'b', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 4, + value: 4, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-180, 45], + [-135, 45], + [-135, 90], + [-180, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -22.5], + }, + properties: { + value: 3, + geohash: 'q', + center: [112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'q', + value: 'q', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 3, + value: 3, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, -45], + [135, -45], + [135, 0], + [90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -67.5], + }, + properties: { + value: 2, + geohash: '4', + center: [-67.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '4', + value: '4', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -90], + [-45, -90], + [-45, -45], + [-90, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 67.5], + }, + properties: { + value: 1, + geohash: 'z', + center: [157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'z', + value: 'z', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 45], + [180, 45], + [180, 90], + [135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 22.5], + }, + properties: { + value: 1, + geohash: 'x', + center: [157.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'x', + value: 'x', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 0], + [180, 0], + [180, 45], + [135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -67.5], + }, + properties: { + value: 1, + geohash: 'p', + center: [157.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'p', + value: 'p', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -90], + [180, -90], + [180, -45], + [135, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, -22.5], + }, + properties: { + value: 1, + geohash: 'm', + center: [67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: 'm', + value: 'm', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -45], + [90, -45], + [90, 0], + [45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -22.5], + }, + properties: { + value: 1, + geohash: '7', + center: [-22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: false, + }, + }, + type: 'bucket', + }, + key: '7', + value: '7', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -45], + [0, -45], + [0, 0], + [-45, 0], + ], + }, + }, + ], + properties: { + min: 1, + max: 32, + }, + }, + }, + ], +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_geo_json.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_geo_json.js new file mode 100644 index 00000000000000..a26dc9bd8b181c --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_geo_json.js @@ -0,0 +1,1326 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + valueFormatter: _.identity, + geohashGridAgg: { vis: { params: {} } }, + geoJson: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 22.5], + }, + properties: { + value: 608, + geohash: 's', + center: [22.5, 22.5], + aggConfigResult: { + $parent: { + key: 's', + value: 's', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 608, + value: 608, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 0], + [0, 45], + [45, 45], + [45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 22.5], + }, + properties: { + value: 522, + geohash: 'w', + center: [112.5, 22.5], + aggConfigResult: { + $parent: { + key: 'w', + value: 'w', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 522, + value: 522, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 90], + [0, 135], + [45, 135], + [45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -22.5], + }, + properties: { + value: 517, + geohash: '6', + center: [-67.5, -22.5], + aggConfigResult: { + $parent: { + key: '6', + value: '6', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 517, + value: 517, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -90], + [-45, -45], + [0, -45], + [0, -90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 22.5], + }, + properties: { + value: 446, + geohash: 'd', + center: [-67.5, 22.5], + aggConfigResult: { + $parent: { + key: 'd', + value: 'd', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 446, + value: 446, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -90], + [0, -45], + [45, -45], + [45, -90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 67.5], + }, + properties: { + value: 426, + geohash: 'u', + center: [22.5, 67.5], + aggConfigResult: { + $parent: { + key: 'u', + value: 'u', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 426, + value: 426, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 0], + [45, 45], + [90, 45], + [90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 22.5], + }, + properties: { + value: 413, + geohash: 't', + center: [67.5, 22.5], + aggConfigResult: { + $parent: { + key: 't', + value: 't', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 413, + value: 413, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 45], + [0, 90], + [45, 90], + [45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -22.5], + }, + properties: { + value: 362, + geohash: 'k', + center: [22.5, -22.5], + aggConfigResult: { + $parent: { + key: 'k', + value: 'k', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 362, + value: 362, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 0], + [-45, 45], + [0, 45], + [0, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 22.5], + }, + properties: { + value: 352, + geohash: '9', + center: [-112.5, 22.5], + aggConfigResult: { + $parent: { + key: '9', + value: '9', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 352, + value: 352, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -135], + [0, -90], + [45, -90], + [45, -135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 22.5], + }, + properties: { + value: 216, + geohash: 'e', + center: [-22.5, 22.5], + aggConfigResult: { + $parent: { + key: 'e', + value: 'e', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 216, + value: 216, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -45], + [0, 0], + [45, 0], + [45, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 67.5], + }, + properties: { + value: 183, + geohash: 'v', + center: [67.5, 67.5], + aggConfigResult: { + $parent: { + key: 'v', + value: 'v', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 183, + value: 183, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 45], + [45, 90], + [90, 90], + [90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -22.5], + }, + properties: { + value: 158, + geohash: 'r', + center: [157.5, -22.5], + aggConfigResult: { + $parent: { + key: 'r', + value: 'r', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 158, + value: 158, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 135], + [-45, 180], + [0, 180], + [0, 135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 67.5], + }, + properties: { + value: 139, + geohash: 'y', + center: [112.5, 67.5], + aggConfigResult: { + $parent: { + key: 'y', + value: 'y', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 139, + value: 139, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 90], + [45, 135], + [90, 135], + [90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 67.5], + }, + properties: { + value: 110, + geohash: 'c', + center: [-112.5, 67.5], + aggConfigResult: { + $parent: { + key: 'c', + value: 'c', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 110, + value: 110, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -135], + [45, -90], + [90, -90], + [90, -135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -22.5], + }, + properties: { + value: 101, + geohash: 'q', + center: [112.5, -22.5], + aggConfigResult: { + $parent: { + key: 'q', + value: 'q', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 101, + value: 101, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 90], + [-45, 135], + [0, 135], + [0, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -22.5], + }, + properties: { + value: 101, + geohash: '7', + center: [-22.5, -22.5], + aggConfigResult: { + $parent: { + key: '7', + value: '7', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 101, + value: 101, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -45], + [-45, 0], + [0, 0], + [0, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 67.5], + }, + properties: { + value: 92, + geohash: 'f', + center: [-67.5, 67.5], + aggConfigResult: { + $parent: { + key: 'f', + value: 'f', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 92, + value: 92, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -90], + [45, -45], + [90, -45], + [90, -90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, 67.5], + }, + properties: { + value: 75, + geohash: 'b', + center: [-157.5, 67.5], + aggConfigResult: { + $parent: { + key: 'b', + value: 'b', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 75, + value: 75, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -180], + [45, -135], + [90, -135], + [90, -180], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 67.5], + }, + properties: { + value: 64, + geohash: 'g', + center: [-22.5, 67.5], + aggConfigResult: { + $parent: { + key: 'g', + value: 'g', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 64, + value: 64, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -45], + [45, 0], + [90, 0], + [90, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 67.5], + }, + properties: { + value: 36, + geohash: 'z', + center: [157.5, 67.5], + aggConfigResult: { + $parent: { + key: 'z', + value: 'z', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 36, + value: 36, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 135], + [45, 180], + [90, 180], + [90, 135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 22.5], + }, + properties: { + value: 34, + geohash: 'x', + center: [157.5, 22.5], + aggConfigResult: { + $parent: { + key: 'x', + value: 'x', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 34, + value: 34, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 135], + [0, 180], + [45, 180], + [45, 135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -67.5], + }, + properties: { + value: 30, + geohash: '4', + center: [-67.5, -67.5], + aggConfigResult: { + $parent: { + key: '4', + value: '4', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 30, + value: 30, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -90], + [-90, -45], + [-45, -45], + [-45, -90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, -22.5], + }, + properties: { + value: 16, + geohash: 'm', + center: [67.5, -22.5], + aggConfigResult: { + $parent: { + key: 'm', + value: 'm', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 16, + value: 16, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 45], + [-45, 90], + [0, 90], + [0, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -67.5], + }, + properties: { + value: 10, + geohash: '5', + center: [-22.5, -67.5], + aggConfigResult: { + $parent: { + key: '5', + value: '5', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 10, + value: 10, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -45], + [-90, 0], + [-45, 0], + [-45, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -67.5], + }, + properties: { + value: 6, + geohash: 'p', + center: [157.5, -67.5], + aggConfigResult: { + $parent: { + key: 'p', + value: 'p', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 135], + [-90, 180], + [-45, 180], + [-45, 135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, -22.5], + }, + properties: { + value: 6, + geohash: '2', + center: [-157.5, -22.5], + aggConfigResult: { + $parent: { + key: '2', + value: '2', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -180], + [-45, -135], + [0, -135], + [0, -180], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -67.5], + }, + properties: { + value: 4, + geohash: 'h', + center: [22.5, -67.5], + aggConfigResult: { + $parent: { + key: 'h', + value: 'h', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 4, + value: 4, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 0], + [-90, 45], + [-45, 45], + [-45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -67.5], + }, + properties: { + value: 2, + geohash: 'n', + center: [112.5, -67.5], + aggConfigResult: { + $parent: { + key: 'n', + value: 'n', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 90], + [-90, 135], + [-45, 135], + [-45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, -67.5], + }, + properties: { + value: 2, + geohash: 'j', + center: [67.5, -67.5], + aggConfigResult: { + $parent: { + key: 'j', + value: 'j', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 45], + [-90, 90], + [-45, 90], + [-45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, -22.5], + }, + properties: { + value: 1, + geohash: '3', + center: [-112.5, -22.5], + aggConfigResult: { + $parent: { + key: '3', + value: '3', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -135], + [-45, -90], + [0, -90], + [0, -135], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, -67.5], + }, + properties: { + value: 1, + geohash: '1', + center: [-112.5, -67.5], + aggConfigResult: { + $parent: { + key: '1', + value: '1', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -135], + [-90, -90], + [-45, -90], + [-45, -135], + ], + }, + }, + ], + properties: { + min: 1, + max: 608, + zoom: 2, + center: [5, 15], + }, + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_rows.js new file mode 100644 index 00000000000000..ca4cb2a7feee1f --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/geohash/_rows.js @@ -0,0 +1,2858 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + title: 'Top 2 geo.dest: CN', + valueFormatter: _.identity, + geoJson: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 22.5], + }, + properties: { + value: 39, + geohash: 's', + center: [22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 's', + value: 's', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 39, + value: 39, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 0], + [45, 0], + [45, 45], + [0, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 22.5], + }, + properties: { + value: 31, + geohash: 'w', + center: [112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'w', + value: 'w', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 31, + value: 31, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 0], + [135, 0], + [135, 45], + [90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 22.5], + }, + properties: { + value: 30, + geohash: 'd', + center: [-67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'd', + value: 'd', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 30, + value: 30, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 0], + [-45, 0], + [-45, 45], + [-90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 22.5], + }, + properties: { + value: 25, + geohash: '9', + center: [-112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '9', + value: '9', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 25, + value: 25, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 0], + [-90, 0], + [-90, 45], + [-135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 22.5], + }, + properties: { + value: 23, + geohash: 't', + center: [67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 't', + value: 't', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 23, + value: 23, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 0], + [90, 0], + [90, 45], + [45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -22.5], + }, + properties: { + value: 23, + geohash: 'k', + center: [22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'k', + value: 'k', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 23, + value: 23, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -45], + [45, -45], + [45, 0], + [0, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -22.5], + }, + properties: { + value: 22, + geohash: '6', + center: [-67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '6', + value: '6', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 22, + value: 22, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -45], + [-45, -45], + [-45, 0], + [-90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 67.5], + }, + properties: { + value: 20, + geohash: 'u', + center: [22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'u', + value: 'u', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 20, + value: 20, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 45], + [45, 45], + [45, 90], + [0, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 67.5], + }, + properties: { + value: 18, + geohash: 'v', + center: [67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'v', + value: 'v', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 18, + value: 18, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 45], + [90, 45], + [90, 90], + [45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -22.5], + }, + properties: { + value: 11, + geohash: 'r', + center: [157.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'r', + value: 'r', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 11, + value: 11, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -45], + [180, -45], + [180, 0], + [135, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 22.5], + }, + properties: { + value: 11, + geohash: 'e', + center: [-22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'e', + value: 'e', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 11, + value: 11, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 0], + [0, 0], + [0, 45], + [-45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 67.5], + }, + properties: { + value: 10, + geohash: 'y', + center: [112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'y', + value: 'y', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 10, + value: 10, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 45], + [135, 45], + [135, 90], + [90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 67.5], + }, + properties: { + value: 10, + geohash: 'c', + center: [-112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'c', + value: 'c', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 10, + value: 10, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 45], + [-90, 45], + [-90, 90], + [-135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 67.5], + }, + properties: { + value: 8, + geohash: 'f', + center: [-67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'f', + value: 'f', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 8, + value: 8, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 45], + [-45, 45], + [-45, 90], + [-90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -22.5], + }, + properties: { + value: 8, + geohash: '7', + center: [-22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '7', + value: '7', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 8, + value: 8, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -45], + [0, -45], + [0, 0], + [-45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -22.5], + }, + properties: { + value: 6, + geohash: 'q', + center: [112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'q', + value: 'q', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, -45], + [135, -45], + [135, 0], + [90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 67.5], + }, + properties: { + value: 6, + geohash: 'g', + center: [-22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'g', + value: 'g', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 45], + [0, 45], + [0, 90], + [-45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 22.5], + }, + properties: { + value: 4, + geohash: 'x', + center: [157.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'x', + value: 'x', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 4, + value: 4, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 0], + [180, 0], + [180, 45], + [135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, 67.5], + }, + properties: { + value: 3, + geohash: 'b', + center: [-157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'b', + value: 'b', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 3, + value: 3, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-180, 45], + [-135, 45], + [-135, 90], + [-180, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 67.5], + }, + properties: { + value: 2, + geohash: 'z', + center: [157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'z', + value: 'z', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 45], + [180, 45], + [180, 90], + [135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -67.5], + }, + properties: { + value: 2, + geohash: '4', + center: [-67.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '4', + value: '4', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -90], + [-45, -90], + [-45, -45], + [-90, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -67.5], + }, + properties: { + value: 1, + geohash: '5', + center: [-22.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '5', + value: '5', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -90], + [0, -90], + [0, -45], + [-45, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, -22.5], + }, + properties: { + value: 1, + geohash: '3', + center: [-112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'CN', + value: 'CN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '3', + value: '3', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, -45], + [-90, -45], + [-90, 0], + [-135, 0], + ], + }, + }, + ], + properties: { + min: 1, + max: 39, + }, + }, + }, + { + label: 'Top 2 geo.dest: IN', + valueFormatter: _.identity, + geoJson: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -22.5], + }, + properties: { + value: 31, + geohash: '6', + center: [-67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '6', + value: '6', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 31, + value: 31, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -45], + [-45, -45], + [-45, 0], + [-90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 22.5], + }, + properties: { + value: 30, + geohash: 's', + center: [22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 's', + value: 's', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 30, + value: 30, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 0], + [45, 0], + [45, 45], + [0, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 22.5], + }, + properties: { + value: 29, + geohash: 'w', + center: [112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'w', + value: 'w', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 29, + value: 29, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 0], + [135, 0], + [135, 45], + [90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 22.5], + }, + properties: { + value: 28, + geohash: 'd', + center: [-67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'd', + value: 'd', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 28, + value: 28, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 0], + [-45, 0], + [-45, 45], + [-90, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 22.5], + }, + properties: { + value: 25, + geohash: 't', + center: [67.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 't', + value: 't', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 25, + value: 25, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 0], + [90, 0], + [90, 45], + [45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, -22.5], + }, + properties: { + value: 24, + geohash: 'k', + center: [22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'k', + value: 'k', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 24, + value: 24, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, -45], + [45, -45], + [45, 0], + [0, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [22.5, 67.5], + }, + properties: { + value: 20, + geohash: 'u', + center: [22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'u', + value: 'u', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 20, + value: 20, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [0, 45], + [45, 45], + [45, 90], + [0, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 22.5], + }, + properties: { + value: 18, + geohash: '9', + center: [-112.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '9', + value: '9', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 18, + value: 18, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 0], + [-90, 0], + [-90, 45], + [-135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, 67.5], + }, + properties: { + value: 14, + geohash: 'v', + center: [67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'v', + value: 'v', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 14, + value: 14, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, 45], + [90, 45], + [90, 90], + [45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 22.5], + }, + properties: { + value: 11, + geohash: 'e', + center: [-22.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'e', + value: 'e', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 11, + value: 11, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 0], + [0, 0], + [0, 45], + [-45, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -22.5], + }, + properties: { + value: 9, + geohash: 'r', + center: [157.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'r', + value: 'r', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 9, + value: 9, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -45], + [180, -45], + [180, 0], + [135, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, 67.5], + }, + properties: { + value: 6, + geohash: 'y', + center: [112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'y', + value: 'y', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, 45], + [135, 45], + [135, 90], + [90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, 67.5], + }, + properties: { + value: 6, + geohash: 'f', + center: [-67.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'f', + value: 'f', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 6, + value: 6, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, 45], + [-45, 45], + [-45, 90], + [-90, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, 67.5], + }, + properties: { + value: 5, + geohash: 'g', + center: [-22.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'g', + value: 'g', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 5, + value: 5, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, 45], + [0, 45], + [0, 90], + [-45, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-112.5, 67.5], + }, + properties: { + value: 5, + geohash: 'c', + center: [-112.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'c', + value: 'c', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 5, + value: 5, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-135, 45], + [-90, 45], + [-90, 90], + [-135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-157.5, 67.5], + }, + properties: { + value: 4, + geohash: 'b', + center: [-157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'b', + value: 'b', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 4, + value: 4, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-180, 45], + [-135, 45], + [-135, 90], + [-180, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [112.5, -22.5], + }, + properties: { + value: 3, + geohash: 'q', + center: [112.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'q', + value: 'q', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 3, + value: 3, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [90, -45], + [135, -45], + [135, 0], + [90, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-67.5, -67.5], + }, + properties: { + value: 2, + geohash: '4', + center: [-67.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '4', + value: '4', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 2, + value: 2, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-90, -90], + [-45, -90], + [-45, -45], + [-90, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 67.5], + }, + properties: { + value: 1, + geohash: 'z', + center: [157.5, 67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'z', + value: 'z', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 45], + [180, 45], + [180, 90], + [135, 90], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, 22.5], + }, + properties: { + value: 1, + geohash: 'x', + center: [157.5, 22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'x', + value: 'x', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, 0], + [180, 0], + [180, 45], + [135, 45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [157.5, -67.5], + }, + properties: { + value: 1, + geohash: 'p', + center: [157.5, -67.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'p', + value: 'p', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [135, -90], + [180, -90], + [180, -45], + [135, -45], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [67.5, -22.5], + }, + properties: { + value: 1, + geohash: 'm', + center: [67.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: 'm', + value: 'm', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [45, -45], + [90, -45], + [90, 0], + [45, 0], + ], + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-22.5, -22.5], + }, + properties: { + value: 1, + geohash: '7', + center: [-22.5, -22.5], + aggConfigResult: { + $parent: { + $parent: { + $parent: null, + key: 'IN', + value: 'IN', + aggConfig: { + id: '3', + type: 'terms', + schema: 'split', + params: { + field: 'geo.dest', + size: 2, + order: 'desc', + orderBy: '1', + row: true, + }, + }, + type: 'bucket', + }, + key: '7', + value: '7', + aggConfig: { + id: '2', + type: 'geohash_grid', + schema: 'segment', + params: { + field: 'geo.coordinates', + precision: 1, + }, + }, + type: 'bucket', + }, + key: 1, + value: 1, + aggConfig: { + id: '1', + type: 'count', + schema: 'metric', + params: {}, + }, + type: 'metric', + }, + rectangle: [ + [-45, -45], + [0, -45], + [0, 0], + [-45, 0], + ], + }, + }, + ], + properties: { + min: 1, + max: 31, + }, + }, + }, + ], + hits: 1639, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns.js new file mode 100644 index 00000000000000..c93365234d1582 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_columns.js @@ -0,0 +1,381 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + columns: [ + { + label: '404: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 2147483600, + y: 1, + y0: 0, + }, + { + x: 3221225400, + y: 0, + y0: 0, + }, + { + x: 4294967200, + y: 0, + y0: 0, + }, + { + x: 5368709100, + y: 0, + y0: 0, + }, + { + x: 6442450900, + y: 0, + y0: 0, + }, + { + x: 7516192700, + y: 0, + y0: 0, + }, + { + x: 8589934500, + y: 0, + y0: 0, + }, + { + x: 10737418200, + y: 0, + y0: 0, + }, + { + x: 11811160000, + y: 0, + y0: 0, + }, + { + x: 12884901800, + y: 1, + y0: 0, + }, + { + x: 13958643700, + y: 0, + y0: 0, + }, + { + x: 15032385500, + y: 0, + y0: 0, + }, + { + x: 16106127300, + y: 0, + y0: 0, + }, + { + x: 18253611000, + y: 0, + y0: 0, + }, + { + x: 19327352800, + y: 0, + y0: 0, + }, + { + x: 20401094600, + y: 0, + y0: 0, + }, + { + x: 21474836400, + y: 0, + y0: 0, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '200: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 2147483600, + y: 0, + y0: 0, + }, + { + x: 3221225400, + y: 2, + y0: 0, + }, + { + x: 4294967200, + y: 3, + y0: 0, + }, + { + x: 5368709100, + y: 3, + y0: 0, + }, + { + x: 6442450900, + y: 1, + y0: 0, + }, + { + x: 7516192700, + y: 1, + y0: 0, + }, + { + x: 8589934500, + y: 4, + y0: 0, + }, + { + x: 10737418200, + y: 0, + y0: 0, + }, + { + x: 11811160000, + y: 1, + y0: 0, + }, + { + x: 12884901800, + y: 1, + y0: 0, + }, + { + x: 13958643700, + y: 1, + y0: 0, + }, + { + x: 15032385500, + y: 2, + y0: 0, + }, + { + x: 16106127300, + y: 3, + y0: 0, + }, + { + x: 18253611000, + y: 4, + y0: 0, + }, + { + x: 19327352800, + y: 5, + y0: 0, + }, + { + x: 20401094600, + y: 2, + y0: 0, + }, + { + x: 21474836400, + y: 2, + y0: 0, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '503: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 2147483600, + y: 0, + y0: 0, + }, + { + x: 3221225400, + y: 0, + y0: 0, + }, + { + x: 4294967200, + y: 0, + y0: 0, + }, + { + x: 5368709100, + y: 0, + y0: 0, + }, + { + x: 6442450900, + y: 0, + y0: 0, + }, + { + x: 7516192700, + y: 0, + y0: 0, + }, + { + x: 8589934500, + y: 0, + y0: 0, + }, + { + x: 10737418200, + y: 1, + y0: 0, + }, + { + x: 11811160000, + y: 0, + y0: 0, + }, + { + x: 12884901800, + y: 0, + y0: 0, + }, + { + x: 13958643700, + y: 0, + y0: 0, + }, + { + x: 15032385500, + y: 0, + y0: 0, + }, + { + x: 16106127300, + y: 0, + y0: 0, + }, + { + x: 18253611000, + y: 0, + y0: 0, + }, + { + x: 19327352800, + y: 0, + y0: 0, + }, + { + x: 20401094600, + y: 0, + y0: 0, + }, + { + x: 21474836400, + y: 0, + y0: 0, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + xAxisOrderedValues: [ + 2147483600, + 3221225400, + 4294967200, + 5368709100, + 6442450900, + 7516192700, + 8589934500, + 10737418200, + 11811160000, + 12884901800, + 13958643700, + 15032385500, + 16106127300, + 18253611000, + 19327352800, + 20401094600, + 21474836400, + ], + hits: 40, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows.js new file mode 100644 index 00000000000000..d88197c3737e52 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_rows.js @@ -0,0 +1,225 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + label: '404: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 2147483600, + y: 1, + }, + { + x: 10737418200, + y: 1, + }, + { + x: 15032385500, + y: 2, + }, + { + x: 19327352800, + y: 1, + }, + { + x: 32212254700, + y: 1, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '200: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 3221225400, + y: 4, + }, + { + x: 4294967200, + y: 3, + }, + { + x: 5368709100, + y: 3, + }, + { + x: 6442450900, + y: 2, + }, + { + x: 7516192700, + y: 2, + }, + { + x: 8589934500, + y: 2, + }, + { + x: 9663676400, + y: 3, + }, + { + x: 11811160000, + y: 3, + }, + { + x: 12884901800, + y: 2, + }, + { + x: 13958643700, + y: 1, + }, + { + x: 15032385500, + y: 2, + }, + { + x: 16106127300, + y: 3, + }, + { + x: 17179869100, + y: 1, + }, + { + x: 18253611000, + y: 4, + }, + { + x: 19327352800, + y: 1, + }, + { + x: 20401094600, + y: 1, + }, + { + x: 21474836400, + y: 4, + }, + { + x: 32212254700, + y: 3, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '503: response', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 10737418200, + y: 1, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + xAxisOrderedValues: [ + 2147483600, + 3221225400, + 4294967200, + 5368709100, + 6442450900, + 7516192700, + 8589934500, + 9663676400, + 10737418200, + 11811160000, + 12884901800, + 13958643700, + 15032385500, + 16106127300, + 17179869100, + 18253611000, + 19327352800, + 20401094600, + 21474836400, + 32212254700, + ], + hits: 51, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_series.js new file mode 100644 index 00000000000000..99511e693ff023 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_series.js @@ -0,0 +1,141 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + label: '', + xAxisLabel: 'machine.ram', + ordered: { + interval: 100, + }, + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 3221225400, + y: 5, + }, + { + x: 4294967200, + y: 2, + }, + { + x: 5368709100, + y: 5, + }, + { + x: 6442450900, + y: 4, + }, + { + x: 7516192700, + y: 1, + }, + { + x: 9663676400, + y: 9, + }, + { + x: 10737418200, + y: 5, + }, + { + x: 11811160000, + y: 5, + }, + { + x: 12884901800, + y: 2, + }, + { + x: 13958643700, + y: 3, + }, + { + x: 15032385500, + y: 3, + }, + { + x: 16106127300, + y: 3, + }, + { + x: 17179869100, + y: 1, + }, + { + x: 18253611000, + y: 6, + }, + { + x: 19327352800, + y: 3, + }, + { + x: 20401094600, + y: 3, + }, + { + x: 21474836400, + y: 7, + }, + { + x: 32212254700, + y: 4, + }, + ], + }, + ], + hits: 71, + xAxisOrderedValues: [ + 3221225400, + 4294967200, + 5368709100, + 6442450900, + 7516192700, + 9663676400, + 10737418200, + 11811160000, + 12884901800, + 13958643700, + 15032385500, + 16106127300, + 17179869100, + 18253611000, + 19327352800, + 20401094600, + 21474836400, + 32212254700, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_slices.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_slices.js new file mode 100644 index 00000000000000..c23a89b755b5b3 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/histogram/_slices.js @@ -0,0 +1,328 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + label: '', + slices: { + children: [ + { + name: 0, + size: 378611, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 1000, + size: 205997, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 2000, + size: 397189, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 3000, + size: 397195, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 4000, + size: 398429, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 5000, + size: 397843, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 6000, + size: 398140, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 7000, + size: 398076, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 8000, + size: 396746, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 9000, + size: 397418, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 10000, + size: 20222, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 11000, + size: 20173, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 12000, + size: 20026, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 13000, + size: 19986, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 14000, + size: 20091, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 15000, + size: 20052, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 16000, + size: 20349, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 17000, + size: 20290, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 18000, + size: 20399, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 19000, + size: 20133, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + { + name: 20000, + size: 9, + aggConfig: { + type: 'histogram', + schema: 'segment', + fieldFormatter: _.constant(String), + params: { + interval: 1000, + extended_bounds: {}, + }, + }, + }, + ], + }, + names: [ + 0, + 1000, + 2000, + 3000, + 4000, + 5000, + 6000, + 7000, + 8000, + 9000, + 10000, + 11000, + 12000, + 13000, + 14000, + 15000, + 16000, + 17000, + 18000, + 19000, + 20000, + ], + hits: 3967374, + tooltipFormatter: function(event) { + return event.point; + }, +}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/not_enough_data/_one_point.js similarity index 61% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js rename to src/plugins/vis_type_vislib/public/fixtures/mock_data/not_enough_data/_one_point.js index 734c6d003278fd..df71f4efc58b55 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/not_enough_data/_one_point.js @@ -18,22 +18,34 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; -import VislibProvider from '..'; - -describe('Vislib Index Test Suite', function() { - let vislib; - - beforeEach(() => { - vislib = new VislibProvider(); - }); - - it('should return an object', function() { - expect(_.isObject(vislib)).to.be(true); - }); - - it('should return a Vis function', function() { - expect(_.isFunction(vislib.Vis)).to.be(true); - }); -}); +export default { + label: '', + xAxisLabel: '', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '_all', + y: 274, + }, + ], + }, + ], + hits: 274, + xAxisOrderedValues: ['_all'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_columns.js new file mode 100644 index 00000000000000..b5b931383f7321 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_columns.js @@ -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 _ from 'lodash'; + +export default { + columns: [ + { + label: 'apache: _type', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 13309, + }, + { + x: '1000.0-2000.0', + y: 7196, + }, + ], + }, + ], + }, + { + label: 'nginx: _type', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 3278, + }, + { + x: '1000.0-2000.0', + y: 1804, + }, + ], + }, + ], + }, + ], + hits: 171499, + xAxisOrderedValues: ['0.0-1000.0', '1000.0-2000.0'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows.js new file mode 100644 index 00000000000000..bc7e4c9f49625f --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_rows.js @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + label: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1: agent.raw', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 6422, + y0: 0, + }, + { + x: '1000.0-2000.0', + y: 3446, + y0: 0, + }, + ], + }, + ], + }, + { + label: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24: agent.raw', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 5430, + y0: 0, + }, + { + x: '1000.0-2000.0', + y: 3010, + y0: 0, + }, + ], + }, + ], + }, + { + label: + 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322): agent.raw', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 4735, + y0: 0, + }, + { + x: '1000.0-2000.0', + y: 2542, + y0: 0, + }, + ], + }, + ], + }, + ], + hits: 171501, + xAxisOrderedValues: ['0.0-1000.0', '1000.0-2000.0'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_series.js new file mode 100644 index 00000000000000..40c14beeb4f3ef --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/range/_series.js @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import _ from 'lodash'; + +export default { + label: '', + xAxisLabel: 'bytes ranges', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: '0.0-1000.0', + y: 16576, + }, + { + x: '1000.0-2000.0', + y: 9005, + }, + ], + }, + ], + hits: 171500, + xAxisOrderedValues: ['0.0-1000.0', '1000.0-2000.0'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_columns.js new file mode 100644 index 00000000000000..bf4fcb7e9e526a --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_columns.js @@ -0,0 +1,251 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + columns: [ + { + label: 'http: links', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 144000, + }, + { + x: 'info', + y: 128237, + }, + { + x: 'security', + y: 34518, + }, + { + x: 'error', + y: 10258, + }, + { + x: 'warning', + y: 17188, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'info: links', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 108148, + }, + { + x: 'info', + y: 96242, + }, + { + x: 'security', + y: 25889, + }, + { + x: 'error', + y: 7673, + }, + { + x: 'warning', + y: 12842, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'www.slate.com: links', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 98056, + }, + { + x: 'info', + y: 87344, + }, + { + x: 'security', + y: 23577, + }, + { + x: 'error', + y: 7004, + }, + { + x: 'warning', + y: 11759, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'twitter.com: links', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 74154, + }, + { + x: 'info', + y: 65963, + }, + { + x: 'security', + y: 17832, + }, + { + x: 'error', + y: 5258, + }, + { + x: 'warning', + y: 8906, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'www.www.slate.com: links', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 62591, + }, + { + x: 'info', + y: 55822, + }, + { + x: 'security', + y: 15100, + }, + { + x: 'error', + y: 4564, + }, + { + x: 'warning', + y: 7498, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + hits: 171446, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_rows.js new file mode 100644 index 00000000000000..5d737131dc9980 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_rows.js @@ -0,0 +1,251 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + label: 'h3: headings', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 144000, + }, + { + x: 'info', + y: 128235, + }, + { + x: 'security', + y: 34518, + }, + { + x: 'error', + y: 10257, + }, + { + x: 'warning', + y: 17188, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'h5: headings', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 144000, + }, + { + x: 'info', + y: 128235, + }, + { + x: 'security', + y: 34518, + }, + { + x: 'error', + y: 10257, + }, + { + x: 'warning', + y: 17188, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'http: headings', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 144000, + }, + { + x: 'info', + y: 128235, + }, + { + x: 'security', + y: 34518, + }, + { + x: 'error', + y: 10257, + }, + { + x: 'warning', + y: 17188, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'success: headings', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 120689, + }, + { + x: 'info', + y: 107621, + }, + { + x: 'security', + y: 28916, + }, + { + x: 'error', + y: 8590, + }, + { + x: 'warning', + y: 14548, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: 'www.slate.com: headings', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 62292, + }, + { + x: 'info', + y: 55646, + }, + { + x: 'security', + y: 14823, + }, + { + x: 'error', + y: 4441, + }, + { + x: 'warning', + y: 7539, + }, + ], + }, + ], + xAxisOrderedValues: ['success', 'info', 'security', 'error', 'warning'], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + hits: 171445, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_series.js new file mode 100644 index 00000000000000..36df8e091ba895 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/significant_terms/_series.js @@ -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 _ from 'lodash'; + +export default { + label: '', + xAxisLabel: 'Top 5 unusual terms in @tags', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'success', + y: 143995, + }, + { + x: 'info', + y: 128233, + }, + { + x: 'security', + y: 34515, + }, + { + x: 'error', + y: 10256, + }, + { + x: 'warning', + y: 17188, + }, + ], + }, + ], + hits: 171439, + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/stacked/_stacked.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/stacked/_stacked.js new file mode 100644 index 00000000000000..a914f20a7ffc6f --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/stacked/_stacked.js @@ -0,0 +1,1654 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 moment from 'moment'; + +export default { + label: '', + xAxisLabel: '@timestamp per 30 sec', + ordered: { + date: true, + interval: 30000, + min: 1416850340336, + max: 1416852140336, + }, + yAxisLabel: 'Count of documents', + xAxisOrderedValues: [ + 1416850320000, + 1416850350000, + 1416850380000, + 1416850410000, + 1416850440000, + 1416850470000, + 1416850500000, + 1416850530000, + 1416850560000, + 1416850590000, + 1416850620000, + 1416850650000, + 1416850680000, + 1416850710000, + 1416850740000, + 1416850770000, + 1416850800000, + 1416850830000, + 1416850860000, + 1416850890000, + 1416850920000, + 1416850950000, + 1416850980000, + 1416851010000, + 1416851040000, + 1416851070000, + 1416851100000, + 1416851130000, + 1416851160000, + 1416851190000, + 1416851220000, + 1416851250000, + 1416851280000, + 1416851310000, + 1416851340000, + 1416851370000, + 1416851400000, + 1416851430000, + 1416851460000, + 1416851490000, + 1416851520000, + 1416851550000, + 1416851580000, + 1416851610000, + 1416851640000, + 1416851670000, + 1416851700000, + 1416851730000, + 1416851760000, + 1416851790000, + 1416851820000, + 1416851850000, + 1416851880000, + 1416851910000, + 1416851940000, + 1416851970000, + 1416852000000, + 1416852030000, + 1416852060000, + 1416852090000, + 1416852120000, + ], + series: [ + { + label: 'jpg', + values: [ + { + x: 1416850320000, + y: 110, + y0: 0, + }, + { + x: 1416850350000, + y: 24, + y0: 0, + }, + { + x: 1416850380000, + y: 34, + y0: 0, + }, + { + x: 1416850410000, + y: 21, + y0: 0, + }, + { + x: 1416850440000, + y: 32, + y0: 0, + }, + { + x: 1416850470000, + y: 24, + y0: 0, + }, + { + x: 1416850500000, + y: 16, + y0: 0, + }, + { + x: 1416850530000, + y: 27, + y0: 0, + }, + { + x: 1416850560000, + y: 24, + y0: 0, + }, + { + x: 1416850590000, + y: 38, + y0: 0, + }, + { + x: 1416850620000, + y: 33, + y0: 0, + }, + { + x: 1416850650000, + y: 33, + y0: 0, + }, + { + x: 1416850680000, + y: 31, + y0: 0, + }, + { + x: 1416850710000, + y: 24, + y0: 0, + }, + { + x: 1416850740000, + y: 24, + y0: 0, + }, + { + x: 1416850770000, + y: 38, + y0: 0, + }, + { + x: 1416850800000, + y: 34, + y0: 0, + }, + { + x: 1416850830000, + y: 30, + y0: 0, + }, + { + x: 1416850860000, + y: 38, + y0: 0, + }, + { + x: 1416850890000, + y: 19, + y0: 0, + }, + { + x: 1416850920000, + y: 23, + y0: 0, + }, + { + x: 1416850950000, + y: 33, + y0: 0, + }, + { + x: 1416850980000, + y: 28, + y0: 0, + }, + { + x: 1416851010000, + y: 24, + y0: 0, + }, + { + x: 1416851040000, + y: 22, + y0: 0, + }, + { + x: 1416851070000, + y: 28, + y0: 0, + }, + { + x: 1416851100000, + y: 27, + y0: 0, + }, + { + x: 1416851130000, + y: 32, + y0: 0, + }, + { + x: 1416851160000, + y: 32, + y0: 0, + }, + { + x: 1416851190000, + y: 30, + y0: 0, + }, + { + x: 1416851220000, + y: 32, + y0: 0, + }, + { + x: 1416851250000, + y: 36, + y0: 0, + }, + { + x: 1416851280000, + y: 32, + y0: 0, + }, + { + x: 1416851310000, + y: 29, + y0: 0, + }, + { + x: 1416851340000, + y: 22, + y0: 0, + }, + { + x: 1416851370000, + y: 29, + y0: 0, + }, + { + x: 1416851400000, + y: 33, + y0: 0, + }, + { + x: 1416851430000, + y: 28, + y0: 0, + }, + { + x: 1416851460000, + y: 39, + y0: 0, + }, + { + x: 1416851490000, + y: 28, + y0: 0, + }, + { + x: 1416851520000, + y: 28, + y0: 0, + }, + { + x: 1416851550000, + y: 28, + y0: 0, + }, + { + x: 1416851580000, + y: 30, + y0: 0, + }, + { + x: 1416851610000, + y: 29, + y0: 0, + }, + { + x: 1416851640000, + y: 30, + y0: 0, + }, + { + x: 1416851670000, + y: 23, + y0: 0, + }, + { + x: 1416851700000, + y: 23, + y0: 0, + }, + { + x: 1416851730000, + y: 27, + y0: 0, + }, + { + x: 1416851760000, + y: 21, + y0: 0, + }, + { + x: 1416851790000, + y: 24, + y0: 0, + }, + { + x: 1416851820000, + y: 26, + y0: 0, + }, + { + x: 1416851850000, + y: 26, + y0: 0, + }, + { + x: 1416851880000, + y: 21, + y0: 0, + }, + { + x: 1416851910000, + y: 33, + y0: 0, + }, + { + x: 1416851940000, + y: 23, + y0: 0, + }, + { + x: 1416851970000, + y: 46, + y0: 0, + }, + { + x: 1416852000000, + y: 27, + y0: 0, + }, + { + x: 1416852030000, + y: 20, + y0: 0, + }, + { + x: 1416852060000, + y: 34, + y0: 0, + }, + { + x: 1416852090000, + y: 15, + y0: 0, + }, + { + x: 1416852120000, + y: 18, + y0: 0, + }, + ], + }, + { + label: 'css', + values: [ + { + x: 1416850320000, + y: 3, + y0: 11, + }, + { + x: 1416850350000, + y: 13, + y0: 24, + }, + { + x: 1416850380000, + y: 5, + y0: 34, + }, + { + x: 1416850410000, + y: 12, + y0: 21, + }, + { + x: 1416850440000, + y: 9, + y0: 32, + }, + { + x: 1416850470000, + y: 12, + y0: 24, + }, + { + x: 1416850500000, + y: 6, + y0: 16, + }, + { + x: 1416850530000, + y: 6, + y0: 27, + }, + { + x: 1416850560000, + y: 11, + y0: 24, + }, + { + x: 1416850590000, + y: 11, + y0: 38, + }, + { + x: 1416850620000, + y: 6, + y0: 33, + }, + { + x: 1416850650000, + y: 8, + y0: 33, + }, + { + x: 1416850680000, + y: 6, + y0: 31, + }, + { + x: 1416850710000, + y: 4, + y0: 24, + }, + { + x: 1416850740000, + y: 9, + y0: 24, + }, + { + x: 1416850770000, + y: 3, + y0: 38, + }, + { + x: 1416850800000, + y: 5, + y0: 34, + }, + { + x: 1416850830000, + y: 6, + y0: 30, + }, + { + x: 1416850860000, + y: 9, + y0: 38, + }, + { + x: 1416850890000, + y: 5, + y0: 19, + }, + { + x: 1416850920000, + y: 8, + y0: 23, + }, + { + x: 1416850950000, + y: 9, + y0: 33, + }, + { + x: 1416850980000, + y: 5, + y0: 28, + }, + { + x: 1416851010000, + y: 6, + y0: 24, + }, + { + x: 1416851040000, + y: 9, + y0: 22, + }, + { + x: 1416851070000, + y: 9, + y0: 28, + }, + { + x: 1416851100000, + y: 11, + y0: 27, + }, + { + x: 1416851130000, + y: 5, + y0: 32, + }, + { + x: 1416851160000, + y: 8, + y0: 32, + }, + { + x: 1416851190000, + y: 6, + y0: 30, + }, + { + x: 1416851220000, + y: 10, + y0: 32, + }, + { + x: 1416851250000, + y: 5, + y0: 36, + }, + { + x: 1416851280000, + y: 6, + y0: 32, + }, + { + x: 1416851310000, + y: 4, + y0: 29, + }, + { + x: 1416851340000, + y: 8, + y0: 22, + }, + { + x: 1416851370000, + y: 3, + y0: 29, + }, + { + x: 1416851400000, + y: 8, + y0: 33, + }, + { + x: 1416851430000, + y: 10, + y0: 28, + }, + { + x: 1416851460000, + y: 5, + y0: 39, + }, + { + x: 1416851490000, + y: 7, + y0: 28, + }, + { + x: 1416851520000, + y: 6, + y0: 28, + }, + { + x: 1416851550000, + y: 4, + y0: 28, + }, + { + x: 1416851580000, + y: 9, + y0: 30, + }, + { + x: 1416851610000, + y: 3, + y0: 29, + }, + { + x: 1416851640000, + y: 9, + y0: 30, + }, + { + x: 1416851670000, + y: 6, + y0: 23, + }, + { + x: 1416851700000, + y: 11, + y0: 23, + }, + { + x: 1416851730000, + y: 4, + y0: 27, + }, + { + x: 1416851760000, + y: 8, + y0: 21, + }, + { + x: 1416851790000, + y: 5, + y0: 24, + }, + { + x: 1416851820000, + y: 7, + y0: 26, + }, + { + x: 1416851850000, + y: 7, + y0: 26, + }, + { + x: 1416851880000, + y: 4, + y0: 21, + }, + { + x: 1416851910000, + y: 8, + y0: 33, + }, + { + x: 1416851940000, + y: 6, + y0: 23, + }, + { + x: 1416851970000, + y: 6, + y0: 46, + }, + { + x: 1416852000000, + y: 3, + y0: 27, + }, + { + x: 1416852030000, + y: 6, + y0: 20, + }, + { + x: 1416852060000, + y: 5, + y0: 34, + }, + { + x: 1416852090000, + y: 5, + y0: 15, + }, + { + x: 1416852120000, + y: 1, + y0: 18, + }, + ], + }, + { + label: 'gif', + values: [ + { + x: 1416850320000, + y: 1, + y0: 14, + }, + { + x: 1416850350000, + y: 2, + y0: 37, + }, + { + x: 1416850380000, + y: 4, + y0: 39, + }, + { + x: 1416850410000, + y: 2, + y0: 33, + }, + { + x: 1416850440000, + y: 3, + y0: 41, + }, + { + x: 1416850470000, + y: 1, + y0: 36, + }, + { + x: 1416850500000, + y: 1, + y0: 22, + }, + { + x: 1416850530000, + y: 1, + y0: 33, + }, + { + x: 1416850560000, + y: 2, + y0: 35, + }, + { + x: 1416850590000, + y: 5, + y0: 49, + }, + { + x: 1416850620000, + y: 1, + y0: 39, + }, + { + x: 1416850650000, + y: 1, + y0: 41, + }, + { + x: 1416850680000, + y: 4, + y0: 37, + }, + { + x: 1416850710000, + y: 1, + y0: 28, + }, + { + x: 1416850740000, + y: 3, + y0: 33, + }, + { + x: 1416850770000, + y: 2, + y0: 41, + }, + { + x: 1416850800000, + y: 2, + y0: 39, + }, + { + x: 1416850830000, + y: 5, + y0: 36, + }, + { + x: 1416850860000, + y: 3, + y0: 47, + }, + { + x: 1416850890000, + y: 1, + y0: 24, + }, + { + x: 1416850920000, + y: 3, + y0: 31, + }, + { + x: 1416850950000, + y: 4, + y0: 42, + }, + { + x: 1416850980000, + y: 3, + y0: 33, + }, + { + x: 1416851010000, + y: 5, + y0: 30, + }, + { + x: 1416851040000, + y: 2, + y0: 31, + }, + { + x: 1416851070000, + y: 3, + y0: 37, + }, + { + x: 1416851100000, + y: 5, + y0: 38, + }, + { + x: 1416851130000, + y: 3, + y0: 37, + }, + { + x: 1416851160000, + y: 4, + y0: 40, + }, + { + x: 1416851190000, + y: 9, + y0: 36, + }, + { + x: 1416851220000, + y: 7, + y0: 42, + }, + { + x: 1416851250000, + y: 2, + y0: 41, + }, + { + x: 1416851280000, + y: 1, + y0: 38, + }, + { + x: 1416851310000, + y: 2, + y0: 33, + }, + { + x: 1416851340000, + y: 5, + y0: 30, + }, + { + x: 1416851370000, + y: 3, + y0: 32, + }, + { + x: 1416851400000, + y: 5, + y0: 41, + }, + { + x: 1416851430000, + y: 4, + y0: 38, + }, + { + x: 1416851460000, + y: 5, + y0: 44, + }, + { + x: 1416851490000, + y: 2, + y0: 35, + }, + { + x: 1416851520000, + y: 2, + y0: 34, + }, + { + x: 1416851550000, + y: 4, + y0: 32, + }, + { + x: 1416851580000, + y: 3, + y0: 39, + }, + { + x: 1416851610000, + y: 4, + y0: 32, + }, + { + x: 1416851640000, + y: 0, + y0: 39, + }, + { + x: 1416851670000, + y: 2, + y0: 29, + }, + { + x: 1416851700000, + y: 1, + y0: 34, + }, + { + x: 1416851730000, + y: 3, + y0: 31, + }, + { + x: 1416851760000, + y: 0, + y0: 29, + }, + { + x: 1416851790000, + y: 4, + y0: 29, + }, + { + x: 1416851820000, + y: 3, + y0: 33, + }, + { + x: 1416851850000, + y: 3, + y0: 33, + }, + { + x: 1416851880000, + y: 0, + y0: 25, + }, + { + x: 1416851910000, + y: 0, + y0: 41, + }, + { + x: 1416851940000, + y: 3, + y0: 29, + }, + { + x: 1416851970000, + y: 3, + y0: 52, + }, + { + x: 1416852000000, + y: 1, + y0: 30, + }, + { + x: 1416852030000, + y: 5, + y0: 26, + }, + { + x: 1416852060000, + y: 3, + y0: 39, + }, + { + x: 1416852090000, + y: 1, + y0: 20, + }, + { + x: 1416852120000, + y: 2, + y0: 19, + }, + ], + }, + { + label: 'png', + values: [ + { + x: 1416850320000, + y: 1, + y0: 15, + }, + { + x: 1416850350000, + y: 6, + y0: 39, + }, + { + x: 1416850380000, + y: 6, + y0: 43, + }, + { + x: 1416850410000, + y: 5, + y0: 35, + }, + { + x: 1416850440000, + y: 3, + y0: 44, + }, + { + x: 1416850470000, + y: 5, + y0: 37, + }, + { + x: 1416850500000, + y: 6, + y0: 23, + }, + { + x: 1416850530000, + y: 1, + y0: 34, + }, + { + x: 1416850560000, + y: 3, + y0: 37, + }, + { + x: 1416850590000, + y: 2, + y0: 54, + }, + { + x: 1416850620000, + y: 1, + y0: 40, + }, + { + x: 1416850650000, + y: 1, + y0: 42, + }, + { + x: 1416850680000, + y: 2, + y0: 41, + }, + { + x: 1416850710000, + y: 5, + y0: 29, + }, + { + x: 1416850740000, + y: 7, + y0: 36, + }, + { + x: 1416850770000, + y: 2, + y0: 43, + }, + { + x: 1416850800000, + y: 3, + y0: 41, + }, + { + x: 1416850830000, + y: 6, + y0: 41, + }, + { + x: 1416850860000, + y: 2, + y0: 50, + }, + { + x: 1416850890000, + y: 4, + y0: 25, + }, + { + x: 1416850920000, + y: 2, + y0: 34, + }, + { + x: 1416850950000, + y: 3, + y0: 46, + }, + { + x: 1416850980000, + y: 8, + y0: 36, + }, + { + x: 1416851010000, + y: 4, + y0: 35, + }, + { + x: 1416851040000, + y: 4, + y0: 33, + }, + { + x: 1416851070000, + y: 1, + y0: 40, + }, + { + x: 1416851100000, + y: 2, + y0: 43, + }, + { + x: 1416851130000, + y: 4, + y0: 40, + }, + { + x: 1416851160000, + y: 3, + y0: 44, + }, + { + x: 1416851190000, + y: 4, + y0: 45, + }, + { + x: 1416851220000, + y: 2, + y0: 49, + }, + { + x: 1416851250000, + y: 4, + y0: 43, + }, + { + x: 1416851280000, + y: 8, + y0: 39, + }, + { + x: 1416851310000, + y: 4, + y0: 35, + }, + { + x: 1416851340000, + y: 4, + y0: 35, + }, + { + x: 1416851370000, + y: 7, + y0: 35, + }, + { + x: 1416851400000, + y: 2, + y0: 46, + }, + { + x: 1416851430000, + y: 3, + y0: 42, + }, + { + x: 1416851460000, + y: 3, + y0: 49, + }, + { + x: 1416851490000, + y: 3, + y0: 37, + }, + { + x: 1416851520000, + y: 4, + y0: 36, + }, + { + x: 1416851550000, + y: 3, + y0: 36, + }, + { + x: 1416851580000, + y: 4, + y0: 42, + }, + { + x: 1416851610000, + y: 5, + y0: 36, + }, + { + x: 1416851640000, + y: 3, + y0: 39, + }, + { + x: 1416851670000, + y: 3, + y0: 31, + }, + { + x: 1416851700000, + y: 2, + y0: 35, + }, + { + x: 1416851730000, + y: 5, + y0: 34, + }, + { + x: 1416851760000, + y: 4, + y0: 29, + }, + { + x: 1416851790000, + y: 5, + y0: 33, + }, + { + x: 1416851820000, + y: 1, + y0: 36, + }, + { + x: 1416851850000, + y: 3, + y0: 36, + }, + { + x: 1416851880000, + y: 6, + y0: 25, + }, + { + x: 1416851910000, + y: 4, + y0: 41, + }, + { + x: 1416851940000, + y: 7, + y0: 32, + }, + { + x: 1416851970000, + y: 5, + y0: 55, + }, + { + x: 1416852000000, + y: 2, + y0: 31, + }, + { + x: 1416852030000, + y: 2, + y0: 31, + }, + { + x: 1416852060000, + y: 4, + y0: 42, + }, + { + x: 1416852090000, + y: 6, + y0: 21, + }, + { + x: 1416852120000, + y: 2, + y0: 21, + }, + ], + }, + { + label: 'php', + values: [ + { + x: 1416850320000, + y: 0, + y0: 16, + }, + { + x: 1416850350000, + y: 1, + y0: 45, + }, + { + x: 1416850380000, + y: 0, + y0: 49, + }, + { + x: 1416850410000, + y: 2, + y0: 40, + }, + { + x: 1416850440000, + y: 0, + y0: 47, + }, + { + x: 1416850470000, + y: 0, + y0: 42, + }, + { + x: 1416850500000, + y: 3, + y0: 29, + }, + { + x: 1416850530000, + y: 1, + y0: 35, + }, + { + x: 1416850560000, + y: 3, + y0: 40, + }, + { + x: 1416850590000, + y: 2, + y0: 56, + }, + { + x: 1416850620000, + y: 2, + y0: 41, + }, + { + x: 1416850650000, + y: 5, + y0: 43, + }, + { + x: 1416850680000, + y: 2, + y0: 43, + }, + { + x: 1416850710000, + y: 1, + y0: 34, + }, + { + x: 1416850740000, + y: 2, + y0: 43, + }, + { + x: 1416850770000, + y: 2, + y0: 45, + }, + { + x: 1416850800000, + y: 1, + y0: 44, + }, + { + x: 1416850830000, + y: 1, + y0: 47, + }, + { + x: 1416850860000, + y: 1, + y0: 52, + }, + { + x: 1416850890000, + y: 1, + y0: 29, + }, + { + x: 1416850920000, + y: 2, + y0: 36, + }, + { + x: 1416850950000, + y: 2, + y0: 49, + }, + { + x: 1416850980000, + y: 0, + y0: 44, + }, + { + x: 1416851010000, + y: 3, + y0: 39, + }, + { + x: 1416851040000, + y: 2, + y0: 37, + }, + { + x: 1416851070000, + y: 2, + y0: 41, + }, + { + x: 1416851100000, + y: 2, + y0: 45, + }, + { + x: 1416851130000, + y: 0, + y0: 44, + }, + { + x: 1416851160000, + y: 1, + y0: 47, + }, + { + x: 1416851190000, + y: 2, + y0: 49, + }, + { + x: 1416851220000, + y: 4, + y0: 51, + }, + { + x: 1416851250000, + y: 0, + y0: 47, + }, + { + x: 1416851280000, + y: 3, + y0: 47, + }, + { + x: 1416851310000, + y: 3, + y0: 39, + }, + { + x: 1416851340000, + y: 2, + y0: 39, + }, + { + x: 1416851370000, + y: 2, + y0: 42, + }, + { + x: 1416851400000, + y: 3, + y0: 48, + }, + { + x: 1416851430000, + y: 1, + y0: 45, + }, + { + x: 1416851460000, + y: 0, + y0: 52, + }, + { + x: 1416851490000, + y: 2, + y0: 40, + }, + { + x: 1416851520000, + y: 1, + y0: 40, + }, + { + x: 1416851550000, + y: 3, + y0: 39, + }, + { + x: 1416851580000, + y: 1, + y0: 46, + }, + { + x: 1416851610000, + y: 2, + y0: 41, + }, + { + x: 1416851640000, + y: 1, + y0: 42, + }, + { + x: 1416851670000, + y: 2, + y0: 34, + }, + { + x: 1416851700000, + y: 3, + y0: 37, + }, + { + x: 1416851730000, + y: 1, + y0: 39, + }, + { + x: 1416851760000, + y: 1, + y0: 33, + }, + { + x: 1416851790000, + y: 1, + y0: 38, + }, + { + x: 1416851820000, + y: 1, + y0: 37, + }, + { + x: 1416851850000, + y: 1, + y0: 39, + }, + { + x: 1416851880000, + y: 1, + y0: 31, + }, + { + x: 1416851910000, + y: 2, + y0: 45, + }, + { + x: 1416851940000, + y: 0, + y0: 39, + }, + { + x: 1416851970000, + y: 0, + y0: 60, + }, + { + x: 1416852000000, + y: 1, + y0: 33, + }, + { + x: 1416852030000, + y: 2, + y0: 33, + }, + { + x: 1416852060000, + y: 1, + y0: 46, + }, + { + x: 1416852090000, + y: 1, + y0: 27, + }, + { + x: 1416852120000, + y: 0, + y0: 23, + }, + ], + }, + ], + hits: 2595, + xAxisFormatter: function(thing) { + return moment(thing); + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns.js new file mode 100644 index 00000000000000..8891d9badb2be3 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_columns.js @@ -0,0 +1,159 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + columns: [ + { + label: 'logstash: index', + xAxisLabel: 'Top 5 extension', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 110710, + }, + { + x: 'css', + y: 27376, + }, + { + x: 'png', + y: 16664, + }, + { + x: 'gif', + y: 11264, + }, + { + x: 'php', + y: 5448, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '2014.11.12: index', + xAxisLabel: 'Top 5 extension', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 110643, + }, + { + x: 'css', + y: 27350, + }, + { + x: 'png', + y: 16648, + }, + { + x: 'gif', + y: 11257, + }, + { + x: 'php', + y: 5440, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '2014.11.11: index', + xAxisLabel: 'Top 5 extension', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 67, + }, + { + x: 'css', + y: 26, + }, + { + x: 'png', + y: 16, + }, + { + x: 'gif', + y: 7, + }, + { + x: 'php', + y: 8, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + xAxisOrderedValues: ['jpg', 'css', 'png', 'gif', 'php'], + hits: 171462, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_rows.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_rows.js new file mode 100644 index 00000000000000..09a1c159897603 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_rows.js @@ -0,0 +1,115 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + rows: [ + { + label: '0.0-1000.0: bytes', + xAxisLabel: 'Top 5 extension', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 3378, + }, + { + x: 'css', + y: 762, + }, + { + x: 'png', + y: 527, + }, + { + x: 'gif', + y: 11258, + }, + { + x: 'php', + y: 653, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + { + label: '1000.0-2000.0: bytes', + xAxisLabel: 'Top 5 extension', + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 6422, + }, + { + x: 'css', + y: 1591, + }, + { + x: 'png', + y: 430, + }, + { + x: 'gif', + y: 8, + }, + { + x: 'php', + y: 561, + }, + ], + }, + ], + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, + }, + ], + xAxisOrderedValues: ['jpg', 'css', 'png', 'gif', 'php'], + hits: 171458, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series.js new file mode 100644 index 00000000000000..c55bff5631e888 --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series.js @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 _ from 'lodash'; + +export default { + label: '', + xAxisLabel: 'Top 5 extension', + xAxisOrderedValues: ['jpg', 'css', 'png', 'gif', 'php'], + yAxisLabel: 'Count of documents', + series: [ + { + label: 'Count', + values: [ + { + x: 'jpg', + y: 110710, + }, + { + x: 'css', + y: 27389, + }, + { + x: 'png', + y: 16661, + }, + { + x: 'gif', + y: 11269, + }, + { + x: 'php', + y: 5447, + }, + ], + }, + ], + hits: 171476, + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple.js b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple.js new file mode 100644 index 00000000000000..372325120ee8ec --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mock_data/terms/_series_multiple.js @@ -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 _ from 'lodash'; + +export default { + xAxisOrderedValues: ['_all'], + yAxisLabel: 'Count', + zAxisLabel: 'machine.os.raw: Descending', + yScale: null, + series: [ + { + label: 'ios', + id: '1', + yAxisFormatter: _.identity, + values: [ + { + x: '_all', + y: 2820, + series: 'ios', + }, + ], + }, + { + label: 'win 7', + aggId: '1', + yAxisFormatter: _.identity, + values: [ + { + x: '_all', + y: 2319, + series: 'win 7', + }, + ], + }, + { + label: 'win 8', + id: '1', + yAxisFormatter: _.identity, + values: [ + { + x: '_all', + y: 1835, + series: 'win 8', + }, + ], + }, + { + label: 'windows xp service pack 2 version 20123452', + id: '1', + yAxisFormatter: _.identity, + values: [ + { + x: '_all', + y: 734, + series: 'win xp', + }, + ], + }, + { + label: 'osx', + id: '1', + yAxisFormatter: _.identity, + values: [ + { + x: '_all', + y: 1352, + series: 'osx', + }, + ], + }, + ], + hits: 14005, + xAxisFormatter: function(val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } else if (val == null) { + return ''; + } else { + return '' + val; + } + }, + yAxisFormatter: function(val) { + return val; + }, + tooltipFormatter: function(d) { + return d; + }, +}; diff --git a/src/plugins/vis_type_vislib/public/fixtures/mocks.js b/src/plugins/vis_type_vislib/public/fixtures/mocks.js new file mode 100644 index 00000000000000..60edf6c1ff05cc --- /dev/null +++ b/src/plugins/vis_type_vislib/public/fixtures/mocks.js @@ -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 { setFormatService } from '../services'; + +setFormatService({ + deserialize: () => ({ + convert: v => v, + }), +}); + +export const getMockUiState = () => { + const map = new Map(); + + return (() => ({ + get: (...args) => map.get(...args), + set: (...args) => map.set(...args), + setSilent: (...args) => map.set(...args), + on: () => undefined, + }))(); +}; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts b/src/plugins/vis_type_vislib/public/gauge.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/gauge.ts rename to src/plugins/vis_type_vislib/public/gauge.ts index 5e0b2b8fbd36cb..561c45d26fa7f6 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts +++ b/src/plugins/vis_type_vislib/public/gauge.ts @@ -19,17 +19,11 @@ import { i18n } from '@kbn/i18n'; -import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public'; -import { AggGroupNames } from '../../../../plugins/data/public'; +import { RangeValues, Schemas } from '../../vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; import { GaugeOptions } from './components/options'; import { getGaugeCollections, Alignments, GaugeTypes } from './utils/collections'; -import { - ColorModes, - ColorSchemas, - ColorSchemaParams, - Labels, - Style, -} from '../../../../plugins/charts/public'; +import { ColorModes, ColorSchemas, ColorSchemaParams, Labels, Style } from '../../charts/public'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts b/src/plugins/vis_type_vislib/public/goal.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/goal.ts rename to src/plugins/vis_type_vislib/public/goal.ts index 0f70dca69728d2..5f74698938a0b1 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts +++ b/src/plugins/vis_type_vislib/public/goal.ts @@ -23,9 +23,9 @@ import { GaugeOptions } from './components/options'; import { getGaugeCollections, GaugeTypes } from './utils/collections'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; -import { ColorModes, ColorSchemas } from '../../../../plugins/charts/public'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { ColorModes, ColorSchemas } from '../../charts/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; export const createGoalVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'goal', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts b/src/plugins/vis_type_vislib/public/heatmap.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts rename to src/plugins/vis_type_vislib/public/heatmap.ts index 9feed60b984bae..ced7a38568ffd0 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts +++ b/src/plugins/vis_type_vislib/public/heatmap.ts @@ -19,15 +19,15 @@ import { i18n } from '@kbn/i18n'; -import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public'; -import { AggGroupNames } from '../../../../plugins/data/public'; +import { RangeValues, Schemas } from '../../vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; import { AxisTypes, getHeatmapCollections, Positions, ScaleTypes } from './utils/collections'; import { HeatmapOptions } from './components/options'; import { createVislibVisController } from './vis_controller'; import { TimeMarker } from './vislib/visualizations/time_marker'; import { CommonVislibParams, ValueAxis } from './types'; import { VisTypeVislibDependencies } from './plugin'; -import { ColorSchemas, ColorSchemaParams } from '../../../../plugins/charts/public'; +import { ColorSchemas, ColorSchemaParams } from '../../charts/public'; export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaParams { type: 'heatmap'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts b/src/plugins/vis_type_vislib/public/histogram.ts similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/histogram.ts rename to src/plugins/vis_type_vislib/public/histogram.ts index 54ccf66f362ca3..52242ad11e8f58 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts +++ b/src/plugins/vis_type_vislib/public/histogram.ts @@ -23,8 +23,8 @@ import { palettes } from '@elastic/eui/lib/services'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; import { Positions, ChartTypes, @@ -38,7 +38,7 @@ import { import { getAreaOptionTabs, countLabel } from './utils/common_config'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; -import { Rotates } from '../../../../plugins/charts/public'; +import { Rotates } from '../../charts/public'; export const createHistogramVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'histogram', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts b/src/plugins/vis_type_vislib/public/horizontal_bar.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts rename to src/plugins/vis_type_vislib/public/horizontal_bar.ts index 6f732717266601..a58c15f136431e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts +++ b/src/plugins/vis_type_vislib/public/horizontal_bar.ts @@ -19,12 +19,10 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore -import { palettes } from '@elastic/eui/lib/services'; -// @ts-ignore -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; +import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; import { Positions, ChartTypes, @@ -38,7 +36,7 @@ import { import { getAreaOptionTabs, countLabel } from './utils/common_config'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; -import { Rotates } from '../../../../plugins/charts/public'; +import { Rotates } from '../../charts/public'; export const createHorizontalBarVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'horizontal_bar', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/_index.scss b/src/plugins/vis_type_vislib/public/index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/_index.scss rename to src/plugins/vis_type_vislib/public/index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/index.ts b/src/plugins/vis_type_vislib/public/index.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/index.ts rename to src/plugins/vis_type_vislib/public/index.ts index 4d7091ffb204be..665643a6763f6b 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/index.ts +++ b/src/plugins/vis_type_vislib/public/index.ts @@ -17,7 +17,7 @@ * under the License. */ -import { PluginInitializerContext } from '../../../../core/public'; +import { PluginInitializerContext } from '../../../core/public'; import { VisTypeVislibPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/line.ts b/src/plugins/vis_type_vislib/public/line.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/line.ts rename to src/plugins/vis_type_vislib/public/line.ts index 1f9a8d77398e63..a94fd3f3945ab7 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/line.ts +++ b/src/plugins/vis_type_vislib/public/line.ts @@ -19,12 +19,10 @@ import { i18n } from '@kbn/i18n'; // @ts-ignore -import { palettes } from '@elastic/eui/lib/services'; -// @ts-ignore -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; +import { palettes, euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; import { Positions, ChartTypes, @@ -39,7 +37,7 @@ import { import { getAreaOptionTabs, countLabel } from './utils/common_config'; import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; -import { Rotates } from '../../../../plugins/charts/public'; +import { Rotates } from '../../charts/public'; export const createLineVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'line', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts b/src/plugins/vis_type_vislib/public/pie.ts similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/pie.ts rename to src/plugins/vis_type_vislib/public/pie.ts index 2774836baa3817..a68bc5893406f5 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts +++ b/src/plugins/vis_type_vislib/public/pie.ts @@ -19,8 +19,8 @@ import { i18n } from '@kbn/i18n'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; import { PieOptions } from './components/options'; import { getPositions, Positions } from './utils/collections'; import { createVislibVisController } from './vis_controller'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts b/src/plugins/vis_type_vislib/public/pie_fn.test.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts rename to src/plugins/vis_type_vislib/public/pie_fn.test.ts index 15c80e47194872..a8c03eba2b4499 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts +++ b/src/plugins/vis_type_vislib/public/pie_fn.test.ts @@ -18,12 +18,11 @@ */ // eslint-disable-next-line -import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils'; +import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; import { createPieVisFn } from './pie_fn'; // @ts-ignore import { vislibSlicesResponseHandler } from './vislib/response_handler'; -jest.mock('ui/new_platform'); jest.mock('./vislib/response_handler', () => ({ vislibSlicesResponseHandler: jest.fn().mockReturnValue({ hits: 1, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts b/src/plugins/vis_type_vislib/public/pie_fn.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts rename to src/plugins/vis_type_vislib/public/pie_fn.ts index 452e0be0df3e4a..52da0f7ac14eca 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts +++ b/src/plugins/vis_type_vislib/public/pie_fn.ts @@ -18,11 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { - ExpressionFunctionDefinition, - KibanaDatatable, - Render, -} from '../../../../plugins/expressions/public'; +import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; // @ts-ignore import { vislibSlicesResponseHandler } from './vislib/response_handler'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts similarity index 92% rename from src/legacy/core_plugins/vis_type_vislib/public/plugin.ts rename to src/plugins/vis_type_vislib/public/plugin.ts index 26800f8a1620ed..e19a2ec451f2b8 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts +++ b/src/plugins/vis_type_vislib/public/plugin.ts @@ -16,6 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + +import './index.scss'; + import { CoreSetup, CoreStart, @@ -25,8 +28,8 @@ import { } from 'kibana/public'; import { VisTypeXyPluginSetup } from 'src/plugins/vis_type_xy/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn'; import { createPieVisFn } from './pie_fn'; import { @@ -39,8 +42,8 @@ import { createGaugeVisTypeDefinition, createGoalVisTypeDefinition, } from './vis_type_vislib_vis_types'; -import { ChartsPluginSetup } from '../../../../plugins/charts/public'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { ChartsPluginSetup } from '../../charts/public'; +import { DataPublicPluginStart } from '../../data/public'; import { setFormatService, setDataActions } from './services'; export interface VisTypeVislibDependencies { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/services.ts b/src/plugins/vis_type_vislib/public/services.ts similarity index 87% rename from src/legacy/core_plugins/vis_type_vislib/public/services.ts rename to src/plugins/vis_type_vislib/public/services.ts index 0d6b1b5e8de589..633fae9c7f2a61 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/services.ts +++ b/src/plugins/vis_type_vislib/public/services.ts @@ -17,8 +17,8 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { createGetterSetter } from '../../kibana_utils/public'; +import { DataPublicPluginStart } from '../../data/public'; export const [getDataActions, setDataActions] = createGetterSetter< DataPublicPluginStart['actions'] diff --git a/src/legacy/core_plugins/vis_type_vislib/public/types.ts b/src/plugins/vis_type_vislib/public/types.ts similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/types.ts rename to src/plugins/vis_type_vislib/public/types.ts index 25c6ae5439fe85..83d0b49b1c5518 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/types.ts +++ b/src/plugins/vis_type_vislib/public/types.ts @@ -28,7 +28,7 @@ import { ScaleTypes, ThresholdLineStyles, } from './utils/collections'; -import { Labels, Style } from '../../../../plugins/charts/public'; +import { Labels, Style } from '../../charts/public'; export interface CommonVislibParams { addTooltip: boolean; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts b/src/plugins/vis_type_vislib/public/utils/collections.ts similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts rename to src/plugins/vis_type_vislib/public/utils/collections.ts index 2024c43dd1c8b4..44df4864bfd68c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts +++ b/src/plugins/vis_type_vislib/public/utils/collections.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { $Values } from '@kbn/utility-types'; -import { colorSchemas, Rotates } from '../../../../../plugins/charts/public'; +import { colorSchemas, Rotates } from '../../../charts/public'; export const Positions = Object.freeze({ RIGHT: 'right' as 'right', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx b/src/plugins/vis_type_vislib/public/utils/common_config.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx rename to src/plugins/vis_type_vislib/public/utils/common_config.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx b/src/plugins/vis_type_vislib/public/vis_controller.tsx similarity index 96% rename from src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx rename to src/plugins/vis_type_vislib/public/vis_controller.tsx index ec091e5d29cfdf..65acc08b58da02 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx +++ b/src/plugins/vis_type_vislib/public/vis_controller.tsx @@ -24,9 +24,9 @@ import React, { RefObject } from 'react'; import { Vis as Vislib } from './vislib/vis'; import { Positions } from './utils/collections'; import { VisTypeVislibDependencies } from './plugin'; -import { mountReactNode } from '../../../../core/public/utils'; +import { mountReactNode } from '../../../core/public/utils'; import { VisLegend, CUSTOM_LEGEND_VIS_TYPES } from './vislib/components/legend'; -import { VisParams, ExprVis } from '../../../../plugins/visualizations/public'; +import { VisParams, ExprVis } from '../../visualizations/public'; const legendClassName = { top: 'visLib--legend-top', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts rename to src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts index 854b70b04e58a6..a4243c6d25c417 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts +++ b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts @@ -18,11 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { - ExpressionFunctionDefinition, - KibanaDatatable, - Render, -} from '../../../../plugins/expressions/public'; +import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; // @ts-ignore import { vislibSeriesResponseHandler } from './vislib/response_handler'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts rename to src/plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/VISLIB.md b/src/plugins/vis_type_vislib/public/vislib/VISLIB.md similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/VISLIB.md rename to src/plugins/vis_type_vislib/public/vislib/VISLIB.md diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss b/src/plugins/vis_type_vislib/public/vislib/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/_variables.scss b/src/plugins/vis_type_vislib/public/vislib/_variables.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/_variables.scss rename to src/plugins/vis_type_vislib/public/vislib/_variables.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss b/src/plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss rename to src/plugins/vis_type_vislib/public/vislib/_vislib_vis_type.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/data_array.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/data_array.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/data_array.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/data_array.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/index.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/index.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/index.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/index.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/labels.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/labels.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/labels.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/labels.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/labels.test.js similarity index 80% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/labels.test.js index db99b881a6e380..838275d44d2a1a 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/labels/labels.test.js @@ -18,12 +18,11 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; -import { labels } from '../../components/labels/labels'; -import { dataArray } from '../../components/labels/data_array'; -import { uniqLabels } from '../../components/labels/uniq_labels'; -import { flattenSeries as getSeries } from '../../components/labels/flatten_series'; +import { labels } from './labels'; +import { dataArray } from './data_array'; +import { uniqLabels } from './uniq_labels'; +import { flattenSeries as getSeries } from './flatten_series'; let seriesLabels; let rowsLabels; @@ -175,27 +174,27 @@ describe('Vislib Labels Module Test Suite', function() { }); it('should be a function', function() { - expect(typeof labels).to.be('function'); + expect(typeof labels).toBe('function'); }); it('should return an array if input is data.series', function() { - expect(seriesArr).to.be(true); + expect(seriesArr).toBe(true); }); it('should return an array if input is data.rows', function() { - expect(rowsArr).to.be(true); + expect(rowsArr).toBe(true); }); it('should throw an error if input is not an object', function() { expect(function() { labels('string not object'); - }).to.throwError(); + }).toThrow(); }); it('should return unique label values', function() { - expect(rowsLabels[0]).to.equal(uniqSeriesLabels[0]); - expect(rowsLabels[1]).to.equal(uniqSeriesLabels[1]); - expect(rowsLabels[2]).to.equal(uniqSeriesLabels[2]); + expect(rowsLabels[0]).toEqual(uniqSeriesLabels[0]); + expect(rowsLabels[1]).toEqual(uniqSeriesLabels[1]); + expect(rowsLabels[2]).toEqual(uniqSeriesLabels[2]); }); }); @@ -231,27 +230,27 @@ describe('Vislib Labels Module Test Suite', function() { it('should throw an error if the input is not an object', function() { expect(function() { dataArray(string); - }).to.throwError(); + }).toThrow(); expect(function() { dataArray(number); - }).to.throwError(); + }).toThrow(); expect(function() { dataArray(boolean); - }).to.throwError(); + }).toThrow(); expect(function() { dataArray(emptyArray); - }).to.throwError(); + }).toThrow(); expect(function() { dataArray(nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { dataArray(notAValue); - }).to.throwError(); + }).toThrow(); }); it( @@ -259,7 +258,7 @@ describe('Vislib Labels Module Test Suite', function() { function() { expect(function() { dataArray(childrenObject); - }).to.throwError(); + }).toThrow(); } ); @@ -268,42 +267,42 @@ describe('Vislib Labels Module Test Suite', function() { function() { expect(function() { dataArray(seriesObject); - }).to.not.throwError(); + }).not.toThrow(); expect(function() { dataArray(rowsObject); - }).to.not.throwError(); + }).not.toThrow(); expect(function() { dataArray(columnsObject); - }).to.not.throwError(); + }).not.toThrow(); } ); it('should be a function', function() { - expect(typeof dataArray).to.equal('function'); + expect(typeof dataArray).toEqual('function'); }); it('should return an array of objects if input is data.series', function() { - expect(testSeries).to.equal(true); + expect(testSeries).toEqual(true); }); it('should return an array of objects if input is data.rows', function() { - expect(testRows).to.equal(true); + expect(testRows).toEqual(true); }); it('should return an array of same length as input data.series', function() { - expect(seriesLabels.length).to.equal(seriesData.series.length); + expect(seriesLabels.length).toEqual(seriesData.series.length); }); it('should return an array of same length as input data.rows', function() { - expect(rowsLabels.length).to.equal(rowsData.rows.length); + expect(rowsLabels.length).toEqual(rowsData.rows.length); }); it('should return an array of objects with obj.labels and obj.values', function() { - expect(seriesLabels[0].label).to.equal('100'); - expect(seriesLabels[0].values[0].x).to.equal(0); - expect(seriesLabels[0].values[0].y).to.equal(1); + expect(seriesLabels[0].label).toEqual('100'); + expect(seriesLabels[0].values[0].x).toEqual(0); + expect(seriesLabels[0].values[0].y).toEqual(1); }); }); @@ -337,45 +336,45 @@ describe('Vislib Labels Module Test Suite', function() { it('should throw an error if input is not an array', function() { expect(function() { uniqLabels(string); - }).to.throwError(); + }).toThrow(); expect(function() { uniqLabels(number); - }).to.throwError(); + }).toThrow(); expect(function() { uniqLabels(boolean); - }).to.throwError(); + }).toThrow(); expect(function() { uniqLabels(nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { uniqLabels(emptyObject); - }).to.throwError(); + }).toThrow(); expect(function() { uniqLabels(notAValue); - }).to.throwError(); + }).toThrow(); }); it('should not throw an error if the input is an array', function() { expect(function() { uniqLabels(emptyArray); - }).to.not.throwError(); + }).not.toThrow(); }); it('should be a function', function() { - expect(typeof uniqLabels).to.be('function'); + expect(typeof uniqLabels).toBe('function'); }); it('should return an array', function() { - expect(testArr).to.be(true); + expect(testArr).toBe(true); }); it('should return array of 5 unique values', function() { - expect(uniq.length).to.be(5); + expect(uniq.length).toBe(5); }); }); @@ -408,63 +407,63 @@ describe('Vislib Labels Module Test Suite', function() { it('should throw an error if input is not an object', function() { expect(function() { getSeries(string); - }).to.throwError(); + }).toThrow(); expect(function() { getSeries(number); - }).to.throwError(); + }).toThrow(); expect(function() { getSeries(boolean); - }).to.throwError(); + }).toThrow(); expect(function() { getSeries(nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { getSeries(emptyArray); - }).to.throwError(); + }).toThrow(); expect(function() { getSeries(notAValue); - }).to.throwError(); + }).toThrow(); }); it('should throw an if property rows or columns is not set on the object', function() { expect(function() { getSeries(emptyObject); - }).to.throwError(); + }).toThrow(); }); it('should not throw an error if rows or columns set on object', function() { expect(function() { getSeries(rowsObject); - }).to.not.throwError(); + }).not.toThrow(); expect(function() { getSeries(columnsObject); - }).to.not.throwError(); + }).not.toThrow(); }); it('should be a function', function() { - expect(typeof getSeries).to.be('function'); + expect(typeof getSeries).toBe('function'); }); it('should return an array if input is data.columns', function() { - expect(columnsArr).to.be(true); + expect(columnsArr).toBe(true); }); it('should return an array if input is data.rows', function() { - expect(rowsArr).to.be(true); + expect(rowsArr).toBe(true); }); it('should return an array of the same length as as input data.columns', function() { - expect(columnsLabels.length).to.be(columnsData.columns.length); + expect(columnsLabels.length).toBe(columnsData.columns.length); }); it('should return an array of the same length as as input data.rows', function() { - expect(rowsLabels.length).to.be(rowsData.rows.length); + expect(rowsLabels.length).toBe(rowsData.rows.length); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js b/src/plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js rename to src/plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap rename to src/plugins/vis_type_vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss b/src/plugins/vis_type_vislib/public/vislib/components/legend/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/components/legend/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss b/src/plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss rename to src/plugins/vis_type_vislib/public/vislib/components/legend/_legend.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/index.ts b/src/plugins/vis_type_vislib/public/vislib/components/legend/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/index.ts rename to src/plugins/vis_type_vislib/public/vislib/components/legend/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx b/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx rename to src/plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx index 6bf66c2bdd788b..c203642f353c53 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx +++ b/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.test.tsx @@ -31,10 +31,6 @@ jest.mock('@elastic/eui', () => ({ htmlIdGenerator: jest.fn().mockReturnValue(() => 'legendId'), })); -jest.mock('../../../legacy_imports', () => ({ - getTableAggs: jest.fn(), -})); - jest.mock('../../../services', () => ({ getDataActions: () => ({ createFiltersFromValueClickAction: jest.fn().mockResolvedValue(['yes']), diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx b/src/plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx rename to src/plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx b/src/plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx rename to src/plugins/vis_type_vislib/public/vislib/components/legend/legend_item.tsx diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/models.ts b/src/plugins/vis_type_vislib/public/vislib/components/legend/models.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/models.ts rename to src/plugins/vis_type_vislib/public/vislib/components/legend/models.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts b/src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts rename to src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts index d9eea83d40b48c..0167a542c6372e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts +++ b/src/plugins/vis_type_vislib/public/vislib/components/legend/pie_utils.ts @@ -25,7 +25,7 @@ import _ from 'lodash'; * * > Duplicated utilty method from vislib Data class to decouple `vislib_vis_legend` from `vislib` * - * @see src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js + * @see src/plugins/vis_type_vislib/public/vislib/lib/data.js * * @returns {Array} Array of unique names (strings) */ diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_collect_branch.test.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_hierarchical_tooltip_formatter.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js similarity index 83% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js index a3aabcb90be624..953c115cc0d024 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/_tooltip_formatter.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_pointseries_tooltip_formatter.test.js @@ -19,9 +19,8 @@ import _ from 'lodash'; import $ from 'jquery'; -import expect from '@kbn/expect'; -import { pointSeriesTooltipFormatter } from '../../components/tooltip'; +import { pointSeriesTooltipFormatter } from './_pointseries_tooltip_formatter'; describe('tooltipFormatter', function() { const tooltipFormatter = pointSeriesTooltipFormatter(); @@ -64,19 +63,19 @@ describe('tooltipFormatter', function() { const event = _.cloneDeep(baseEvent); const $el = $(tooltipFormatter(event)); const $rows = $el.find('tr'); - expect($rows.length).to.be(3); + expect($rows.length).toBe(3); const $row1 = $rows.eq(0).find('td'); - expect(cell($row1, 0)).to.be('inner'); - expect(cell($row1, 1)).to.be('3'); + expect(cell($row1, 0)).toBe('inner'); + expect(cell($row1, 1)).toBe('3'); const $row2 = $rows.eq(1).find('td'); - expect(cell($row2, 0)).to.be('middle'); - expect(cell($row2, 1)).to.be('2'); + expect(cell($row2, 0)).toBe('middle'); + expect(cell($row2, 1)).toBe('2'); const $row3 = $rows.eq(2).find('td'); - expect(cell($row3, 0)).to.be('top'); - expect(cell($row3, 1)).to.be('1'); + expect(cell($row3, 0)).toBe('top'); + expect(cell($row3, 1)).toBe('1'); }); it('renders correctly on missing extraMetrics in datum', function() { @@ -84,6 +83,6 @@ describe('tooltipFormatter', function() { delete event.datum.extraMetrics; const $el = $(tooltipFormatter(event)); const $rows = $el.find('tr'); - expect($rows.length).to.be(3); + expect($rows.length).toBe(3); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/_tooltip.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/index.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/index.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/index.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/index.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.test.js similarity index 82% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.test.js index f1c80c99810204..c184d0e9fbcf7f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/positioning.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/position_tooltip.test.js @@ -17,12 +17,11 @@ * under the License. */ -import expect from '@kbn/expect'; import $ from 'jquery'; import _ from 'lodash'; import sinon from 'sinon'; -import { positionTooltip } from '../../components/tooltip/position_tooltip'; +import { positionTooltip } from './position_tooltip'; describe('Tooltip Positioning', function() { const sandbox = sinon.createSandbox(); @@ -95,11 +94,11 @@ describe('Tooltip Positioning', function() { positionTooltip.getTtSize($tooltip.html(), $sizer); [w, h].forEach(function(spy) { - expect(spy).to.have.property('callCount', 1); + expect(spy).toHaveProperty('callCount', 1); const matchHtml = w.thisValues.filter(function($t) { return !$t.is($tooltip) && $t.html() === $tooltip.html(); }); - expect(matchHtml).to.have.length(1); + expect(matchHtml).toHaveLength(1); }); }); }); @@ -110,11 +109,11 @@ describe('Tooltip Positioning', function() { const pos = positionTooltip.getBasePosition(size, makeEvent()); positions.forEach(function(p) { - expect(pos).to.have.property(p); + expect(pos).toHaveProperty(p); }); - expect(pos.north).to.be.lessThan(pos.south); - expect(pos.east).to.be.greaterThan(pos.west); + expect(pos.north).toBeLessThan(pos.south); + expect(pos.east).toBeGreaterThan(pos.west); }); }); @@ -123,11 +122,11 @@ describe('Tooltip Positioning', function() { const cbounds = positionTooltip.getBounds($chart); bounds.forEach(function(b) { - expect(cbounds).to.have.property(b); + expect(cbounds).toHaveProperty(b); }); - expect(cbounds.top).to.be.lessThan(cbounds.bottom); - expect(cbounds.left).to.be.lessThan(cbounds.right); + expect(cbounds.top).toBeLessThan(cbounds.bottom); + expect(cbounds.left).toBeLessThan(cbounds.right); }); }); @@ -137,18 +136,18 @@ describe('Tooltip Positioning', function() { $tooltip.css({ width: 15, height: 15 }); $sizer.css({ width: 15, height: 15 }); const size = positionTooltip.getTtSize($tooltip.html(), $sizer); - expect(size).to.have.property('width', 15); - expect(size).to.have.property('height', 15); + expect(size).toHaveProperty('width', 15); + expect(size).toHaveProperty('height', 15); // position the element based on a mouse that is in the middle of the chart const pos = positionTooltip.getBasePosition(size, makeEvent(0.5, 0.5)); const overflow = positionTooltip.getOverflow(size, pos, [$chart, $window]); positions.forEach(function(p) { - expect(overflow).to.have.property(p); + expect(overflow).toHaveProperty(p); // all positions should be less than 0 because the tooltip is so much smaller than the chart - expect(overflow[p]).to.be.lessThan(0); + expect(overflow[p]).toBeLessThan(0); }); }); @@ -160,12 +159,12 @@ describe('Tooltip Positioning', function() { const overflow = positionTooltip.getOverflow(size, pos, [$chart, $window]); positions.forEach(function(p) { - expect(overflow).to.have.property(p); + expect(overflow).toHaveProperty(p); if (p === 'south' || p === 'east') { - expect(overflow[p]).to.be.greaterThan(0); + expect(overflow[p]).toBeGreaterThan(0); } else { - expect(overflow[p]).to.be.lessThan(0); + expect(overflow[p]).toBeLessThan(0); } }); }); @@ -176,10 +175,10 @@ describe('Tooltip Positioning', function() { $tooltip.css({ width: largeWidth }); $sizer.css({ width: largeWidth }); const size = positionTooltip.getTtSize($tooltip.html(), $sizer); - expect(size).to.have.property('width', largeWidth); + expect(size).toHaveProperty('width', largeWidth); // $chart is flush with the $window on the left side - expect(positionTooltip.getBounds($chart).left).to.be(0); + expect(positionTooltip.getBounds($chart).left).toBe(0); // Size $window large enough for tooltip on right side $window.css({ width: $chart.width() * 3 }); @@ -190,17 +189,17 @@ describe('Tooltip Positioning', function() { const overflow = positionTooltip.getOverflow(size, pos, [$chart, $window]); // no overflow on left (east) - expect(overflow.east).to.be.lessThan(0); + expect(overflow.east).toBeLessThan(0); // overflow on right (west) - expect(overflow.west).to.be.greaterThan(0); + expect(overflow.west).toBeGreaterThan(0); }); }); describe('positionTooltip() integration', function() { it('returns nothing if the $chart or $tooltip are not passed in', function() { - expect(positionTooltip() === void 0).to.be(true); - expect(positionTooltip(null, null, null) === void 0).to.be(true); - expect(positionTooltip(null, $(), $()) === void 0).to.be(true); + expect(positionTooltip() === void 0).toBe(true); + expect(positionTooltip(null, null, null) === void 0).toBe(true); + expect(positionTooltip(null, $(), $()) === void 0).toBe(true); }); function check(xPercent, yPercent /*, prev, directions... */) { @@ -215,23 +214,22 @@ describe('Tooltip Positioning', function() { prev: _.isObject(directions[0]) ? directions.shift() : null, }); - expect(placement) - .to.have.property('top') - .and.property('left'); + expect(placement).toHaveProperty('top'); + expect(placement).toHaveProperty('left'); directions.forEach(function(dir) { switch (dir) { case 'top': - expect(placement.top).to.be.lessThan(event.clientY); + expect(placement.top).toBeLessThan(event.clientY); return; case 'bottom': - expect(placement.top).to.be.greaterThan(event.clientY); + expect(placement.top).toBeGreaterThan(event.clientY); return; case 'right': - expect(placement.left).to.be.greaterThan(event.clientX); + expect(placement.left).toBeGreaterThan(event.clientX); return; case 'left': - expect(placement.left).to.be.lessThan(event.clientX); + expect(placement.left).toBeLessThan(event.clientX); return; } }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js b/src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js rename to src/plugins/vis_type_vislib/public/vislib/components/tooltip/tooltip.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js similarity index 65% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js rename to src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js index 129006cdf0ca37..df502b7cde3dfb 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js @@ -18,7 +18,6 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; import { injectZeros } from './inject_zeros'; import { orderXValues } from './ordered_x_keys'; import { getUniqKeys } from './uniq_keys'; @@ -159,45 +158,45 @@ describe('Vislib Zero Injection Module Test Suite', function() { }); it('should be a function', function() { - expect(_.isFunction(injectZeros)).to.be(true); + expect(_.isFunction(injectZeros)).toBe(true); }); it('should return an object with series[0].values', function() { - expect(_.isObject(sample1)).to.be(true); - expect(_.isObject(sample1[0].values)).to.be(true); + expect(_.isObject(sample1)).toBe(true); + expect(_.isObject(sample1[0].values)).toBe(true); }); it('should return the same array of objects when the length of the series array is 1', function() { - expect(sample1[0].values[0].x).to.be(seriesData[0].values[0].x); - expect(sample1[0].values[1].x).to.be(seriesData[0].values[1].x); - expect(sample1[0].values[2].x).to.be(seriesData[0].values[2].x); - expect(sample1[0].values[3].x).to.be(seriesData[0].values[3].x); - expect(sample1[0].values[4].x).to.be(seriesData[0].values[4].x); + expect(sample1[0].values[0].x).toBe(seriesData[0].values[0].x); + expect(sample1[0].values[1].x).toBe(seriesData[0].values[1].x); + expect(sample1[0].values[2].x).toBe(seriesData[0].values[2].x); + expect(sample1[0].values[3].x).toBe(seriesData[0].values[3].x); + expect(sample1[0].values[4].x).toBe(seriesData[0].values[4].x); }); it('should inject zeros in the input array', function() { - expect(sample2[1].values[1].y).to.be(0); - expect(sample2[2].values[0].y).to.be(0); - expect(sample2[2].values[1].y).to.be(0); - expect(sample2[2].values[4].y).to.be(0); - expect(sample3[1].values[1].y).to.be(0); - expect(sample3[2].values[0].y).to.be(0); - expect(sample3[2].values[1].y).to.be(0); - expect(sample3[2].values[4].y).to.be(0); + expect(sample2[1].values[1].y).toBe(0); + expect(sample2[2].values[0].y).toBe(0); + expect(sample2[2].values[1].y).toBe(0); + expect(sample2[2].values[4].y).toBe(0); + expect(sample3[1].values[1].y).toBe(0); + expect(sample3[2].values[0].y).toBe(0); + expect(sample3[2].values[1].y).toBe(0); + expect(sample3[2].values[4].y).toBe(0); }); it('should return values arrays with the same x values', function() { - expect(sample2[1].values[0].x).to.be(sample2[2].values[0].x); - expect(sample2[1].values[1].x).to.be(sample2[2].values[1].x); - expect(sample2[1].values[2].x).to.be(sample2[2].values[2].x); - expect(sample2[1].values[3].x).to.be(sample2[2].values[3].x); - expect(sample2[1].values[4].x).to.be(sample2[2].values[4].x); + expect(sample2[1].values[0].x).toBe(sample2[2].values[0].x); + expect(sample2[1].values[1].x).toBe(sample2[2].values[1].x); + expect(sample2[1].values[2].x).toBe(sample2[2].values[2].x); + expect(sample2[1].values[3].x).toBe(sample2[2].values[3].x); + expect(sample2[1].values[4].x).toBe(sample2[2].values[4].x); }); it('should return values arrays of the same length', function() { - expect(sample2[0].values.length).to.be(sample2[1].values.length); - expect(sample2[0].values.length).to.be(sample2[2].values.length); - expect(sample2[1].values.length).to.be(sample2[2].values.length); + expect(sample2[0].values.length).toBe(sample2[1].values.length); + expect(sample2[0].values.length).toBe(sample2[2].values.length); + expect(sample2[1].values.length).toBe(sample2[2].values.length); }); }); @@ -211,24 +210,24 @@ describe('Vislib Zero Injection Module Test Suite', function() { }); it('should return a function', function() { - expect(_.isFunction(orderXValues)).to.be(true); + expect(_.isFunction(orderXValues)).toBe(true); }); it('should return an array', function() { - expect(Array.isArray(results)).to.be(true); + expect(Array.isArray(results)).toBe(true); }); it('should return an array of values ordered by their index by default', function() { - expect(results[0]).to.be('1'); - expect(results[1]).to.be('2'); - expect(results[2]).to.be('3'); - expect(results[3]).to.be('4'); - expect(results[4]).to.be('5'); - expect(numberedResults[0]).to.be(1); - expect(numberedResults[1]).to.be(2); - expect(numberedResults[2]).to.be(3); - expect(numberedResults[3]).to.be(4); - expect(numberedResults[4]).to.be(5); + expect(results[0]).toBe('1'); + expect(results[1]).toBe('2'); + expect(results[2]).toBe('3'); + expect(results[3]).toBe('4'); + expect(results[4]).toBe('5'); + expect(numberedResults[0]).toBe(1); + expect(numberedResults[1]).toBe(2); + expect(numberedResults[2]).toBe(3); + expect(numberedResults[3]).toBe(4); + expect(numberedResults[4]).toBe(5); }); it('should return an array of values that preserve the index from xAxisOrderedValues', function() { @@ -257,7 +256,7 @@ describe('Vislib Zero Injection Module Test Suite', function() { ], }; const result = orderXValues(data); - expect(result).to.eql(['1', '2', '3', '4', '5']); + expect(result).toEqual(['1', '2', '3', '4', '5']); }); it('should return an array of values ordered by their sum when orderBucketsBySum is true', function() { @@ -265,16 +264,16 @@ describe('Vislib Zero Injection Module Test Suite', function() { results = orderXValues(multiSeriesDataObj, orderBucketsBySum); numberedResults = orderXValues(multiSeriesNumberedDataObj, orderBucketsBySum); - expect(results[0]).to.be('3'); - expect(results[1]).to.be('1'); - expect(results[2]).to.be('4'); - expect(results[3]).to.be('5'); - expect(results[4]).to.be('2'); - expect(numberedResults[0]).to.be(3); - expect(numberedResults[1]).to.be(1); - expect(numberedResults[2]).to.be(4); - expect(numberedResults[3]).to.be(5); - expect(numberedResults[4]).to.be(2); + expect(results[0]).toBe('3'); + expect(results[1]).toBe('1'); + expect(results[2]).toBe('4'); + expect(results[3]).toBe('5'); + expect(results[4]).toBe('2'); + expect(numberedResults[0]).toBe(3); + expect(numberedResults[1]).toBe(1); + expect(numberedResults[2]).toBe(4); + expect(numberedResults[3]).toBe(5); + expect(numberedResults[4]).toBe(2); }); }); @@ -288,39 +287,39 @@ describe('Vislib Zero Injection Module Test Suite', function() { it('should throw an error if input is not an object', function() { expect(function() { getUniqKeys(str); - }).to.throwError(); + }).toThrow(); expect(function() { getUniqKeys(number); - }).to.throwError(); + }).toThrow(); expect(function() { getUniqKeys(boolean); - }).to.throwError(); + }).toThrow(); expect(function() { getUniqKeys(nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { getUniqKeys(emptyArray); - }).to.throwError(); + }).toThrow(); expect(function() { getUniqKeys(notAValue); - }).to.throwError(); + }).toThrow(); }); it('should return a function', function() { - expect(_.isFunction(getUniqKeys)).to.be(true); + expect(_.isFunction(getUniqKeys)).toBe(true); }); it('should return an object', function() { - expect(_.isObject(results)).to.be(true); + expect(_.isObject(results)).toBe(true); }); it('should return an object of unique keys', function() { - expect(_.uniq(_.keys(results)).length).to.be(_.keys(results).length); + expect(_.uniq(_.keys(results)).length).toBe(_.keys(results).length); }); }); @@ -332,17 +331,17 @@ describe('Vislib Zero Injection Module Test Suite', function() { }); it('should return a function', function() { - expect(_.isFunction(flattenData)).to.be(true); + expect(_.isFunction(flattenData)).toBe(true); }); it('should return an array', function() { - expect(Array.isArray(results)).to.be(true); + expect(Array.isArray(results)).toBe(true); }); it('should return an array of objects', function() { - expect(_.isObject(results[0])).to.be(true); - expect(_.isObject(results[1])).to.be(true); - expect(_.isObject(results[2])).to.be(true); + expect(_.isObject(results[0])).toBe(true); + expect(_.isObject(results[1])).toBe(true); + expect(_.isObject(results[2])).toBe(true); }); }); @@ -360,67 +359,67 @@ describe('Vislib Zero Injection Module Test Suite', function() { it('should throw an error if input is not an array', function() { expect(function() { createZeroFilledArray(str); - }).to.throwError(); + }).toThrow(); expect(function() { createZeroFilledArray(number); - }).to.throwError(); + }).toThrow(); expect(function() { createZeroFilledArray(boolean); - }).to.throwError(); + }).toThrow(); expect(function() { createZeroFilledArray(nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { createZeroFilledArray(emptyObject); - }).to.throwError(); + }).toThrow(); expect(function() { createZeroFilledArray(notAValue); - }).to.throwError(); + }).toThrow(); }); it('should return a function', function() { - expect(_.isFunction(createZeroFilledArray)).to.be(true); + expect(_.isFunction(createZeroFilledArray)).toBe(true); }); it('should return an array', function() { - expect(Array.isArray(results1)).to.be(true); + expect(Array.isArray(results1)).toBe(true); }); it('should return an array of objects', function() { - expect(_.isObject(results1[0])).to.be(true); - expect(_.isObject(results1[1])).to.be(true); - expect(_.isObject(results1[2])).to.be(true); - expect(_.isObject(results1[3])).to.be(true); - expect(_.isObject(results1[4])).to.be(true); + expect(_.isObject(results1[0])).toBe(true); + expect(_.isObject(results1[1])).toBe(true); + expect(_.isObject(results1[2])).toBe(true); + expect(_.isObject(results1[3])).toBe(true); + expect(_.isObject(results1[4])).toBe(true); }); it('should return an array of objects where each y value is 0', function() { - expect(results1[0].y).to.be(0); - expect(results1[1].y).to.be(0); - expect(results1[2].y).to.be(0); - expect(results1[3].y).to.be(0); - expect(results1[4].y).to.be(0); + expect(results1[0].y).toBe(0); + expect(results1[1].y).toBe(0); + expect(results1[2].y).toBe(0); + expect(results1[3].y).toBe(0); + expect(results1[4].y).toBe(0); }); it('should return an array of objects where each x values are numbers', function() { - expect(_.isNumber(results1[0].x)).to.be(true); - expect(_.isNumber(results1[1].x)).to.be(true); - expect(_.isNumber(results1[2].x)).to.be(true); - expect(_.isNumber(results1[3].x)).to.be(true); - expect(_.isNumber(results1[4].x)).to.be(true); + expect(_.isNumber(results1[0].x)).toBe(true); + expect(_.isNumber(results1[1].x)).toBe(true); + expect(_.isNumber(results1[2].x)).toBe(true); + expect(_.isNumber(results1[3].x)).toBe(true); + expect(_.isNumber(results1[4].x)).toBe(true); }); it('should return an array of objects where each x values are strings', function() { - expect(_.isString(results2[0].x)).to.be(true); - expect(_.isString(results2[1].x)).to.be(true); - expect(_.isString(results2[2].x)).to.be(true); - expect(_.isString(results2[3].x)).to.be(true); - expect(_.isString(results2[4].x)).to.be(true); + expect(_.isString(results2[0].x)).toBe(true); + expect(_.isString(results2[1].x)).toBe(true); + expect(_.isString(results2[2].x)).toBe(true); + expect(_.isString(results2[3].x)).toBe(true); + expect(_.isString(results2[4].x)).toBe(true); }); }); @@ -439,48 +438,48 @@ describe('Vislib Zero Injection Module Test Suite', function() { it('should throw an error if input are not arrays', function() { expect(function() { zeroFillDataArray(str, str); - }).to.throwError(); + }).toThrow(); expect(function() { zeroFillDataArray(number, number); - }).to.throwError(); + }).toThrow(); expect(function() { zeroFillDataArray(boolean, boolean); - }).to.throwError(); + }).toThrow(); expect(function() { zeroFillDataArray(nullValue, nullValue); - }).to.throwError(); + }).toThrow(); expect(function() { zeroFillDataArray(emptyObject, emptyObject); - }).to.throwError(); + }).toThrow(); expect(function() { zeroFillDataArray(notAValue, notAValue); - }).to.throwError(); + }).toThrow(); }); it('should return a function', function() { - expect(_.isFunction(zeroFillDataArray)).to.be(true); + expect(_.isFunction(zeroFillDataArray)).toBe(true); }); it('should return an array', function() { - expect(Array.isArray(results)).to.be(true); + expect(Array.isArray(results)).toBe(true); }); it('should return an array of objects', function() { - expect(_.isObject(results[0])).to.be(true); - expect(_.isObject(results[1])).to.be(true); - expect(_.isObject(results[2])).to.be(true); + expect(_.isObject(results[0])).toBe(true); + expect(_.isObject(results[1])).toBe(true); + expect(_.isObject(results[2])).toBe(true); }); it('should return an array with zeros injected in the appropriate objects as y values', function() { - expect(results[0].y).to.be(0); - expect(results[1].y).to.be(0); - expect(results[3].y).to.be(0); - expect(results[4].y).to.be(0); + expect(results[0].y).toBe(0); + expect(results[1].y).toBe(0); + expect(results[3].y).toBe(0); + expect(results[4].y).toBe(0); }); }); @@ -493,18 +492,18 @@ describe('Vislib Zero Injection Module Test Suite', function() { it('should return an array of objects', function() { results.forEach(function(row) { - expect(Array.isArray(row.values)).to.be(true); + expect(Array.isArray(row.values)).toBe(true); }); }); it('should return ordered x values', function() { const values = results[0].values; - expect(values[0].x).to.be.lessThan(values[1].x); - expect(values[1].x).to.be.lessThan(values[2].x); - expect(values[2].x).to.be.lessThan(values[3].x); - expect(values[3].x).to.be.lessThan(values[4].x); - expect(values[4].x).to.be.lessThan(values[5].x); - expect(values[5].x).to.be.lessThan(values[6].x); + expect(values[0].x).toBeLessThan(values[1].x); + expect(values[1].x).toBeLessThan(values[2].x); + expect(values[2].x).toBeLessThan(values[3].x); + expect(values[3].x).toBeLessThan(values[4].x); + expect(values[4].x).toBeLessThan(values[5].x); + expect(values[5].x).toBeLessThan(values[6].x); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts b/src/plugins/vis_type_vislib/public/vislib/errors.ts similarity index 95% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts rename to src/plugins/vis_type_vislib/public/vislib/errors.ts index 9014349c38d253..c2965e81657596 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts +++ b/src/plugins/vis_type_vislib/public/vislib/errors.ts @@ -19,7 +19,7 @@ /* eslint-disable max-classes-per-file */ -import { KbnError } from '../../../../../plugins/kibana_utils/public'; +import { KbnError } from '../../../kibana_utils/public'; export class VislibError extends KbnError { constructor(message: string) { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts index 2c6d62ed084b57..c3b82f72af482e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts +++ b/src/plugins/vis_type_vislib/public/vislib/helpers/hierarchical/build_hierarchical_data.ts @@ -18,7 +18,7 @@ */ import { toArray } from 'lodash'; -import { SerializedFieldFormat } from '../../../../../../../plugins/expressions/common/types'; +import { SerializedFieldFormat } from '../../../../../expressions/common/types'; import { getFormatService } from '../../../services'; import { Table } from '../../types'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/index.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/index.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_add_to_siri.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_fake_x_aspect.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_aspects.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts similarity index 97% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts index 0c79c5b263ceac..dc10c9f4938a0e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts +++ b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { IFieldFormatsRegistry } from '../../../../../../../plugins/data/common'; +import { IFieldFormatsRegistry } from '../../../../../data/common'; import { getPoint } from './_get_point'; import { setFormatService } from '../../../services'; import { Aspect } from './point_series'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_point.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_get_series.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_x_axis.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_init_y_axis.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/_ordered_date_axis.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/index.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts b/src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts rename to src/plugins/vis_type_vislib/public/vislib/helpers/point_series/point_series.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap b/src/plugins/vis_type_vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap rename to src/plugins/vis_type_vislib/public/vislib/lib/__snapshots__/dispatch_heatmap.test.js.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_alerts.scss b/src/plugins/vis_type_vislib/public/vislib/lib/_alerts.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_alerts.scss rename to src/plugins/vis_type_vislib/public/vislib/lib/_alerts.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_data_label.js b/src/plugins/vis_type_vislib/public/vislib/lib/_data_label.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_data_label.js rename to src/plugins/vis_type_vislib/public/vislib/lib/_data_label.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_error_handler.js b/src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_error_handler.js rename to src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js b/src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.test.js similarity index 88% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js rename to src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.test.js index 4523e70ccbb4cb..b7764b92b78059 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/_error_handler.test.js @@ -17,9 +17,7 @@ * under the License. */ -import expect from '@kbn/expect'; - -import { ErrorHandler } from '../../lib/_error_handler'; +import { ErrorHandler } from './_error_handler'; describe('Vislib ErrorHandler Test Suite', function() { let errorHandler; @@ -32,19 +30,19 @@ describe('Vislib ErrorHandler Test Suite', function() { it('should throw an error when width and/or height is 0', function() { expect(function() { errorHandler.validateWidthandHeight(0, 200); - }).to.throwError(); + }).toThrow(); expect(function() { errorHandler.validateWidthandHeight(200, 0); - }).to.throwError(); + }).toThrow(); }); it('should throw an error when width and/or height is NaN', function() { expect(function() { errorHandler.validateWidthandHeight(null, 200); - }).to.throwError(); + }).toThrow(); expect(function() { errorHandler.validateWidthandHeight(200, null); - }).to.throwError(); + }).toThrow(); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_handler.scss b/src/plugins/vis_type_vislib/public/vislib/lib/_handler.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_handler.scss rename to src/plugins/vis_type_vislib/public/vislib/lib/_handler.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_index.scss b/src/plugins/vis_type_vislib/public/vislib/lib/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/lib/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/alerts.js b/src/plugins/vis_type_vislib/public/vislib/lib/alerts.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/alerts.js rename to src/plugins/vis_type_vislib/public/vislib/lib/alerts.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.test.js similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.test.js index bc4a4f9925513c..dec7de5ceeda95 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis.test.js @@ -19,12 +19,11 @@ import d3 from 'd3'; import _ from 'lodash'; -import expect from '@kbn/expect'; import $ from 'jquery'; -import { Axis } from '../../../lib/axis'; -import { VisConfig } from '../../../lib/vis_config'; -import { getMockUiState } from '../fixtures/_vis_fixture'; +import { Axis } from './axis'; +import { VisConfig } from '../vis_config'; +import { getMockUiState } from '../../../fixtures/mocks'; describe('Vislib Axis Class Test Suite', function() { let mockUiState; @@ -164,7 +163,7 @@ describe('Vislib Axis Class Test Suite', function() { }, ]; const stackedData = yAxis._stackNegAndPosVals(seriesData); - expect(stackedData[1]).to.eql(expectedResult); + expect(stackedData[1]).toEqual(expectedResult); }); it('should correctly stack pos and neg values', function() { @@ -200,7 +199,7 @@ describe('Vislib Axis Class Test Suite', function() { value.y = -value.y; }); const stackedData = yAxis._stackNegAndPosVals(dataClone); - expect(stackedData[1]).to.eql(expectedResult); + expect(stackedData[1]).toEqual(expectedResult); }); it('should correctly stack mixed pos and neg values', function() { @@ -236,7 +235,7 @@ describe('Vislib Axis Class Test Suite', function() { if (i % 2 === 1) value.y = -value.y; }); const stackedData = yAxis._stackNegAndPosVals(dataClone); - expect(stackedData[1]).to.eql(expectedResult); + expect(stackedData[1]).toEqual(expectedResult); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.test.js similarity index 91% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.test.js index fd25335dd2cd4c..7901919d306d27 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.test.js @@ -20,13 +20,12 @@ import d3 from 'd3'; import _ from 'lodash'; import $ from 'jquery'; -import expect from '@kbn/expect'; -import { AxisTitle } from '../../lib/axis/axis_title'; -import { AxisConfig } from '../../lib/axis/axis_config'; -import { VisConfig } from '../../lib/vis_config'; -import { Data } from '../../lib/data'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { AxisTitle } from './axis_title'; +import { AxisConfig } from './axis_config'; +import { VisConfig } from '../vis_config'; +import { Data } from '../data'; +import { getMockUiState } from '../../../fixtures/mocks'; describe('Vislib AxisTitle Class Test Suite', function() { let el; @@ -159,7 +158,7 @@ describe('Vislib AxisTitle Class Test Suite', function() { $(el.node()) .find('.x-axis-title') .find('svg').length - ).to.be(0); + ).toBe(0); }); describe('render Method', function() { @@ -169,8 +168,8 @@ describe('Vislib AxisTitle Class Test Suite', function() { }); it('should append an svg to div', function() { - expect(el.select('.x-axis-title').selectAll('svg').length).to.be(1); - expect(el.select('.y-axis-title').selectAll('svg').length).to.be(1); + expect(el.select('.x-axis-title').selectAll('svg').length).toBe(1); + expect(el.select('.y-axis-title').selectAll('svg').length).toBe(1); }); it('should append a g element to the svg', function() { @@ -179,13 +178,13 @@ describe('Vislib AxisTitle Class Test Suite', function() { .select('.x-axis-title') .selectAll('svg') .select('g').length - ).to.be(1); + ).toBe(1); expect( el .select('.y-axis-title') .selectAll('svg') .select('g').length - ).to.be(1); + ).toBe(1); }); it('should append text', function() { @@ -194,19 +193,19 @@ describe('Vislib AxisTitle Class Test Suite', function() { .select('.x-axis-title') .selectAll('svg') .selectAll('text') - ).to.be(true); + ).toBe(true); expect( !!el .select('.y-axis-title') .selectAll('svg') .selectAll('text') - ).to.be(true); + ).toBe(true); }); }); describe('draw Method', function() { it('should be a function', function() { - expect(_.isFunction(xTitle.draw())).to.be(true); + expect(_.isFunction(xTitle.draw())).toBe(true); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/index.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/index.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/index.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/index.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/x_axis.test.js similarity index 79% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/x_axis.test.js index d42562a87b825a..d007a8a14de131 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/x_axis.test.js @@ -19,12 +19,11 @@ import d3 from 'd3'; import _ from 'lodash'; -import expect from '@kbn/expect'; import $ from 'jquery'; -import { Axis } from '../../lib/axis'; -import { VisConfig } from '../../lib/vis_config'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { Axis } from './axis'; +import { VisConfig } from '../vis_config'; +import { getMockUiState } from '../../../fixtures/mocks'; describe('Vislib xAxis Class Test Suite', function() { let mockUiState; @@ -141,15 +140,15 @@ describe('Vislib xAxis Class Test Suite', function() { }); it('should append an svg to div', function() { - expect(el.selectAll('svg').length).to.be(1); + expect(el.selectAll('svg').length).toBe(1); }); it('should append a g element to the svg', function() { - expect(el.selectAll('svg').select('g').length).to.be(1); + expect(el.selectAll('svg').select('g').length).toBe(1); }); it('should append ticks with text', function() { - expect(!!el.selectAll('svg').selectAll('.tick text')).to.be(true); + expect(!!el.selectAll('svg').selectAll('.tick text')).toBe(true); }); }); @@ -166,22 +165,22 @@ describe('Vislib xAxis Class Test Suite', function() { }); it('should return a function', function() { - expect(_.isFunction(timeScale)).to.be(true); + expect(_.isFunction(timeScale)).toBe(true); }); it('should return the correct domain', function() { - expect(_.isDate(timeScale.domain()[0])).to.be(true); - expect(_.isDate(timeScale.domain()[1])).to.be(true); + expect(_.isDate(timeScale.domain()[0])).toBe(true); + expect(_.isDate(timeScale.domain()[1])).toBe(true); }); it('should return the min and max dates', function() { - expect(timeScale.domain()[0].toDateString()).to.be(new Date(1408734060000).toDateString()); - expect(timeScale.domain()[1].toDateString()).to.be(new Date(1408734330000).toDateString()); + expect(timeScale.domain()[0].toDateString()).toBe(new Date(1408734060000).toDateString()); + expect(timeScale.domain()[1].toDateString()).toBe(new Date(1408734330000).toDateString()); }); it('should return the correct range', function() { - expect(range[0]).to.be(0); - expect(range[1]).to.be(width); + expect(range[0]).toBe(0); + expect(range[1]).toBe(width); }); }); @@ -200,12 +199,12 @@ describe('Vislib xAxis Class Test Suite', function() { }); it('should return an ordinal scale', function() { - expect(ordinalDomain.domain()[0]).to.be('this'); - expect(ordinalDomain.domain()[4]).to.be('array'); + expect(ordinalDomain.domain()[0]).toBe('this'); + expect(ordinalDomain.domain()[4]).toBe('array'); }); it('should return an array of values', function() { - expect(Array.isArray(ordinalDomain.domain())).to.be(true); + expect(Array.isArray(ordinalDomain.domain())).toBe(true); }); }); @@ -220,17 +219,17 @@ describe('Vislib xAxis Class Test Suite', function() { }); it('should return a function', function() { - expect(_.isFunction(xScale)).to.be(true); + expect(_.isFunction(xScale)).toBe(true); }); it('should return a domain', function() { - expect(_.isDate(xScale.domain()[0])).to.be(true); - expect(_.isDate(xScale.domain()[1])).to.be(true); + expect(_.isDate(xScale.domain()[0])).toBe(true); + expect(_.isDate(xScale.domain()[1])).toBe(true); }); it('should return a range', function() { - expect(xScale.range()[0]).to.be(0); - expect(xScale.range()[1]).to.be(width); + expect(xScale.range()[0]).toBe(0); + expect(xScale.range()[1]).toBe(width); }); }); @@ -243,13 +242,13 @@ describe('Vislib xAxis Class Test Suite', function() { }); it('should create an getScale function on the xAxis class', function() { - expect(_.isFunction(xAxis.getScale())).to.be(true); + expect(_.isFunction(xAxis.getScale())).toBe(true); }); }); describe('draw Method', function() { it('should be a function', function() { - expect(_.isFunction(xAxis.draw())).to.be(true); + expect(_.isFunction(xAxis.draw())).toBe(true); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/y_axis.test.js similarity index 83% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js rename to src/plugins/vis_type_vislib/public/vislib/lib/axis/y_axis.test.js index f73011d6616451..85378ff1a14e87 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/y_axis.test.js @@ -20,11 +20,10 @@ import _ from 'lodash'; import d3 from 'd3'; import $ from 'jquery'; -import expect from '@kbn/expect'; -import { Axis } from '../../lib/axis'; -import { VisConfig } from '../../lib/vis_config'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { Axis } from './axis'; +import { VisConfig } from '../vis_config'; +import { getMockUiState } from '../../../fixtures/mocks'; const YAxis = Axis; let mockUiState; @@ -123,7 +122,7 @@ function createData(seriesData) { describe('Vislib yAxis Class Test Suite', function() { beforeEach(() => { mockUiState = getMockUiState(); - expect($('.y-axis-wrapper')).to.have.length(0); + expect($('.y-axis-wrapper')).toHaveLength(0); }); afterEach(function() { @@ -140,15 +139,15 @@ describe('Vislib yAxis Class Test Suite', function() { }); it('should append an svg to div', function() { - expect(el.selectAll('svg').length).to.be(1); + expect(el.selectAll('svg').length).toBe(1); }); it('should append a g element to the svg', function() { - expect(el.selectAll('svg').select('g').length).to.be(1); + expect(el.selectAll('svg').select('g').length).toBe(1); }); it('should append ticks with text', function() { - expect(!!el.selectAll('svg').selectAll('.tick text')).to.be(true); + expect(!!el.selectAll('svg').selectAll('.tick text')).toBe(true); }); }); @@ -160,14 +159,14 @@ describe('Vislib yAxis Class Test Suite', function() { function checkDomain(min, max) { const domain = yScale.domain(); - expect(domain[0]).to.be.lessThan(min + 1); - expect(domain[1]).to.be.greaterThan(max - 1); + expect(domain[0]).toBeLessThan(min + 1); + expect(domain[1]).toBeGreaterThan(max - 1); return domain; } function checkRange() { - expect(yScale.range()[0]).to.be(height); - expect(yScale.range()[1]).to.be(0); + expect(yScale.range()[0]).toBe(height); + expect(yScale.range()[1]).toBe(0); } describe('API', function() { @@ -178,7 +177,7 @@ describe('Vislib yAxis Class Test Suite', function() { }); it('should return a function', function() { - expect(_.isFunction(yScale)).to.be(true); + expect(_.isFunction(yScale)).toBe(true); }); }); @@ -194,7 +193,7 @@ describe('Vislib yAxis Class Test Suite', function() { const min = 0; const max = _.max(_.flattenDeep(graphData)); const domain = checkDomain(min, max); - expect(domain[1]).to.be.greaterThan(0); + expect(domain[1]).toBeGreaterThan(0); checkRange(); }); }); @@ -214,7 +213,7 @@ describe('Vislib yAxis Class Test Suite', function() { const min = _.min(_.flattenDeep(graphData)); const max = 0; const domain = checkDomain(min, max); - expect(domain[0]).to.be.lessThan(0); + expect(domain[0]).toBeLessThan(0); checkRange(); }); }); @@ -234,8 +233,8 @@ describe('Vislib yAxis Class Test Suite', function() { const min = _.min(_.flattenDeep(graphData)); const max = _.max(_.flattenDeep(graphData)); const domain = checkDomain(min, max); - expect(domain[0]).to.be.lessThan(0); - expect(domain[1]).to.be.greaterThan(0); + expect(domain[0]).toBeLessThan(0); + expect(domain[1]).toBeGreaterThan(0); checkRange(); }); }); @@ -255,7 +254,7 @@ describe('Vislib yAxis Class Test Suite', function() { expect(function() { yAxis.axisScale.validateUserExtents(min, max); - }).to.throwError(); + }).toThrow(); }); it('should return a decimal value', function() { @@ -267,16 +266,16 @@ describe('Vislib yAxis Class Test Suite', function() { domain[1] = 80; const newDomain = yAxis.axisScale.validateUserExtents(domain); - expect(newDomain[0]).to.be(domain[0] / 100); - expect(newDomain[1]).to.be(domain[1] / 100); + expect(newDomain[0]).toBe(domain[0] / 100); + expect(newDomain[1]).toBe(domain[1] / 100); }); it('should return the user defined value', function() { domain = [20, 50]; const newDomain = yAxis.axisScale.validateUserExtents(domain); - expect(newDomain[0]).to.be(domain[0]); - expect(newDomain[1]).to.be(domain[1]); + expect(newDomain[0]).toBe(domain[0]); + expect(newDomain[1]).toBe(domain[1]); }); }); @@ -287,7 +286,7 @@ describe('Vislib yAxis Class Test Suite', function() { expect(function() { yAxis.axisScale.validateAxisExtents(min, max); - }).to.throwError(); + }).toThrow(); }); it('min > max', function() { @@ -296,7 +295,7 @@ describe('Vislib yAxis Class Test Suite', function() { expect(function() { yAxis.axisScale.validateAxisExtents(min, max); - }).to.throwError(); + }).toThrow(); }); }); }); @@ -306,17 +305,17 @@ describe('Vislib yAxis Class Test Suite', function() { it('should return a function', function() { fnNames.forEach(function(fnName) { - expect(yAxis.axisScale.getD3Scale(fnName)).to.be.a(Function); + expect(yAxis.axisScale.getD3Scale(fnName)).toEqual(expect.any(Function)); }); // if no value is provided to the function, scale should default to a linear scale - expect(yAxis.axisScale.getD3Scale()).to.be.a(Function); + expect(yAxis.axisScale.getD3Scale()).toEqual(expect.any(Function)); }); it('should throw an error if function name is undefined', function() { expect(function() { yAxis.axisScale.getD3Scale('square'); - }).to.throwError(); + }).toThrow(); }); }); @@ -324,18 +323,18 @@ describe('Vislib yAxis Class Test Suite', function() { it('should throw an error', function() { expect(function() { yAxis.axisScale.logDomain(-10, -5); - }).to.throwError(); + }).toThrow(); expect(function() { yAxis.axisScale.logDomain(-10, 5); - }).to.throwError(); + }).toThrow(); expect(function() { yAxis.axisScale.logDomain(0, -5); - }).to.throwError(); + }).toThrow(); }); it('should return a yMin value of 1', function() { const yMin = yAxis.axisScale.logDomain(0, 200)[0]; - expect(yMin).to.be(1); + expect(yMin).toBe(1); }); }); @@ -354,7 +353,7 @@ describe('Vislib yAxis Class Test Suite', function() { it('should use decimal format for small values', function() { yAxis.yMax = 1; const tickFormat = yAxis.getAxis().tickFormat(); - expect(tickFormat(0.8)).to.be('0.8'); + expect(tickFormat(0.8)).toBe('0.8'); }); }); @@ -364,7 +363,7 @@ describe('Vislib yAxis Class Test Suite', function() { }); it('should be a function', function() { - expect(_.isFunction(yAxis.draw())).to.be(true); + expect(_.isFunction(yAxis.draw())).toBe(true); }); }); @@ -374,9 +373,9 @@ describe('Vislib yAxis Class Test Suite', function() { }); it('should return the correct number of ticks', function() { - expect(yAxis.tickScale(1000)).to.be(11); - expect(yAxis.tickScale(40)).to.be(3); - expect(yAxis.tickScale(20)).to.be(0); + expect(yAxis.tickScale(1000)).toBe(11); + expect(yAxis.tickScale(40)).toBe(3); + expect(yAxis.tickScale(20)).toBe(0); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/binder.ts b/src/plugins/vis_type_vislib/public/vislib/lib/binder.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/binder.ts rename to src/plugins/vis_type_vislib/public/vislib/lib/binder.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_grid.js b/src/plugins/vis_type_vislib/public/vislib/lib/chart_grid.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_grid.js rename to src/plugins/vis_type_vislib/public/vislib/lib/chart_grid.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js b/src/plugins/vis_type_vislib/public/vislib/lib/chart_title.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js rename to src/plugins/vis_type_vislib/public/vislib/lib/chart_title.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js b/src/plugins/vis_type_vislib/public/vislib/lib/data.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js rename to src/plugins/vis_type_vislib/public/vislib/lib/data.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js b/src/plugins/vis_type_vislib/public/vislib/lib/data.test.js similarity index 91% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js rename to src/plugins/vis_type_vislib/public/vislib/lib/data.test.js index d4ec6f363a75b0..b1a91979b3d9d8 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/data.test.js @@ -18,10 +18,9 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; -import { Data } from '../../lib/data'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { Data } from './data'; +import { getMockUiState } from '../../fixtures/mocks'; const seriesData = { label: '', @@ -160,12 +159,12 @@ describe('Vislib Data Class Test Suite', function() { describe('Data Class (main)', function() { it('should be a function', function() { - expect(_.isFunction(Data)).to.be(true); + expect(_.isFunction(Data)).toBe(true); }); it('should return an object', function() { const rowIn = new Data(rowsData, mockUiState, () => undefined); - expect(_.isObject(rowIn)).to.be(true); + expect(_.isObject(rowIn)).toBe(true); }); }); @@ -183,7 +182,7 @@ describe('Vislib Data Class Test Suite', function() { it('should remove zero values', function() { const slices = data._removeZeroSlices(data.data.slices); - expect(slices.children.length).to.be(2); + expect(slices.children.length).toBe(2); }); }); @@ -197,7 +196,7 @@ describe('Vislib Data Class Test Suite', function() { }); it('should return an array of value objects from every series', function() { - expect(serOut.every(_.isObject)).to.be(true); + expect(serOut.every(_.isObject)).toBe(true); }); it('should return all points from every series', testLength(seriesData)); @@ -220,7 +219,7 @@ describe('Vislib Data Class Test Suite', function() { 0 ); - expect(data.flatten()).to.have.length(len); + expect(data.flatten()).toHaveLength(len); }; } }); @@ -268,15 +267,15 @@ describe('Vislib Data Class Test Suite', function() { describe('getVisData', function() { it('should return the rows property', function() { const visData = data.getVisData(); - expect(visData[0].title).to.eql(geohashGridData.rows[0].title); + expect(visData[0].title).toEqual(geohashGridData.rows[0].title); }); }); describe('getGeoExtents', function() { it('should return the min and max geoJson properties', function() { const minMax = data.getGeoExtents(); - expect(minMax.min).to.be(1); - expect(minMax.max).to.be(331); + expect(minMax.min).toBe(1); + expect(minMax.max).toBe(331); }); }); }); @@ -284,7 +283,7 @@ describe('Vislib Data Class Test Suite', function() { describe('null value check', function() { it('should return false', function() { const data = new Data(rowsData, mockUiState, () => undefined); - expect(data.hasNullValues()).to.be(false); + expect(data.hasNullValues()).toBe(false); }); it('should return true', function() { @@ -304,7 +303,7 @@ describe('Vislib Data Class Test Suite', function() { }); const data = new Data(nullRowData, mockUiState, () => undefined); - expect(data.hasNullValues()).to.be(true); + expect(data.hasNullValues()).toBe(true); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/dispatch.js b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/dispatch.js rename to src/plugins/vis_type_vislib/public/vislib/lib/dispatch.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_heatmap.test.js similarity index 75% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js rename to src/plugins/vis_type_vislib/public/vislib/lib/dispatch_heatmap.test.js index e22f19ea643fd3..4e650d4c20f977 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_heatmap.test.js @@ -17,12 +17,11 @@ * under the License. */ -import mockDispatchDataD3 from './fixtures/dispatch_heatmap_d3.json'; -import { Dispatch } from '../../lib/dispatch'; -import mockdataPoint from './fixtures/dispatch_heatmap_data_point.json'; -import mockConfigPercentage from './fixtures/dispatch_heatmap_config.json'; +import mockDispatchDataD3 from '../../fixtures/dispatch_heatmap_d3.json'; +import { Dispatch } from './dispatch'; +import mockdataPoint from '../../fixtures/dispatch_heatmap_data_point.json'; +import mockConfigPercentage from '../../fixtures/dispatch_heatmap_config.json'; -jest.mock('ui/new_platform'); jest.mock('d3', () => ({ event: { target: { @@ -32,15 +31,6 @@ jest.mock('d3', () => ({ }, }, })); -jest.mock('../../../legacy_imports.ts', () => ({ - ...jest.requireActual('../../../legacy_imports.ts'), - chrome: { - getUiSettingsClient: () => ({ - get: () => '', - }), - addBasePath: () => {}, - }, -})); function getHandlerMock(config = {}, data = {}) { return { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js similarity index 74% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js rename to src/plugins/vis_type_vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js index 8fe9ac24db77b5..a680788281fb16 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/dispatch_vertical_bar_chart.test.js @@ -17,13 +17,12 @@ * under the License. */ -import mockDispatchDataD3 from './fixtures/dispatch_bar_chart_d3.json'; -import { Dispatch } from '../../lib/dispatch'; -import mockdataPoint from './fixtures/dispatch_bar_chart_data_point.json'; -import mockConfigPercentage from './fixtures/dispatch_bar_chart_config_percentage.json'; -import mockConfigNormal from './fixtures/dispatch_bar_chart_config_normal.json'; +import mockDispatchDataD3 from '../../fixtures/dispatch_bar_chart_d3.json'; +import { Dispatch } from './dispatch'; +import mockdataPoint from '../../fixtures/dispatch_bar_chart_data_point.json'; +import mockConfigPercentage from '../../fixtures/dispatch_bar_chart_config_percentage.json'; +import mockConfigNormal from '../../fixtures/dispatch_bar_chart_config_normal.json'; -jest.mock('ui/new_platform'); jest.mock('d3', () => ({ event: { target: { @@ -33,15 +32,6 @@ jest.mock('d3', () => ({ }, }, })); -jest.mock('../../../legacy_imports.ts', () => ({ - ...jest.requireActual('../../../legacy_imports.ts'), - chrome: { - getUiSettingsClient: () => ({ - get: () => '', - }), - addBasePath: () => {}, - }, -})); function getHandlerMock(config = {}, data = {}) { return { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js b/src/plugins/vis_type_vislib/public/vislib/lib/handler.js similarity index 98% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js rename to src/plugins/vis_type_vislib/public/vislib/lib/handler.js index f33ce0395af1fd..f5b1c13f1a83f7 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/handler.js @@ -29,7 +29,7 @@ import { Axis } from './axis/axis'; import { ChartGrid as Grid } from './chart_grid'; import { visTypes as chartTypes } from '../visualizations/vis_types'; import { Binder } from './binder'; -import { dispatchRenderComplete } from '../../../../../../plugins/kibana_utils/public'; +import { dispatchRenderComplete } from '../../../../kibana_utils/public'; const markdownIt = new MarkdownIt({ html: false, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss b/src/plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss b/src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/index.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/index.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/index.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/index.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/layout.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.test.js similarity index 83% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.test.js index cc6d33a2d98daf..0bc11e5124a077 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.test.js @@ -18,9 +18,8 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; -import { layoutTypes as layoutType } from '../../../lib/layout/layout_types'; +import { layoutTypes as layoutType } from './layout_types'; describe('Vislib Layout Types Test Suite', function() { let layoutFunc; @@ -30,10 +29,10 @@ describe('Vislib Layout Types Test Suite', function() { }); it('should be an object', function() { - expect(_.isObject(layoutType)).to.be(true); + expect(_.isObject(layoutType)).toBe(true); }); it('should return a function', function() { - expect(typeof layoutFunc).to.be('function'); + expect(typeof layoutFunc).toBe('function'); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js similarity index 89% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js index 3942aa18891b84..117b346efda898 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/splits.test.js @@ -18,13 +18,12 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; import $ from 'jquery'; -import { chartSplit } from '../../../../../lib/layout/splits/column_chart/chart_split'; -import { chartTitleSplit } from '../../../../../lib/layout/splits/column_chart/chart_title_split'; -import { xAxisSplit } from '../../../../../lib/layout/splits/column_chart/x_axis_split'; -import { yAxisSplit } from '../../../../../lib/layout/splits/column_chart/y_axis_split'; +import { chartSplit } from './chart_split'; +import { chartTitleSplit } from './chart_title_split'; +import { xAxisSplit } from './x_axis_split'; +import { yAxisSplit } from './y_axis_split'; describe('Vislib Split Function Test Suite', function() { describe('Column Chart', function() { @@ -174,11 +173,11 @@ describe('Vislib Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect($('.chart').length).to.be(2); + expect($('.chart').length).toBe(2); }); it('should add the correct class name', function() { - expect(!!$('.visWrapper__splitCharts--row').length).to.be(true); + expect(!!$('.visWrapper__splitCharts--row').length).toBe(true); }); }); @@ -213,16 +212,16 @@ describe('Vislib Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect($('.chart-title').length).to.be(2); + expect($('.chart-title').length).toBe(2); }); it('should remove the correct div', function() { - expect($('.visAxis__splitTitles--y').length).to.be(1); - expect($('.visAxis__splitTitles--x').length).to.be(0); + expect($('.visAxis__splitTitles--y').length).toBe(1); + expect($('.visAxis__splitTitles--x').length).toBe(0); }); it('should remove all chart title divs when only one chart is rendered', function() { - expect(fixture).to.be(0); + expect(fixture).toBe(0); }); }); @@ -246,7 +245,7 @@ describe('Vislib Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect(divs.length).to.be(2); + expect(divs.length).toBe(2); }); }); @@ -272,7 +271,7 @@ describe('Vislib Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect(divs.length).to.be(2); + expect(divs.length).toBe(2); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js similarity index 93% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js index 8978f80f58dde8..05f6f72246d4a8 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/splits.test.js @@ -18,11 +18,10 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; import $ from 'jquery'; -import { chartSplit } from '../../../../../lib/layout/splits/gauge_chart/chart_split'; -import { chartTitleSplit } from '../../../../../lib/layout/splits/gauge_chart/chart_title_split'; +import { chartSplit } from './chart_split'; +import { chartTitleSplit } from './chart_title_split'; describe('Vislib Gauge Split Function Test Suite', function() { describe('Column Chart', function() { @@ -172,11 +171,11 @@ describe('Vislib Gauge Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect($('.chart').length).to.be(2); + expect($('.chart').length).toBe(2); }); it('should add the correct class name', function() { - expect(!!$('.visWrapper__splitCharts--row').length).to.be(true); + expect(!!$('.visWrapper__splitCharts--row').length).toBe(true); }); }); @@ -196,8 +195,8 @@ describe('Vislib Gauge Split Function Test Suite', function() { }); it('should append the correct number of divs', function() { - expect($('.visAxis__splitTitles--x .chart-title').length).to.be(2); - expect($('.visAxis__splitTitles--y .chart-title').length).to.be(2); + expect($('.visAxis__splitTitles--x .chart-title').length).toBe(2); + expect($('.visAxis__splitTitles--y .chart-title').length).toBe(2); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.test.js similarity index 91% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.test.js index e9c2ff0d2fa078..a27ee57e64a5a9 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.test.js @@ -19,9 +19,8 @@ import d3 from 'd3'; import _ from 'lodash'; -import expect from '@kbn/expect'; -import { layoutTypes } from '../../../../lib/layout/layout_types'; +import { layoutTypes } from '../layout_types'; describe('Vislib Column Layout Test Suite', function() { let columnLayout; @@ -98,13 +97,13 @@ describe('Vislib Column Layout Test Suite', function() { }); it('should return an array of objects', function() { - expect(Array.isArray(columnLayout)).to.be(true); - expect(_.isObject(columnLayout[0])).to.be(true); + expect(Array.isArray(columnLayout)).toBe(true); + expect(_.isObject(columnLayout[0])).toBe(true); }); it('should throw an error when the wrong number or no arguments provided', function() { expect(function() { layoutTypes.point_series(el); - }).to.throwError(); + }).toThrow(); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js b/src/plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js rename to src/plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/gauge.js b/src/plugins/vis_type_vislib/public/vislib/lib/types/gauge.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/gauge.js rename to src/plugins/vis_type_vislib/public/vislib/lib/types/gauge.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/index.js b/src/plugins/vis_type_vislib/public/vislib/lib/types/index.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/index.js rename to src/plugins/vis_type_vislib/public/vislib/lib/types/index.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/pie.js b/src/plugins/vis_type_vislib/public/vislib/lib/types/pie.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/pie.js rename to src/plugins/vis_type_vislib/public/vislib/lib/types/pie.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js b/src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js rename to src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js b/src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js similarity index 53% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js rename to src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js index 38a6be548594f3..684d8f346744e2 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js @@ -16,8 +16,57 @@ * specific language governing permissions and limitations * under the License. */ -import stackedSeries from '../../__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series'; +import stackedSeries from '../../../fixtures/mock_data/date_histogram/_stacked_series'; import { vislibPointSeriesTypes } from './point_series'; +import percentileTestdata from './testdata_linechart_percentile.json'; +import percentileTestdataResult from './testdata_linechart_percentile_result.json'; + +const maxBucketData = { + get: prop => { + return maxBucketData[prop] || maxBucketData.data[prop] || null; + }, + getLabels: () => [], + data: { + hits: 621, + ordered: { + date: true, + interval: 30000, + max: 1408734982458, + min: 1408734082458, + }, + series: [ + { label: 's1', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's2', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's3', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's4', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's5', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's6', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's7', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's8', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's9', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's10', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's11', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's12', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's13', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's14', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's15', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's16', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's17', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's18', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's19', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's20', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's21', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's22', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's23', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's24', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's25', values: [{ x: 1408734060000, y: 8 }] }, + { label: 's26', values: [{ x: 1408734060000, y: 8 }] }, + ], + xAxisLabel: 'Date Histogram', + yAxisLabel: 'series', + yAxisFormatter: () => 'test', + }, +}; describe('vislibPointSeriesTypes', () => { const heatmapConfig = { @@ -41,53 +90,6 @@ describe('vislibPointSeriesTypes', () => { data: stackedSeries, }; - const maxBucketData = { - get: prop => { - return maxBucketData[prop] || maxBucketData.data[prop] || null; - }, - getLabels: () => [], - data: { - hits: 621, - ordered: { - date: true, - interval: 30000, - max: 1408734982458, - min: 1408734082458, - }, - series: [ - { label: 's1', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's2', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's3', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's4', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's5', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's6', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's7', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's8', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's9', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's10', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's11', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's12', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's13', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's14', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's15', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's16', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's17', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's18', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's19', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's20', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's21', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's22', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's23', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's24', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's25', values: [{ x: 1408734060000, y: 8 }] }, - { label: 's26', values: [{ x: 1408734060000, y: 8 }] }, - ], - xAxisLabel: 'Date Histogram', - yAxisLabel: 'series', - yAxisFormatter: () => 'test', - }, - }; - describe('axis formatters', () => { it('should create a value axis config with the default y axis formatter', () => { const parsedConfig = vislibPointSeriesTypes.line({}, maxBucketData); @@ -172,3 +174,59 @@ describe('vislibPointSeriesTypes', () => { }); }); }); + +describe('Point Series Config Type Class Test Suite', function() { + let parsedConfig; + const histogramConfig = { + type: 'histogram', + addLegend: true, + tooltip: { + show: true, + }, + categoryAxes: [ + { + id: 'CategoryAxis-1', + type: 'category', + title: {}, + }, + ], + valueAxes: [ + { + id: 'ValueAxis-1', + type: 'value', + labels: {}, + title: {}, + }, + ], + }; + + describe('histogram chart', function() { + beforeEach(function() { + parsedConfig = vislibPointSeriesTypes.column(histogramConfig, maxBucketData); + }); + it('should not throw an error when more than 25 series are provided', function() { + expect(parsedConfig.error).toBeUndefined(); + }); + + it('should set axis title and formatter from data', () => { + expect(parsedConfig.categoryAxes[0].title.text).toEqual(maxBucketData.data.xAxisLabel); + expect(parsedConfig.valueAxes[0].labels.axisFormatter).toBeDefined(); + }); + }); + + describe('line chart', function() { + beforeEach(function() { + const percentileDataObj = { + get: prop => { + return maxBucketData[prop] || maxBucketData.data[prop] || null; + }, + getLabels: () => [], + data: percentileTestdata.data, + }; + parsedConfig = vislibPointSeriesTypes.line(percentileTestdata.cfg, percentileDataObj); + }); + it('should render a percentile line chart', function() { + expect(JSON.stringify(parsedConfig)).toEqual(JSON.stringify(percentileTestdataResult)); + }); + }); +}); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json b/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json rename to src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json b/src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json rename to src/plugins/vis_type_vislib/public/vislib/lib/types/testdata_linechart_percentile_result.json diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/vis_config.js b/src/plugins/vis_type_vislib/public/vislib/lib/vis_config.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/vis_config.js rename to src/plugins/vis_type_vislib/public/vislib/lib/vis_config.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js b/src/plugins/vis_type_vislib/public/vislib/lib/vis_config.test.js similarity index 86% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js rename to src/plugins/vis_type_vislib/public/vislib/lib/vis_config.test.js index 7dfd2ded36a667..1ba7d4aaa8a0c9 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/vis_config.test.js @@ -18,10 +18,9 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; -import { VisConfig } from '../../lib/vis_config'; -import { getMockUiState } from './fixtures/_vis_fixture'; +import { VisConfig } from './vis_config'; +import { getMockUiState } from '../../fixtures/mocks'; describe('Vislib VisConfig Class Test Suite', function() { let el; @@ -109,33 +108,33 @@ describe('Vislib VisConfig Class Test Suite', function() { describe('get Method', function() { it('should be a function', function() { - expect(typeof visConfig.get).to.be('function'); + expect(typeof visConfig.get).toBe('function'); }); it('should get the property', function() { - expect(visConfig.get('el')).to.be(el); - expect(visConfig.get('type')).to.be('point_series'); + expect(visConfig.get('el')).toBe(el); + expect(visConfig.get('type')).toBe('point_series'); }); it('should return defaults if property does not exist', function() { - expect(visConfig.get('this.does.not.exist', 'defaults')).to.be('defaults'); + expect(visConfig.get('this.does.not.exist', 'defaults')).toBe('defaults'); }); it('should throw an error if property does not exist and defaults were not provided', function() { expect(function() { visConfig.get('this.does.not.exist'); - }).to.throwError(); + }).toThrow(); }); }); describe('set Method', function() { it('should be a function', function() { - expect(typeof visConfig.set).to.be('function'); + expect(typeof visConfig.set).toBe('function'); }); it('should set a property', function() { visConfig.set('this.does.not.exist', 'it.does.now'); - expect(visConfig.get('this.does.not.exist')).to.be('it.does.now'); + expect(visConfig.get('this.does.not.exist')).toBe('it.does.now'); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html b/src/plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html rename to src/plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/response_handler.js b/src/plugins/vis_type_vislib/public/vislib/response_handler.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/response_handler.js rename to src/plugins/vis_type_vislib/public/vislib/response_handler.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/response_handler.test.ts b/src/plugins/vis_type_vislib/public/vislib/response_handler.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/response_handler.test.ts rename to src/plugins/vis_type_vislib/public/vislib/response_handler.test.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/types.ts b/src/plugins/vis_type_vislib/public/vislib/types.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/types.ts rename to src/plugins/vis_type_vislib/public/vislib/types.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/vis.js b/src/plugins/vis_type_vislib/public/vislib/vis.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/vis.js rename to src/plugins/vis_type_vislib/public/vislib/vis.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss rename to src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js index 62de6d84136641..deafe010b6773c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js @@ -20,7 +20,7 @@ import d3 from 'd3'; import _ from 'lodash'; -import { getHeatmapColors } from '../../../../../../../plugins/charts/public'; +import { getHeatmapColors } from '../../../../../charts/public'; const arcAngles = { angleFactor: 0.75, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js similarity index 99% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js index 9822932c6071b8..6f497ae057d726 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js @@ -23,7 +23,7 @@ import moment from 'moment'; import { isColorDark } from '@elastic/eui'; import { PointSeries } from './_point_series'; -import { getHeatmapColors } from '../../.../../../../../../../plugins/charts/public'; +import { getHeatmapColors } from '../../../../../../plugins/charts/public'; const defaults = { color: undefined, // todo diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts b/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts rename to src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.test.js similarity index 83% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.test.js index d69f952325ed02..058bdb5de8785f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/time_marker.test.js @@ -19,11 +19,10 @@ import d3 from 'd3'; import $ from 'jquery'; -import expect from '@kbn/expect'; -import series from '../lib/fixtures/mock_data/date_histogram/_series'; -import terms from '../lib/fixtures/mock_data/terms/_columns'; -import { TimeMarker } from '../../visualizations/time_marker'; +import series from '../../fixtures/mock_data/date_histogram/_series'; +import terms from '../../fixtures/mock_data/terms/_columns'; +import { TimeMarker } from './time_marker'; describe('Vislib Time Marker Test Suite', function() { const height = 50; @@ -87,13 +86,13 @@ describe('Vislib Time Marker Test Suite', function() { it('should return true when data is time based', function() { boolean = defaultMarker._isTimeBasedChart(selection); - expect(boolean).to.be(true); + expect(boolean).toBe(true); }); it('should return false when data is not time based', function() { newSelection = selection.datum(terms); boolean = defaultMarker._isTimeBasedChart(newSelection); - expect(boolean).to.be(false); + expect(boolean).toBe(false); }); }); @@ -107,34 +106,34 @@ describe('Vislib Time Marker Test Suite', function() { }); it('should render the default line', function() { - expect(!!$('line.time-marker').length).to.be(true); + expect(!!$('line.time-marker').length).toBe(true); }); it('should render the custom (user defined) lines', function() { - expect($('line.custom-time-marker').length).to.be(myTimes.length); + expect($('line.custom-time-marker').length).toBe(myTimes.length); }); it('should set the class', function() { Array.prototype.forEach.call(lineArray, function(line) { - expect(line.getAttribute('class')).to.be(customClass); + expect(line.getAttribute('class')).toBe(customClass); }); }); it('should set the stroke', function() { Array.prototype.forEach.call(lineArray, function(line) { - expect(line.getAttribute('stroke')).to.be(color); + expect(line.getAttribute('stroke')).toBe(color); }); }); it('should set the stroke-opacity', function() { Array.prototype.forEach.call(lineArray, function(line) { - expect(+line.getAttribute('stroke-opacity')).to.be(opacity); + expect(+line.getAttribute('stroke-opacity')).toBe(opacity); }); }); it('should set the stroke-width', function() { Array.prototype.forEach.call(lineArray, function(line) { - expect(+line.getAttribute('stroke-width')).to.be(width); + expect(+line.getAttribute('stroke-width')).toBe(width); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js similarity index 100% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js b/src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.test.js similarity index 85% rename from src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js rename to src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.test.js index c8f0faf8dcca5d..df044f46460c87 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js +++ b/src/plugins/vis_type_vislib/public/vislib/visualizations/vis_types.test.js @@ -18,9 +18,8 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; -import { visTypes } from '../../visualizations/vis_types'; +import { visTypes } from './vis_types'; describe('Vislib Vis Types Test Suite', function() { let visFunc; @@ -30,10 +29,10 @@ describe('Vislib Vis Types Test Suite', function() { }); it('should be an object', function() { - expect(_.isObject(visTypes)).to.be(true); + expect(_.isObject(visTypes)).toBe(true); }); it('should return a function', function() { - expect(typeof visFunc).to.be('function'); + expect(typeof visFunc).toBe('function'); }); }); diff --git a/src/legacy/core_plugins/tile_map/public/shim/index.ts b/src/plugins/vis_type_vislib/server/index.ts similarity index 78% rename from src/legacy/core_plugins/tile_map/public/shim/index.ts rename to src/plugins/vis_type_vislib/server/index.ts index cfc7b62ff4f86d..355c01d255ce77 100644 --- a/src/legacy/core_plugins/tile_map/public/shim/index.ts +++ b/src/plugins/vis_type_vislib/server/index.ts @@ -17,4 +17,13 @@ * under the License. */ -export * from './legacy_dependencies_plugin'; +import { schema } from '@kbn/config-schema'; + +export const config = { + schema: schema.object({ enabled: schema.boolean({ defaultValue: true }) }), +}; + +export const plugin = () => ({ + setup() {}, + start() {}, +}); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts index 94473e35a942d5..f6455d0c1e43f4 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -21,7 +21,7 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { cloneDeep, get, omit, has, flow } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; -const migrateIndexPattern: SavedObjectMigrationFn = doc => { +const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (typeof searchSourceJSON !== 'string') { return doc; @@ -64,7 +64,7 @@ const migrateIndexPattern: SavedObjectMigrationFn = doc => { }; // [TSVB] Migrate percentile-rank aggregation (value -> values) -const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { +const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -100,7 +100,7 @@ const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { }; // [TSVB] Remove stale opperator key -const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { +const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -132,7 +132,7 @@ const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { }; // Migrate date histogram aggregation (remove customInterval) -const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { +const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -174,7 +174,7 @@ const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { return doc; }; -const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { +const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { let visState; @@ -206,7 +206,7 @@ const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { // migrate gauge verticalSplit to alignment // https://github.com/elastic/kibana/issues/34636 -const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { +const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -241,7 +241,7 @@ const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logge Path to the series array is thus: attributes.visState. */ -const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { +const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { // Migrate filters // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly const newDoc = cloneDeep(doc); @@ -325,7 +325,7 @@ const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) return newDoc; }; -const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { +const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { // Migrate split_filters in TSVB objects that weren't migrated in 7.3 // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly const newDoc = cloneDeep(doc); @@ -370,7 +370,7 @@ const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => return newDoc; }; -const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { +const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -402,7 +402,7 @@ const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { return doc; }; -const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { +const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -450,7 +450,7 @@ const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { return doc; }; -const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { +const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -483,12 +483,12 @@ const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger return doc; }; -const addDocReferences: SavedObjectMigrationFn = doc => ({ +const addDocReferences: SavedObjectMigrationFn = doc => ({ ...doc, references: doc.references || [], }); -const migrateSavedSearch: SavedObjectMigrationFn = doc => { +const migrateSavedSearch: SavedObjectMigrationFn = doc => { const savedSearchId = get(doc, 'attributes.savedSearchId'); if (savedSearchId && doc.references) { @@ -505,7 +505,7 @@ const migrateSavedSearch: SavedObjectMigrationFn = doc => { return doc; }; -const migrateControls: SavedObjectMigrationFn = doc => { +const migrateControls: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -536,7 +536,7 @@ const migrateControls: SavedObjectMigrationFn = doc => { return doc; }; -const migrateTableSplits: SavedObjectMigrationFn = doc => { +const migrateTableSplits: SavedObjectMigrationFn = doc => { try { const visState = JSON.parse(doc.attributes.visState); if (get(visState, 'type') !== 'table') { @@ -572,7 +572,7 @@ const migrateTableSplits: SavedObjectMigrationFn = doc => { } }; -const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { @@ -606,7 +606,7 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { }; // [TSVB] Default color palette is changing, keep the default for older viz -const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => { +const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -649,27 +649,30 @@ export const visualizationSavedObjectTypeMigrations = { * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 * only contained the 6.7.2 migration and not the 7.0.1 migration. */ - '6.7.2': flow(migrateMatchAllQuery, removeDateHistogramTimeZones), - '7.0.0': flow( + '6.7.2': flow>( + migrateMatchAllQuery, + removeDateHistogramTimeZones + ), + '7.0.0': flow>( addDocReferences, migrateIndexPattern, migrateSavedSearch, migrateControls, migrateTableSplits ), - '7.0.1': flow(removeDateHistogramTimeZones), - '7.2.0': flow( + '7.0.1': flow>(removeDateHistogramTimeZones), + '7.2.0': flow>( migratePercentileRankAggregation, migrateDateHistogramAggregation ), - '7.3.0': flow( + '7.3.0': flow>( migrateGaugeVerticalSplitToAlignment, transformFilterStringToQueryObject, migrateFiltersAggQuery, replaceMovAvgToMovFn ), - '7.3.1': flow(migrateFiltersAggQueryStringQueries), - '7.4.2': flow(transformSplitFiltersStringToQueryObject), - '7.7.0': flow(migrateOperatorKeyTypo), - '7.8.0': flow(migrateTsvbDefaultColorPalettes), + '7.3.1': flow>(migrateFiltersAggQueryStringQueries), + '7.4.2': flow>(transformSplitFiltersStringToQueryObject), + '7.7.0': flow>(migrateOperatorKeyTypo), + '7.8.0': flow>(migrateTsvbDefaultColorPalettes), }; diff --git a/src/plugins/visualize/public/application/application.ts b/src/plugins/visualize/public/application/application.ts index 9d8a1b98ef023d..19551bba9a43ef 100644 --- a/src/plugins/visualize/public/application/application.ts +++ b/src/plugins/visualize/public/application/application.ts @@ -20,6 +20,8 @@ import './index.scss'; import angular, { IModule } from 'angular'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { AppMountContext } from 'kibana/public'; diff --git a/tasks/config/karma.js b/tasks/config/karma.js index 1ec7c831b48641..f87edbf04f220c 100644 --- a/tasks/config/karma.js +++ b/tasks/config/karma.js @@ -25,6 +25,7 @@ import { DllCompiler } from '../../src/optimize/dynamic_dll_plugin'; const TOTAL_CI_SHARDS = 4; const ROOT = dirname(require.resolve('../../package.json')); +const buildHash = String(Number.MAX_SAFE_INTEGER); module.exports = function(grunt) { function pickBrowser() { @@ -57,27 +58,30 @@ module.exports = function(grunt) { 'http://localhost:5610/test_bundle/karma/globals.js', ...UiSharedDeps.jsDepFilenames.map( - chunkFilename => `http://localhost:5610/bundles/kbn-ui-shared-deps/${chunkFilename}` + chunkFilename => + `http://localhost:5610/${buildHash}/bundles/kbn-ui-shared-deps/${chunkFilename}` ), - `http://localhost:5610/bundles/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, + `http://localhost:5610/${buildHash}/bundles/kbn-ui-shared-deps/${UiSharedDeps.jsFilename}`, - 'http://localhost:5610/built_assets/dlls/vendors_runtime.bundle.dll.js', + `http://localhost:5610/${buildHash}/built_assets/dlls/vendors_runtime.bundle.dll.js`, ...DllCompiler.getRawDllConfig().chunks.map( - chunk => `http://localhost:5610/built_assets/dlls/vendors${chunk}.bundle.dll.js` + chunk => + `http://localhost:5610/${buildHash}/built_assets/dlls/vendors${chunk}.bundle.dll.js` ), shardNum === undefined - ? `http://localhost:5610/bundles/tests.bundle.js` - : `http://localhost:5610/bundles/tests.bundle.js?shards=${TOTAL_CI_SHARDS}&shard_num=${shardNum}`, + ? `http://localhost:5610/${buildHash}/bundles/tests.bundle.js` + : `http://localhost:5610/${buildHash}/bundles/tests.bundle.js?shards=${TOTAL_CI_SHARDS}&shard_num=${shardNum}`, - `http://localhost:5610/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, + `http://localhost:5610/${buildHash}/bundles/kbn-ui-shared-deps/${UiSharedDeps.baseCssDistFilename}`, // this causes tilemap tests to fail, probably because the eui styles haven't been // included in the karma harness a long some time, if ever // `http://localhost:5610/bundles/kbn-ui-shared-deps/${UiSharedDeps.lightCssDistFilename}`, ...DllCompiler.getRawDllConfig().chunks.map( - chunk => `http://localhost:5610/built_assets/dlls/vendors${chunk}.style.dll.css` + chunk => + `http://localhost:5610/${buildHash}/built_assets/dlls/vendors${chunk}.style.dll.css` ), - 'http://localhost:5610/bundles/tests.style.css', + `http://localhost:5610/${buildHash}/bundles/tests.style.css`, ]; } @@ -127,9 +131,9 @@ module.exports = function(grunt) { proxies: { '/tests/': 'http://localhost:5610/tests/', - '/bundles/': 'http://localhost:5610/bundles/', - '/built_assets/dlls/': 'http://localhost:5610/built_assets/dlls/', '/test_bundle/': 'http://localhost:5610/test_bundle/', + [`/${buildHash}/bundles/`]: `http://localhost:5610/${buildHash}/bundles/`, + [`/${buildHash}/built_assets/dlls/`]: `http://localhost:5610/${buildHash}/built_assets/dlls/`, }, client: { diff --git a/test/api_integration/apis/index.js b/test/api_integration/apis/index.js index c5bfc847d0041f..0c4028905657d5 100644 --- a/test/api_integration/apis/index.js +++ b/test/api_integration/apis/index.js @@ -33,5 +33,6 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./status')); loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./ui_metric')); + loadTestFile(require.resolve('./telemetry')); }); } diff --git a/src/legacy/core_plugins/tile_map/common/origin.ts b/test/api_integration/apis/telemetry/index.js similarity index 76% rename from src/legacy/core_plugins/tile_map/common/origin.ts rename to test/api_integration/apis/telemetry/index.js index 7fcf1c659bdf3c..c79f5cb4708903 100644 --- a/src/legacy/core_plugins/tile_map/common/origin.ts +++ b/test/api_integration/apis/telemetry/index.js @@ -17,7 +17,10 @@ * under the License. */ -export enum ORIGIN { - EMS = 'elastic_maps_service', - KIBANA_YML = 'self_hosted', +export default function({ loadTestFile }) { + describe('Telemetry', () => { + loadTestFile(require.resolve('./telemetry_local')); + loadTestFile(require.resolve('./opt_in')); + loadTestFile(require.resolve('./telemetry_optin_notice_seen')); + }); } diff --git a/test/api_integration/apis/telemetry/opt_in.ts b/test/api_integration/apis/telemetry/opt_in.ts new file mode 100644 index 00000000000000..e4654ee3985f32 --- /dev/null +++ b/test/api_integration/apis/telemetry/opt_in.ts @@ -0,0 +1,123 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { TelemetrySavedObjectAttributes } from 'src/plugins/telemetry/server/telemetry_repository'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function optInTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + describe('/api/telemetry/v2/optIn API', () => { + let defaultAttributes: TelemetrySavedObjectAttributes; + let kibanaVersion: any; + before(async () => { + const kibanaVersionAccessor = kibanaServer.version; + kibanaVersion = await kibanaVersionAccessor.get(); + defaultAttributes = + (await getSavedObjectAttributes(supertest).catch(err => { + if (err.message === 'expected 200 "OK", got 404 "Not Found"') { + return null; + } + throw err; + })) || {}; + + expect(typeof kibanaVersion).to.eql('string'); + expect(kibanaVersion.length).to.be.greaterThan(0); + }); + + afterEach(async () => { + await updateSavedObjectAttributes(supertest, defaultAttributes); + }); + + it('should support sending false with allowChangingOptInStatus true', async () => { + await updateSavedObjectAttributes(supertest, { + ...defaultAttributes, + allowChangingOptInStatus: true, + }); + await postTelemetryV2Optin(supertest, false, 200); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(supertest); + expect(enabled).to.be(false); + expect(lastVersionChecked).to.be(kibanaVersion); + }); + + it('should support sending true with allowChangingOptInStatus true', async () => { + await updateSavedObjectAttributes(supertest, { + ...defaultAttributes, + allowChangingOptInStatus: true, + }); + await postTelemetryV2Optin(supertest, true, 200); + const { enabled, lastVersionChecked } = await getSavedObjectAttributes(supertest); + expect(enabled).to.be(true); + expect(lastVersionChecked).to.be(kibanaVersion); + }); + + it('should not support sending false with allowChangingOptInStatus false', async () => { + await updateSavedObjectAttributes(supertest, { + ...defaultAttributes, + allowChangingOptInStatus: false, + }); + await postTelemetryV2Optin(supertest, false, 400); + }); + + it('should not support sending true with allowChangingOptInStatus false', async () => { + await updateSavedObjectAttributes(supertest, { + ...defaultAttributes, + allowChangingOptInStatus: false, + }); + await postTelemetryV2Optin(supertest, true, 400); + }); + + it('should not support sending null', async () => { + await postTelemetryV2Optin(supertest, null, 400); + }); + + it('should not support sending junk', async () => { + await postTelemetryV2Optin(supertest, 42, 400); + }); + }); +} + +async function postTelemetryV2Optin(supertest: any, value: any, statusCode: number): Promise { + const { body } = await supertest + .post('/api/telemetry/v2/optIn') + .set('kbn-xsrf', 'xxx') + .send({ enabled: value }) + .expect(statusCode); + + return body; +} + +async function updateSavedObjectAttributes( + supertest: any, + attributes: TelemetrySavedObjectAttributes +): Promise { + return await supertest + .post('/api/saved_objects/telemetry/telemetry') + .query({ overwrite: true }) + .set('kbn-xsrf', 'xxx') + .send({ attributes }) + .expect(200); +} + +async function getSavedObjectAttributes(supertest: any): Promise { + const { body } = await supertest.get('/api/saved_objects/telemetry/telemetry').expect(200); + return body.attributes; +} diff --git a/test/api_integration/apis/telemetry/telemetry_local.js b/test/api_integration/apis/telemetry/telemetry_local.js new file mode 100644 index 00000000000000..84bfd8a755c116 --- /dev/null +++ b/test/api_integration/apis/telemetry/telemetry_local.js @@ -0,0 +1,133 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import _ from 'lodash'; + +/* + * Create a single-level array with strings for all the paths to values in the + * source object, up to 3 deep. Going deeper than 3 causes a bit too much churn + * in the tests. + */ +function flatKeys(source) { + const recursivelyFlatKeys = (obj, path = [], depth = 0) => { + return depth < 3 && _.isObject(obj) + ? _.map(obj, (v, k) => recursivelyFlatKeys(v, [...path, k], depth + 1)) + : path.join('.'); + }; + + return _.uniq(_.flattenDeep(recursivelyFlatKeys(source))).sort((a, b) => a.localeCompare(b)); +} + +export default function({ getService }) { + const supertest = getService('supertest'); + + describe('/api/telemetry/v2/clusters/_stats', () => { + it('should pull local stats and validate data types', async () => { + const timeRange = { + min: '2018-07-23T22:07:00Z', + max: '2018-07-23T22:13:00Z', + }; + + const { body } = await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ timeRange, unencrypted: true }) + .expect(200); + + expect(body.length).to.be(1); + const stats = body[0]; + expect(stats.collection).to.be('local'); + expect(stats.stack_stats.kibana.count).to.be.a('number'); + expect(stats.stack_stats.kibana.indices).to.be.a('number'); + expect(stats.stack_stats.kibana.os.platforms[0].platform).to.be.a('string'); + expect(stats.stack_stats.kibana.os.platforms[0].count).to.be(1); + expect(stats.stack_stats.kibana.os.platformReleases[0].platformRelease).to.be.a('string'); + expect(stats.stack_stats.kibana.os.platformReleases[0].count).to.be(1); + expect(stats.stack_stats.kibana.plugins.telemetry.opt_in_status).to.be(false); + expect(stats.stack_stats.kibana.plugins.telemetry.usage_fetcher).to.be.a('string'); + expect(stats.stack_stats.kibana.plugins.stack_management).to.be.an('object'); + expect(stats.stack_stats.kibana.plugins.ui_metric).to.be.an('object'); + expect(stats.stack_stats.kibana.plugins.application_usage).to.be.an('object'); + expect(stats.stack_stats.kibana.plugins.kql.defaultQueryLanguage).to.be.a('string'); + expect(stats.stack_stats.kibana.plugins['tsvb-validation']).to.be.an('object'); + expect(stats.stack_stats.kibana.plugins.localization).to.be.an('object'); + expect(stats.stack_stats.kibana.plugins.csp.strict).to.be(true); + expect(stats.stack_stats.kibana.plugins.csp.warnLegacyBrowsers).to.be(true); + expect(stats.stack_stats.kibana.plugins.csp.rulesChangedFromDefault).to.be(false); + }); + + it('should pull local stats and validate fields', async () => { + const timeRange = { + min: '2018-07-23T22:07:00Z', + max: '2018-07-23T22:13:00Z', + }; + + const { body } = await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ timeRange, unencrypted: true }) + .expect(200); + + const stats = body[0]; + + const actual = flatKeys(stats); + expect(actual).to.be.an('array'); + const expected = [ + 'cluster_name', + 'cluster_stats.cluster_uuid', + 'cluster_stats.indices.analysis', + 'cluster_stats.indices.completion', + 'cluster_stats.indices.count', + 'cluster_stats.indices.docs', + 'cluster_stats.indices.fielddata', + 'cluster_stats.indices.mappings', + 'cluster_stats.indices.query_cache', + 'cluster_stats.indices.segments', + 'cluster_stats.indices.shards', + 'cluster_stats.indices.store', + 'cluster_stats.nodes.count', + 'cluster_stats.nodes.discovery_types', + 'cluster_stats.nodes.fs', + 'cluster_stats.nodes.ingest', + 'cluster_stats.nodes.jvm', + 'cluster_stats.nodes.network_types', + 'cluster_stats.nodes.os', + 'cluster_stats.nodes.packaging_types', + 'cluster_stats.nodes.plugins', + 'cluster_stats.nodes.process', + 'cluster_stats.nodes.versions', + 'cluster_stats.status', + 'cluster_stats.timestamp', + 'cluster_uuid', + 'collection', + 'collectionSource', + 'stack_stats.kibana.count', + 'stack_stats.kibana.indices', + 'stack_stats.kibana.os', + 'stack_stats.kibana.plugins', + 'stack_stats.kibana.versions', + 'timestamp', + 'version', + ]; + + expect(expected.every(m => actual.includes(m))).to.be.ok(); + }); + }); +} diff --git a/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts b/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts new file mode 100644 index 00000000000000..4413c672fb46c3 --- /dev/null +++ b/test/api_integration/apis/telemetry/telemetry_optin_notice_seen.ts @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { Client, DeleteDocumentParams, GetParams, GetResponse } from 'elasticsearch'; +import { TelemetrySavedObjectAttributes } from 'src/plugins/telemetry/server/telemetry_repository'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function optInTest({ getService }: FtrProviderContext) { + const client: Client = getService('legacyEs'); + const supertest = getService('supertest'); + + describe('/api/telemetry/v2/userHasSeenNotice API Telemetry User has seen OptIn Notice', () => { + it('should update telemetry setting field via PUT', async () => { + try { + await client.delete({ + index: '.kibana', + id: 'telemetry:telemetry', + } as DeleteDocumentParams); + } catch (err) { + if (err.statusCode !== 404) { + throw err; + } + } + + await supertest + .put('/api/telemetry/v2/userHasSeenNotice') + .set('kbn-xsrf', 'xxx') + .expect(200); + + const { + _source: { telemetry }, + }: GetResponse<{ + telemetry: TelemetrySavedObjectAttributes; + }> = await client.get({ + index: '.kibana', + id: 'telemetry:telemetry', + } as GetParams); + + expect(telemetry.userHasSeenNotice).to.be(true); + }); + }); +} diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 862e5127bb6704..93debdcc37f0ab 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -44,6 +44,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo ensureCurrentUrl: boolean; shouldLoginIfPrompted: boolean; useActualUrl: boolean; + insertTimestamp: boolean; } class CommonPage { @@ -65,7 +66,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo * Logins to Kibana as default user and navigates to provided app * @param appUrl Kibana URL */ - private async loginIfPrompted(appUrl: string) { + private async loginIfPrompted(appUrl: string, insertTimestamp: boolean) { let currentUrl = await browser.getCurrentUrl(); log.debug(`currentUrl = ${currentUrl}\n appUrl = ${appUrl}`); await testSubjects.find('kibanaChrome', 6 * defaultFindTimeout); // 60 sec waiting @@ -87,7 +88,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo '[data-test-subj="kibanaChrome"] nav:not(.ng-hide)', 6 * defaultFindTimeout ); - await browser.get(appUrl); + await browser.get(appUrl, insertTimestamp); currentUrl = await browser.getCurrentUrl(); log.debug(`Finished login process currentUrl = ${currentUrl}`); } @@ -95,7 +96,13 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo } private async navigate(navigateProps: NavigateProps) { - const { appConfig, ensureCurrentUrl, shouldLoginIfPrompted, useActualUrl } = navigateProps; + const { + appConfig, + ensureCurrentUrl, + shouldLoginIfPrompted, + useActualUrl, + insertTimestamp, + } = navigateProps; const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig); await retry.try(async () => { @@ -111,7 +118,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo } const currentUrl = shouldLoginIfPrompted - ? await this.loginIfPrompted(appUrl) + ? await this.loginIfPrompted(appUrl, insertTimestamp) : await browser.getCurrentUrl(); if (ensureCurrentUrl && !currentUrl.includes(appUrl)) { @@ -134,6 +141,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo ensureCurrentUrl = true, shouldLoginIfPrompted = true, useActualUrl = false, + insertTimestamp = true, } = {} ) { const appConfig = { @@ -146,6 +154,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo ensureCurrentUrl, shouldLoginIfPrompted, useActualUrl, + insertTimestamp, }); } @@ -165,6 +174,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo ensureCurrentUrl = true, shouldLoginIfPrompted = true, useActualUrl = true, + insertTimestamp = true, } = {} ) { const appConfig = { @@ -178,6 +188,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo ensureCurrentUrl, shouldLoginIfPrompted, useActualUrl, + insertTimestamp, }); } @@ -208,7 +219,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async navigateToApp( appName: string, - { basePath = '', shouldLoginIfPrompted = true, hash = '' } = {} + { basePath = '', shouldLoginIfPrompted = true, hash = '', insertTimestamp = true } = {} ) { let appUrl: string; if (config.has(['apps', appName])) { @@ -239,7 +250,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo log.debug('returned from get, calling refresh'); await browser.refresh(); let currentUrl = shouldLoginIfPrompted - ? await this.loginIfPrompted(appUrl) + ? await this.loginIfPrompted(appUrl, insertTimestamp) : await browser.getCurrentUrl(); if (currentUrl.includes('app/kibana')) { diff --git a/test/interpreter_functional/test_suites/run_pipeline/esaggs.ts b/test/interpreter_functional/test_suites/run_pipeline/esaggs.ts new file mode 100644 index 00000000000000..5ea151dffdc8ef --- /dev/null +++ b/test/interpreter_functional/test_suites/run_pipeline/esaggs.ts @@ -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 expect from '@kbn/expect'; +import { ExpectExpression, expectExpressionProvider } from './helpers'; +import { FtrProviderContext } from '../../../functional/ftr_provider_context'; + +function getCell(esaggsResult: any, column: number, row: number): unknown | undefined { + const columnId = esaggsResult?.columns[column]?.id; + if (!columnId) { + return; + } + return esaggsResult?.rows[row]?.[columnId]; +} + +export default function({ + getService, + updateBaselines, +}: FtrProviderContext & { updateBaselines: boolean }) { + let expectExpression: ExpectExpression; + describe('esaggs pipeline expression tests', () => { + before(() => { + expectExpression = expectExpressionProvider({ getService, updateBaselines }); + }); + + describe('correctly renders tagcloud', () => { + it('filters on index pattern primary date field by default', async () => { + const aggConfigs = [{ id: 1, enabled: true, type: 'count', schema: 'metric', params: {} }]; + const timeRange = { + from: '2006-09-21T00:00:00Z', + to: '2015-09-22T00:00:00Z', + }; + const expression = ` + kibana_context timeRange='${JSON.stringify(timeRange)}' + | esaggs index='logstash-*' aggConfigs='${JSON.stringify(aggConfigs)}' + `; + const result = await expectExpression('esaggs_primary_timefield', expression).getResponse(); + expect(getCell(result, 0, 0)).to.be(9375); + }); + + it('filters on the specified date field', async () => { + const aggConfigs = [{ id: 1, enabled: true, type: 'count', schema: 'metric', params: {} }]; + const timeRange = { + from: '2006-09-21T00:00:00Z', + to: '2015-09-22T00:00:00Z', + }; + const expression = ` + kibana_context timeRange='${JSON.stringify(timeRange)}' + | esaggs index='logstash-*' timeFields='relatedContent.article:published_time' aggConfigs='${JSON.stringify( + aggConfigs + )}' + `; + const result = await expectExpression('esaggs_other_timefield', expression).getResponse(); + expect(getCell(result, 0, 0)).to.be(11134); + }); + + it('filters on multiple specified date field', async () => { + const aggConfigs = [{ id: 1, enabled: true, type: 'count', schema: 'metric', params: {} }]; + const timeRange = { + from: '2006-09-21T00:00:00Z', + to: '2015-09-22T00:00:00Z', + }; + const expression = ` + kibana_context timeRange='${JSON.stringify(timeRange)}' + | esaggs index='logstash-*' timeFields='relatedContent.article:published_time' timeFields='@timestamp' aggConfigs='${JSON.stringify( + aggConfigs + )}' + `; + const result = await expectExpression( + 'esaggs_multiple_timefields', + expression + ).getResponse(); + expect(getCell(result, 0, 0)).to.be(7452); + }); + }); + }); +} diff --git a/test/interpreter_functional/test_suites/run_pipeline/index.ts b/test/interpreter_functional/test_suites/run_pipeline/index.ts index 031a0e3576ccc0..9590f9f8c17940 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/index.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/index.ts @@ -46,5 +46,6 @@ export default function({ getService, getPageObjects, loadTestFile }: FtrProvide loadTestFile(require.resolve('./basic')); loadTestFile(require.resolve('./tag_cloud')); loadTestFile(require.resolve('./metric')); + loadTestFile(require.resolve('./esaggs')); }); } diff --git a/test/plugin_functional/test_suites/core_plugins/application_status.ts b/test/plugin_functional/test_suites/core_plugins/application_status.ts index b6d13a5604011f..c384e41851e15f 100644 --- a/test/plugin_functional/test_suites/core_plugins/application_status.ts +++ b/test/plugin_functional/test_suites/core_plugins/application_status.ts @@ -17,6 +17,7 @@ * under the License. */ +import url from 'url'; import expect from '@kbn/expect'; import { AppNavLinkStatus, @@ -26,6 +27,15 @@ import { import { PluginFunctionalProviderContext } from '../../services'; import '../../plugins/core_app_status/public/types'; +const getKibanaUrl = (pathname?: string, search?: string) => + url.format({ + protocol: 'http:', + hostname: process.env.TEST_KIBANA_HOST || 'localhost', + port: process.env.TEST_KIBANA_PORT || '5620', + pathname, + search, + }); + // eslint-disable-next-line import/no-default-export export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) { const PageObjects = getPageObjects(['common']); @@ -97,6 +107,22 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider expect(await testSubjects.exists('appStatusApp')).to.eql(true); }); + it('allows to change the defaultPath of an application', async () => { + let link = await appsMenu.getLink('App Status'); + expect(link!.href).to.eql(getKibanaUrl('/app/app_status')); + + await setAppStatus({ + defaultPath: '/arbitrary/path', + }); + + link = await appsMenu.getLink('App Status'); + expect(link!.href).to.eql(getKibanaUrl('/app/app_status/arbitrary/path')); + + await navigateToApp('app_status'); + expect(await testSubjects.exists('appStatusApp')).to.eql(true); + expect(await browser.getCurrentUrl()).to.eql(getKibanaUrl('/app/app_status/arbitrary/path')); + }); + it('can change the state of the currently mounted app', async () => { await setAppStatus({ status: AppStatus.accessible, diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh index 67d88b308ed91c..951ba8e22d8858 100755 --- a/test/scripts/jenkins_xpack.sh +++ b/test/scripts/jenkins_xpack.sh @@ -39,7 +39,7 @@ else # build runtime for canvas echo "NODE_ENV=$NODE_ENV" node ./legacy/plugins/canvas/scripts/shareable_runtime - node --max-old-space-size=6144 scripts/jest --ci --verbose --coverage + node --max-old-space-size=6144 scripts/jest --ci --verbose --detectOpenHandles --coverage # rename file in order to be unique one test -f ../target/kibana-coverage/jest/coverage-final.json \ && mv ../target/kibana-coverage/jest/coverage-final.json \ diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index ae1a55254c702a..4acb170d125744 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -43,7 +43,7 @@ "xpack.transform": "plugins/transform", "xpack.triggersActionsUI": "plugins/triggers_actions_ui", "xpack.upgradeAssistant": "plugins/upgrade_assistant", - "xpack.uptime": ["plugins/uptime", "legacy/plugins/uptime"], + "xpack.uptime": ["plugins/uptime"], "xpack.watcher": "plugins/watcher" }, "translations": [ diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index af5ace8e3cd3b7..4f1251321b0054 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -34,6 +34,20 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) { '^test_utils/stub_web_worker': `${xPackKibanaDirectory}/test_utils/stub_web_worker.ts`, '^(!!)?file-loader!': fileMockPath, }, + collectCoverageFrom: [ + 'legacy/plugins/**/*.{js,jsx,ts,tsx}', + 'legacy/server/**/*.{js,jsx,ts,tsx}', + 'plugins/**/*.{js,jsx,ts,tsx}', + '!**/{__test__,__snapshots__,__examples__,integration_tests,tests}/**', + '!**/*.test.{js,ts,tsx}', + '!**/flot-charts/**', + '!**/test/**', + '!**/build/**', + '!**/scripts/**', + '!**/mocks/**', + '!**/plugins/apm/e2e/**', + ], + coveragePathIgnorePatterns: ['.*\\.d\\.ts'], coverageDirectory: '/../target/kibana-coverage/jest', coverageReporters: !!process.env.CODE_COVERAGE ? ['json'] : ['html'], setupFiles: [ diff --git a/x-pack/index.js b/x-pack/index.js index 3761af5c1ca7a8..cfadddac3994ac 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -12,19 +12,12 @@ import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { beats } from './legacy/plugins/beats_management'; import { apm } from './legacy/plugins/apm'; import { maps } from './legacy/plugins/maps'; -import { indexManagement } from './legacy/plugins/index_management'; import { spaces } from './legacy/plugins/spaces'; import { canvas } from './legacy/plugins/canvas'; import { infra } from './legacy/plugins/infra'; import { taskManager } from './legacy/plugins/task_manager'; -import { remoteClusters } from './legacy/plugins/remote_clusters'; -import { upgradeAssistant } from './legacy/plugins/upgrade_assistant'; -import { uptime } from './legacy/plugins/uptime'; import { encryptedSavedObjects } from './legacy/plugins/encrypted_saved_objects'; -import { actions } from './legacy/plugins/actions'; -import { alerting } from './legacy/plugins/alerting'; import { ingestManager } from './legacy/plugins/ingest_manager'; -import { triggersActionsUI } from './legacy/plugins/triggers_actions_ui'; module.exports = function(kibana) { return [ @@ -38,16 +31,9 @@ module.exports = function(kibana) { apm(kibana), maps(kibana), canvas(kibana), - indexManagement(kibana), infra(kibana), taskManager(kibana), - remoteClusters(kibana), - upgradeAssistant(kibana), - uptime(kibana), encryptedSavedObjects(kibana), - actions(kibana), - alerting(kibana), ingestManager(kibana), - triggersActionsUI(kibana), ]; }; diff --git a/x-pack/legacy/plugins/actions/server/index.ts b/x-pack/legacy/plugins/actions/server/index.ts deleted file mode 100644 index 63dd6f99f9c24b..00000000000000 --- a/x-pack/legacy/plugins/actions/server/index.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. - */ -import { Root } from 'joi'; -import { Legacy } from 'kibana'; -import mappings from './mappings.json'; -import { - LegacyPluginApi, - LegacyPluginSpec, - ArrayOrItem, -} from '../../../../../src/legacy/plugin_discovery/types'; - -export function actions(kibana: LegacyPluginApi): ArrayOrItem { - return new kibana.Plugin({ - id: 'actions', - configPrefix: 'xpack.actions', - config(Joi: Root) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }) - .unknown(true) - .default(); - }, - require: ['kibana', 'elasticsearch'], - isEnabled(config: Legacy.KibanaConfig) { - return ( - config.get('xpack.encryptedSavedObjects.enabled') === true && - config.get('xpack.actions.enabled') === true && - config.get('xpack.task_manager.enabled') === true - ); - }, - uiExports: { - mappings, - }, - } as Legacy.PluginSpecOptions); -} diff --git a/x-pack/legacy/plugins/alerting/server/index.ts b/x-pack/legacy/plugins/alerting/server/index.ts deleted file mode 100644 index 065af7dedebd92..00000000000000 --- a/x-pack/legacy/plugins/alerting/server/index.ts +++ /dev/null @@ -1,40 +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 { Legacy } from 'kibana'; -import { Root } from 'joi'; -import mappings from './mappings.json'; -import { - LegacyPluginApi, - LegacyPluginSpec, - ArrayOrItem, -} from '../../../../../src/legacy/plugin_discovery/types'; - -export function alerting(kibana: LegacyPluginApi): ArrayOrItem { - return new kibana.Plugin({ - id: 'alerting', - configPrefix: 'xpack.alerting', - require: ['kibana', 'elasticsearch', 'actions', 'task_manager', 'encryptedSavedObjects'], - isEnabled(config: Legacy.KibanaConfig) { - return ( - config.get('xpack.alerting.enabled') === true && - config.get('xpack.actions.enabled') === true && - config.get('xpack.encryptedSavedObjects.enabled') === true && - config.get('xpack.task_manager.enabled') === true - ); - }, - config(Joi: Root) { - return Joi.object() - .keys({ - enabled: Joi.boolean().default(true), - }) - .default(); - }, - uiExports: { - mappings, - }, - } as Legacy.PluginSpecOptions); -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx index 27095264461ee3..f57ddb5cf69a23 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx @@ -71,10 +71,10 @@ function getSpanTypes(span: Span) { }; } -const SpanBadge = styled(EuiBadge)` +const SpanBadge = (styled(EuiBadge)` display: inline-block; margin-right: ${px(units.quarter)}; -` as any; +` as unknown) as typeof EuiBadge; const HttpInfoContainer = styled('div')` margin-right: ${px(units.quarter)}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx index a5d8902ff16261..f01b2aa335a3aa 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx @@ -10,10 +10,10 @@ import React from 'react'; import styled from 'styled-components'; import { px, units } from '../../../../../../style/variables'; -const SpanBadge = styled(EuiBadge)` +const SpanBadge = (styled(EuiBadge)` display: inline-block; margin-right: ${px(units.quarter)}; -` as any; +` as unknown) as typeof EuiBadge; interface SyncBadgeProps { /** diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx index 858e6d29bfa5e3..2be3c82a8385b0 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx @@ -17,7 +17,7 @@ interface Props { const Badge = (styled(EuiBadge)` margin-top: ${px(units.eighth)}; -` as any) as any; +` as unknown) as typeof EuiBadge; export const ErrorCountSummaryItemBadge = ({ count }: Props) => ( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx index 1e1d49b2cf417c..d499dddeeb8b3f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx @@ -13,7 +13,7 @@ import { HttpStatusBadge } from '../HttpStatusBadge'; const HttpInfoBadge = (styled(EuiBadge)` margin-right: ${px(units.quarter)}; -` as any) as any; +` as unknown) as typeof EuiBadge; const Url = styled('span')` display: inline-block; diff --git a/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js b/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js index e837f5200a1590..2886aa868eb9e1 100644 --- a/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js +++ b/x-pack/legacy/plugins/canvas/public/components/app/track_route_change.js @@ -7,13 +7,17 @@ import { get } from 'lodash'; import { getWindow } from '../../lib/get_window'; import { CANVAS_APP } from '../../../common/lib/constants'; -import { getCoreStart, getStartPlugins } from '../../legacy'; +import { platformService } from '../../services'; export function trackRouteChange() { - const basePath = getCoreStart().http.basePath.get(); - // storage.set(LOCALSTORAGE_LASTPAGE, pathname); - getStartPlugins().__LEGACY.trackSubUrlForApp( - CANVAS_APP, - getStartPlugins().__LEGACY.absoluteToParsedUrl(get(getWindow(), 'location.href'), basePath) - ); + const basePath = platformService.getService().coreStart.http.basePath.get(); + + platformService + .getService() + .startPlugins.__LEGACY.trackSubUrlForApp( + CANVAS_APP, + platformService + .getService() + .startPlugins.__LEGACY.absoluteToParsedUrl(get(getWindow(), 'location.href'), basePath) + ); } diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index 5bb628909c32e2..f83887bbcbdfd3 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -35,8 +35,6 @@ const shimStartPlugins: CanvasStartDeps = { __LEGACY: { // ToDo: Copy directly into canvas absoluteToParsedUrl, - // ToDo: Copy directly into canvas - formatMsg, // ToDo: Won't be a part of New Platform. Will need to handle internally trackSubUrlForApp: chrome.trackSubUrlForApp, }, diff --git a/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts b/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts index 834d5868c35ea7..57b513affd7815 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/breadcrumbs.ts @@ -5,7 +5,7 @@ */ import { ChromeBreadcrumb } from '../../../../../../src/core/public'; -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; export const getBaseBreadcrumb = () => ({ text: 'Canvas', @@ -24,6 +24,6 @@ export const getWorkpadBreadcrumb = ({ }; export const setBreadcrumb = (paths: ChromeBreadcrumb | ChromeBreadcrumb[]) => { - const setBreadCrumbs = getCoreStart().chrome.setBreadcrumbs; + const setBreadCrumbs = platformService.getService().coreStart.chrome.setBreadcrumbs; setBreadCrumbs(Array.isArray(paths) ? paths : [paths]); }; diff --git a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts index 4118bb81b83639..8952802dc2f2be 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/custom_element_service.ts @@ -8,10 +8,10 @@ import { AxiosPromise } from 'axios'; import { API_ROUTE_CUSTOM_ELEMENT } from '../../common/lib/constants'; import { fetch } from '../../common/lib/fetch'; import { CustomElement } from '../../types'; -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; const getApiPath = function() { - const basePath = getCoreStart().http.basePath.get(); + const basePath = platformService.getService().coreStart.http.basePath.get(); return `${basePath}${API_ROUTE_CUSTOM_ELEMENT}`; }; diff --git a/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts b/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts index 40e09ee39d7144..6430f7d87d4f76 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/documentation_links.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; export const getDocumentationLinks = () => ({ - canvas: `${getCoreStart().docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${ - getCoreStart().docLinks.DOC_LINK_VERSION + canvas: `${platformService.getService().coreStart.docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${ + platformService.getService().coreStart.docLinks.DOC_LINK_VERSION }/canvas.html`, - numeral: `${getCoreStart().docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${ - getCoreStart().docLinks.DOC_LINK_VERSION + numeral: `${platformService.getService().coreStart.docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${ + platformService.getService().coreStart.docLinks.DOC_LINK_VERSION }/guide/numeral.html`, }); diff --git a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts index 6aa4968f29155d..184f4f3c8af7c3 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/es_service.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/es_service.ts @@ -11,21 +11,21 @@ import { API_ROUTE } from '../../common/lib/constants'; import { fetch } from '../../common/lib/fetch'; import { ErrorStrings } from '../../i18n'; import { notifyService } from '../services'; -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; const { esService: strings } = ErrorStrings; const getApiPath = function() { - const basePath = getCoreStart().http.basePath.get(); + const basePath = platformService.getService().coreStart.http.basePath.get(); return basePath + API_ROUTE; }; const getSavedObjectsClient = function() { - return getCoreStart().savedObjects.client; + return platformService.getService().coreStart.savedObjects.client; }; const getAdvancedSettings = function() { - return getCoreStart().uiSettings; + return platformService.getService().coreStart.uiSettings; }; export const getFields = (index = '_all') => { diff --git a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js index f3681f50c56a59..e6628399f53c22 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js +++ b/x-pack/legacy/plugins/canvas/public/lib/workpad_service.js @@ -11,7 +11,7 @@ import { DEFAULT_WORKPAD_CSS, } from '../../common/lib/constants'; import { fetch } from '../../common/lib/fetch'; -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; /* Remove any top level keys from the workpad which will be rejected by validation */ @@ -43,17 +43,17 @@ const sanitizeWorkpad = function(workpad) { }; const getApiPath = function() { - const basePath = getCoreStart().http.basePath.get(); + const basePath = platformService.getService().coreStart.http.basePath.get(); return `${basePath}${API_ROUTE_WORKPAD}`; }; const getApiPathStructures = function() { - const basePath = getCoreStart().http.basePath.get(); + const basePath = platformService.getService().coreStart.http.basePath.get(); return `${basePath}${API_ROUTE_WORKPAD_STRUCTURES}`; }; const getApiPathAssets = function() { - const basePath = getCoreStart().http.basePath.get(); + const basePath = platformService.getService().coreStart.http.basePath.get(); return `${basePath}${API_ROUTE_WORKPAD_ASSETS}`; }; diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index 36ce1974be2724..baeb4ebd453d2c 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -44,7 +44,6 @@ export interface CanvasStartDeps { uiActions: UiActionsStart; __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; - formatMsg: any; trackSubUrlForApp: Chrome['trackSubUrlForApp']; }; } @@ -64,6 +63,7 @@ export class CanvasPlugin implements Plugin { // TODO: Do we want to completely move canvas_plugin_src into it's own plugin? private srcPlugin = new CanvasSrcPlugin(); + private startPlugins: CanvasStartDeps | undefined; public setup(core: CoreSetup, plugins: CanvasSetupDeps) { const { api: canvasApi, registries } = getPluginApi(plugins.expressions); @@ -73,14 +73,26 @@ export class CanvasPlugin core.application.register({ id: 'canvas', title: 'Canvas App', - async mount(context, params) { + mount: async (context, params) => { // Load application bundle const { renderApp, initializeCanvas, teardownCanvas } = await import('./application'); // Get start services const [coreStart, depsStart] = await core.getStartServices(); - const canvasStore = await initializeCanvas(core, coreStart, plugins, depsStart, registries); + // TODO: We only need this to get the __LEGACY stuff that isn't coming from getStartSevices. + // We won't need this as soon as we move over to NP Completely + if (!this.startPlugins) { + throw new Error('Start Plugins not ready at mount time'); + } + + const canvasStore = await initializeCanvas( + core, + coreStart, + plugins, + this.startPlugins, + registries + ); const unmount = renderApp(coreStart, depsStart, params, canvasStore); @@ -115,6 +127,7 @@ export class CanvasPlugin } public start(core: CoreStart, plugins: CanvasStartDeps) { + this.startPlugins = plugins; this.srcPlugin.start(core, plugins); initLoadingIndicator(core.http.addLoadingCountSource); } diff --git a/x-pack/legacy/plugins/canvas/public/services/index.ts b/x-pack/legacy/plugins/canvas/public/services/index.ts index 12c0a687bf308b..17d836f1441c96 100644 --- a/x-pack/legacy/plugins/canvas/public/services/index.ts +++ b/x-pack/legacy/plugins/canvas/public/services/index.ts @@ -7,6 +7,7 @@ import { CoreSetup, CoreStart } from '../../../../../../src/core/public'; import { CanvasSetupDeps, CanvasStartDeps } from '../plugin'; import { notifyServiceFactory } from './notify'; +import { platformServiceFactory } from './platform'; export type CanvasServiceFactory = ( coreSetup: CoreSetup, @@ -49,6 +50,7 @@ export type ServiceFromProvider

= P extends CanvasServiceProvider ? export const services = { notify: new CanvasServiceProvider(notifyServiceFactory), + platform: new CanvasServiceProvider(platformServiceFactory), }; export interface CanvasServices { @@ -70,4 +72,4 @@ export const stopServices = () => { Object.entries(services).forEach(([key, provider]) => provider.stop()); }; -export const { notify: notifyService } = services; +export const { notify: notifyService, platform: platformService } = services; diff --git a/x-pack/legacy/plugins/canvas/public/services/platform.ts b/x-pack/legacy/plugins/canvas/public/services/platform.ts new file mode 100644 index 00000000000000..440e9523044c1c --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/services/platform.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 { CanvasServiceFactory } from '.'; +import { CoreStart, CoreSetup, CanvasSetupDeps, CanvasStartDeps } from '../plugin'; + +interface PlatformService { + coreSetup: CoreSetup; + coreStart: CoreStart; + setupPlugins: CanvasSetupDeps; + startPlugins: CanvasStartDeps; +} + +export const platformServiceFactory: CanvasServiceFactory = ( + coreSetup, + coreStart, + setupPlugins, + startPlugins +) => { + return { coreSetup, coreStart, setupPlugins, startPlugins }; +}; diff --git a/x-pack/legacy/plugins/canvas/public/state/initial_state.js b/x-pack/legacy/plugins/canvas/public/state/initial_state.js index 40c017543147fc..bfa68b33908e01 100644 --- a/x-pack/legacy/plugins/canvas/public/state/initial_state.js +++ b/x-pack/legacy/plugins/canvas/public/state/initial_state.js @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { getCoreStart } from '../legacy'; +import { platformService } from '../services'; import { getDefaultWorkpad } from './defaults'; export const getInitialState = path => { @@ -13,7 +13,7 @@ export const getInitialState = path => { app: {}, // Kibana stuff in here assets: {}, // assets end up here transient: { - canUserWrite: getCoreStart().application.capabilities.canvas.save, + canUserWrite: platformService.getService().coreStart.application.capabilities.canvas.save, zoomScale: 1, elementStats: { total: 0, diff --git a/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js b/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js index 12733680ed32d4..30f9c638a054fe 100644 --- a/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js +++ b/x-pack/legacy/plugins/canvas/public/state/reducers/workpad.js @@ -5,7 +5,7 @@ */ import { handleActions } from 'redux-actions'; -import { getCoreStart } from '../../legacy'; +import { platformService } from '../../services'; import { getDefaultWorkpad } from '../defaults'; import { setWorkpad, @@ -22,11 +22,13 @@ import { APP_ROUTE_WORKPAD } from '../../../common/lib/constants'; export const workpadReducer = handleActions( { [setWorkpad]: (workpadState, { payload }) => { - getCoreStart().chrome.recentlyAccessed.add( - `${APP_ROUTE_WORKPAD}/${payload.id}`, - payload.name, - payload.id - ); + platformService + .getService() + .coreStart.chrome.recentlyAccessed.add( + `${APP_ROUTE_WORKPAD}/${payload.id}`, + payload.name, + payload.id + ); return payload; }, @@ -39,11 +41,13 @@ export const workpadReducer = handleActions( }, [setName]: (workpadState, { payload }) => { - getCoreStart().chrome.recentlyAccessed.add( - `${APP_ROUTE_WORKPAD}/${workpadState.id}`, - payload, - workpadState.id - ); + platformService + .getService() + .coreStart.chrome.recentlyAccessed.add( + `${APP_ROUTE_WORKPAD}/${workpadState.id}`, + payload, + workpadState.id + ); return { ...workpadState, name: payload }; }, diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index d1e8892fa2c984..a1186e04ee27a6 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -54,7 +54,7 @@ export function maps(kibana) { emsLandingPageUrl: mapConfig.emsLandingPageUrl, kbnPkgVersion: serverConfig.get('pkg.version'), regionmapLayers: _.get(mapConfig, 'regionmap.layers', []), - tilemap: _.get(mapConfig, 'tilemap', []), + tilemap: _.get(mapConfig, 'tilemap', {}), }; }, styleSheetPaths: `${__dirname}/public/index.scss`, diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 1b1fbf111fe04b..bb1a6b74d43a70 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -336,7 +336,7 @@ app.controller( function addFilters(newFilters) { newFilters.forEach(filter => { - filter.$state = esFilters.FilterStateStore.APP_STATE; + filter.$state = { store: esFilters.FilterStateStore.APP_STATE }; }); $scope.updateFiltersAndDispatch([...$scope.filters, ...newFilters]); } diff --git a/x-pack/legacy/plugins/monitoring/common/constants.ts b/x-pack/legacy/plugins/monitoring/common/constants.ts index 3a4c7b71dcd03d..36030e1fa7f2a5 100644 --- a/x-pack/legacy/plugins/monitoring/common/constants.ts +++ b/x-pack/legacy/plugins/monitoring/common/constants.ts @@ -251,7 +251,7 @@ export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION, ALERT_TYPE_CLUSTER_ST /** * Matches the id for the built-in in email action type - * See x-pack/legacy/plugins/actions/server/builtin_action_types/email.ts + * See x-pack/plugins/actions/server/builtin_action_types/email.ts */ export const ALERT_ACTION_TYPE_EMAIL = '.email'; diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts index a047c25c2b1d74..c6031cb2203341 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts +++ b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts @@ -5,6 +5,8 @@ */ import angular, { IWindowService } from 'angular'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import { AppMountContext } from 'kibana/public'; diff --git a/x-pack/legacy/plugins/remote_clusters/index.ts b/x-pack/legacy/plugins/remote_clusters/index.ts deleted file mode 100644 index 439cb818d8a562..00000000000000 --- a/x-pack/legacy/plugins/remote_clusters/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { PLUGIN } from './common'; - -export function remoteClusters(kibana: any) { - return new kibana.Plugin({ - id: PLUGIN.ID, - configPrefix: 'xpack.remote_clusters', - publicDir: resolve(__dirname, 'public'), - require: ['kibana'], - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - }, - // TODO: Remove once CCR has migrated to NP - config(Joi: any) { - return Joi.object({ - // display menu item - ui: Joi.object({ - enabled: Joi.boolean().default(true), - }).default(), - - // enable plugin - enabled: Joi.boolean().default(true), - }).default(); - }, - isEnabled(config: Legacy.KibanaConfig) { - return ( - config.get('xpack.remote_clusters.enabled') && config.get('xpack.index_management.enabled') - ); - }, - init() {}, - }); -} diff --git a/x-pack/legacy/plugins/security/index.ts b/x-pack/legacy/plugins/security/index.ts index 5b2218af1fd521..b1dec2ce82c520 100644 --- a/x-pack/legacy/plugins/security/index.ts +++ b/x-pack/legacy/plugins/security/index.ts @@ -78,9 +78,7 @@ export const security = (kibana: Record) => // features are up to date. xpackInfo .feature(this.id) - .registerLicenseCheckResultsGenerator(() => - securityPlugin.__legacyCompat.license.getFeatures() - ); + .registerLicenseCheckResultsGenerator(() => securityPlugin.license.getFeatures()); server.expose({ getUser: async (request: LegacyRequest) => diff --git a/x-pack/legacy/plugins/task_manager/server/index.ts b/x-pack/legacy/plugins/task_manager/server/index.ts index 3ea687f7003f40..a3167920efa06c 100644 --- a/x-pack/legacy/plugins/task_manager/server/index.ts +++ b/x-pack/legacy/plugins/task_manager/server/index.ts @@ -6,8 +6,6 @@ import { Root } from 'joi'; import { Legacy } from 'kibana'; -import mappings from './mappings.json'; -import { migrations } from './migrations'; import { createLegacyApi, getTaskManagerSetup } from './legacy'; export { LegacyTaskManagerApi, getTaskManagerSetup, getTaskManagerStart } from './legacy'; @@ -21,19 +19,6 @@ import { ArrayOrItem, } from '../../../../../src/legacy/plugin_discovery/types'; -const savedObjectSchemas = { - task: { - hidden: true, - isNamespaceAgnostic: true, - convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`, - // legacy config is marked as any in core, no choice here - // eslint-disable-next-line @typescript-eslint/no-explicit-any - indexPattern(config: any) { - return config.get('xpack.task_manager.index'); - }, - }, -}; - export function taskManager(kibana: LegacyPluginApi): ArrayOrItem { return new kibana.Plugin({ id: 'task_manager', @@ -58,7 +43,7 @@ export function taskManager(kibana: LegacyPluginApi): ArrayOrItem { // we can't tell the Kibana Platform Task Manager plugin to // to wait to `start` as that happens before legacy plugins @@ -77,10 +62,5 @@ export function taskManager(kibana: LegacyPluginApi): ArrayOrItem - new kibana.Plugin({ - configPrefix: 'xpack.uptime', - id: PLUGIN.ID, - publicDir: resolve(__dirname, 'public'), - require: ['alerting', 'kibana', 'elasticsearch', 'xpack_main'], - uiExports: { - app: { - description: i18n.translate('xpack.uptime.pluginDescription', { - defaultMessage: 'Uptime monitoring', - description: 'The description text that will be shown to users in Kibana', - }), - icon: 'plugins/uptime/icons/heartbeat_white.svg', - euiIconType: 'uptimeApp', - title: i18n.translate('xpack.uptime.uptimeFeatureCatalogueTitle', { - defaultMessage: 'Uptime', - }), - main: 'plugins/uptime/app', - order: 8900, - url: '/app/uptime#/', - category: DEFAULT_APP_CATEGORIES.observability, - }, - home: ['plugins/uptime/register_feature'], - }, - }); diff --git a/x-pack/legacy/plugins/uptime/public/apps/index.ts b/x-pack/legacy/plugins/uptime/public/apps/index.ts deleted file mode 100644 index d58bf8398fcdea..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/apps/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npSetup } from 'ui/new_platform'; -import { Plugin } from './plugin'; -import 'uiExports/embeddableFactories'; - -const plugin = new Plugin({ - opaqueId: Symbol('uptime'), - env: {} as any, - config: { get: () => ({} as any) }, -}); -plugin.setup(npSetup); diff --git a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts b/x-pack/legacy/plugins/uptime/public/apps/plugin.ts deleted file mode 100644 index e73598c44c9f08..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { LegacyCoreSetup, PluginInitializerContext, AppMountParameters } from 'src/core/public'; -import { PluginsSetup } from 'ui/new_platform/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; -import { UMFrontendLibs } from '../lib/lib'; -import { PLUGIN } from '../../common/constants'; -import { getKibanaFrameworkAdapter } from '../lib/adapters/framework/new_platform_adapter'; - -export interface SetupObject { - core: LegacyCoreSetup; - plugins: PluginsSetup; -} - -export class Plugin { - constructor( - // @ts-ignore this is added to satisfy the New Platform typing constraint, - // but we're not leveraging any of its functionality yet. - private readonly initializerContext: PluginInitializerContext - ) {} - - public setup(setup: SetupObject) { - const { core, plugins } = setup; - const { home } = plugins; - - home.featureCatalogue.register({ - category: FeatureCatalogueCategory.DATA, - description: PLUGIN.DESCRIPTION, - icon: 'uptimeApp', - id: PLUGIN.ID, - path: '/app/uptime#/', - showOnHomePage: true, - title: PLUGIN.TITLE, - }); - - core.application.register({ - id: PLUGIN.ID, - euiIconType: 'uptimeApp', - order: 8900, - title: 'Uptime', - async mount(params: AppMountParameters) { - const [coreStart] = await core.getStartServices(); - const { element } = params; - const libs: UMFrontendLibs = { - framework: getKibanaFrameworkAdapter(coreStart, plugins), - }; - libs.framework.render(element); - return () => {}; - }, - }); - } -} diff --git a/x-pack/legacy/plugins/uptime/public/register_feature.ts b/x-pack/legacy/plugins/uptime/public/register_feature.ts deleted file mode 100644 index 2f83fa33ba4bc5..00000000000000 --- a/x-pack/legacy/plugins/uptime/public/register_feature.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 { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -const { - plugins: { home }, -} = npSetup; - -home.featureCatalogue.register({ - id: 'uptime', - title: i18n.translate('xpack.uptime.uptimeFeatureCatalogueTitle', { defaultMessage: 'Uptime' }), - description: i18n.translate('xpack.uptime.featureCatalogueDescription', { - defaultMessage: 'Perform endpoint health checks and uptime monitoring.', - }), - icon: 'uptimeApp', - path: `uptime#/`, - showOnHomePage: true, - category: FeatureCatalogueCategory.DATA, -}); diff --git a/x-pack/legacy/plugins/uptime/tsconfig.json b/x-pack/legacy/plugins/uptime/tsconfig.json deleted file mode 100644 index 53425909db3e8b..00000000000000 --- a/x-pack/legacy/plugins/uptime/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "exclude": ["**/node_modules/**"], - "paths": { - "react": ["../../../node_modules/@types/react"] - } -} \ No newline at end of file diff --git a/x-pack/package.json b/x-pack/package.json index 604889c6094b9c..dcc9b8c61cb960 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -105,6 +105,7 @@ "@types/recompose": "^0.30.6", "@types/reduce-reducers": "^1.0.0", "@types/redux-actions": "^2.6.1", + "@types/set-value": "^2.0.0", "@types/sinon": "^7.0.13", "@types/styled-components": "^4.4.2", "@types/supertest": "^2.0.5", @@ -344,6 +345,7 @@ "rison-node": "0.3.1", "rxjs": "^6.5.3", "semver": "5.7.0", + "set-value": "^3.0.2", "squel": "^5.13.0", "stats-lite": "^2.2.0", "style-it": "^2.1.3", diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 101e18f2583e39..3e9262c05efac4 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -17,7 +17,7 @@ import { import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server'; import { SpacesServiceSetup } from '../../../spaces/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; -import { IEvent, IEventLogger } from '../../../event_log/server'; +import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; export interface ActionExecutorContext { logger: Logger; @@ -110,7 +110,16 @@ export class ActionExecutor { const actionLabel = `${actionTypeId}:${actionId}: ${name}`; const event: IEvent = { event: { action: EVENT_LOG_ACTIONS.execute }, - kibana: { saved_objects: [{ type: 'action', id: actionId, ...namespace }] }, + kibana: { + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'action', + id: actionId, + ...namespace, + }, + ], + }, }; eventLogger.startTiming(event); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 10e4d645843401..a6cc1fb5463bbb 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -53,6 +53,7 @@ import { } from './routes'; import { IEventLogger, IEventLogService } from '../../event_log/server'; import { initializeActionsTelemetry, scheduleActionsTelemetry } from './usage/task'; +import { setupSavedObjects } from './saved_objects'; const EVENT_LOG_PROVIDER = 'actions'; export const EVENT_LOG_ACTIONS = { @@ -133,19 +134,7 @@ export class ActionsPlugin implements Plugin, Plugi ); } - // Encrypted attributes - // - `secrets` properties will be encrypted - // - `config` will be included in AAD - // - everything else excluded from AAD - plugins.encryptedSavedObjects.registerType({ - type: 'action', - attributesToEncrypt: new Set(['secrets']), - attributesToExcludeFromAAD: new Set(['name']), - }); - plugins.encryptedSavedObjects.registerType({ - type: 'action_task_params', - attributesToEncrypt: new Set(['apiKey']), - }); + setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects); plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS)); this.eventLogger = plugins.eventLog.getLogger({ diff --git a/x-pack/plugins/actions/server/saved_objects/index.ts b/x-pack/plugins/actions/server/saved_objects/index.ts new file mode 100644 index 00000000000000..dbd7925f968713 --- /dev/null +++ b/x-pack/plugins/actions/server/saved_objects/index.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 { SavedObjectsServiceSetup } from 'kibana/server'; +import mappings from './mappings.json'; +import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; + +export function setupSavedObjects( + savedObjects: SavedObjectsServiceSetup, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + savedObjects.registerType({ + name: 'action', + hidden: false, + namespaceType: 'single', + mappings: mappings.action, + }); + + // Encrypted attributes + // - `secrets` properties will be encrypted + // - `config` will be included in AAD + // - everything else excluded from AAD + encryptedSavedObjects.registerType({ + type: 'action', + attributesToEncrypt: new Set(['secrets']), + attributesToExcludeFromAAD: new Set(['name']), + }); + + savedObjects.registerType({ + name: 'action_task_params', + hidden: false, + namespaceType: 'single', + mappings: mappings.action_task_params, + }); + encryptedSavedObjects.registerType({ + type: 'action_task_params', + attributesToEncrypt: new Set(['apiKey']), + }); +} diff --git a/x-pack/legacy/plugins/actions/server/mappings.json b/x-pack/plugins/actions/server/saved_objects/mappings.json similarity index 100% rename from x-pack/legacy/plugins/actions/server/mappings.json rename to x-pack/plugins/actions/server/saved_objects/mappings.json diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index c03d3506a051d4..8cdde2eeb9877a 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -58,6 +58,7 @@ import { Services } from './types'; import { registerAlertsUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; import { IEventLogger, IEventLogService } from '../../event_log/server'; +import { setupSavedObjects } from './saved_objects'; const EVENT_LOG_PROVIDER = 'alerting'; export const EVENT_LOG_ACTIONS = { @@ -134,17 +135,7 @@ export class AlertingPlugin { ); } - // Encrypted attributes - plugins.encryptedSavedObjects.registerType({ - type: 'alert', - attributesToEncrypt: new Set(['apiKey']), - attributesToExcludeFromAAD: new Set([ - 'scheduledTaskId', - 'muteAll', - 'mutedInstanceIds', - 'updatedBy', - ]), - }); + setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects); plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS)); this.eventLogger = plugins.eventLog.getLogger({ diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts new file mode 100644 index 00000000000000..4efec2fe55ef01 --- /dev/null +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsServiceSetup } from 'kibana/server'; +import mappings from './mappings.json'; +import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; + +export function setupSavedObjects( + savedObjects: SavedObjectsServiceSetup, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + savedObjects.registerType({ + name: 'alert', + hidden: false, + namespaceType: 'single', + mappings: mappings.alert, + }); + + // Encrypted attributes + encryptedSavedObjects.registerType({ + type: 'alert', + attributesToEncrypt: new Set(['apiKey']), + attributesToExcludeFromAAD: new Set([ + 'scheduledTaskId', + 'muteAll', + 'mutedInstanceIds', + 'updatedBy', + ]), + }); +} diff --git a/x-pack/legacy/plugins/alerting/server/mappings.json b/x-pack/plugins/alerting/server/saved_objects/mappings.json similarity index 100% rename from x-pack/legacy/plugins/alerting/server/mappings.json rename to x-pack/plugins/alerting/server/saved_objects/mappings.json diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 0e46ef4919626a..a564b87f2ca50c 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -95,6 +95,7 @@ test('calls actionsPlugin.execute per selected action', async () => { "saved_objects": Array [ Object { "id": "1", + "rel": "primary", "type": "alert", }, Object { diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 5c3e36b88879dd..16fadc8b06cd59 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -9,7 +9,7 @@ import { AlertAction, State, Context, AlertType } from '../types'; import { Logger } from '../../../../../src/core/server'; import { transformActionParams } from './transform_action_params'; import { PluginStartContract as ActionsPluginStartContract } from '../../../../plugins/actions/server'; -import { IEventLogger, IEvent } from '../../../event_log/server'; +import { IEventLogger, IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS } from '../plugin'; interface CreateExecutionHandlerOptions { @@ -96,7 +96,7 @@ export function createExecutionHandler({ instance_id: alertInstanceId, }, saved_objects: [ - { type: 'alert', id: alertId, ...namespace }, + { rel: SAVED_OBJECT_REL_PRIMARY, type: 'alert', id: alertId, ...namespace }, { type: 'action', id: action.id, ...namespace }, ], }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 26d8a1d1777c04..35a0018049c335 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -172,6 +172,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -234,6 +235,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -254,6 +256,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -274,6 +277,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, Object { @@ -351,6 +355,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -371,6 +376,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], @@ -568,6 +574,7 @@ describe('Task Runner', () => { Object { "id": "1", "namespace": undefined, + "rel": "primary", "type": "alert", }, ], diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 26970dc6b2b0d9..bf005301adc07c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -25,7 +25,7 @@ import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/ import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { AlertInstances } from '../alert_instance/alert_instance'; import { EVENT_LOG_ACTIONS } from '../plugin'; -import { IEvent, IEventLogger } from '../../../event_log/server'; +import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; const FALLBACK_RETRY_INTERVAL: IntervalSchedule = { interval: '5m' }; @@ -174,7 +174,16 @@ export class TaskRunner { const alertLabel = `${this.alertType.id}:${alertId}: '${name}'`; const event: IEvent = { event: { action: EVENT_LOG_ACTIONS.execute }, - kibana: { saved_objects: [{ type: 'alert', id: alertId, namespace }] }, + kibana: { + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: alertId, + namespace, + }, + ], + }, }; eventLogger.startTiming(event); @@ -393,7 +402,14 @@ function generateNewAndResolvedInstanceEvents(params: GenerateNewAndResolvedInst alerting: { instance_id: id, }, - saved_objects: [{ type: 'alert', id: params.alertId, namespace: params.namespace }], + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: 'alert', + id: params.alertId, + namespace: params.namespace, + }, + ], }, message, }; diff --git a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts index a7d6aa894d91db..6c635cc5b4489a 100644 --- a/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/async_search_strategy.test.ts @@ -96,6 +96,34 @@ describe('Async search strategy', () => { }); }); + // For bug fixed in https://github.com/elastic/kibana/pull/64155 + it('Continues polling if no records are returned on first async request', async () => { + mockSearch + .mockReturnValueOnce(of({ id: 1, total: 0, loaded: 0, is_running: true, is_partial: true })) + .mockReturnValueOnce( + of({ id: 1, total: 2, loaded: 2, is_running: false, is_partial: false }) + ); + + const asyncSearch = asyncSearchStrategyProvider({ + core: mockCoreStart, + getSearchStrategy: jest.fn().mockImplementation(() => { + return () => { + return { + search: mockSearch, + }; + }; + }), + }); + + expect(mockSearch).toBeCalledTimes(0); + + await asyncSearch.search(mockRequest, mockOptions).toPromise(); + + expect(mockSearch).toBeCalledTimes(2); + expect(mockSearch.mock.calls[0][0]).toEqual(mockRequest); + expect(mockSearch.mock.calls[1][0]).toEqual({ id: 1, serverStrategy: 'foo' }); + }); + it('only sends the ID and server strategy after the first request', async () => { mockSearch .mockReturnValueOnce(of({ id: 1, total: 2, loaded: 1, is_running: true, is_partial: true })) diff --git a/x-pack/plugins/endpoint/common/alert_constants.ts b/x-pack/plugins/endpoint/common/alert_constants.ts new file mode 100644 index 00000000000000..85e1643d684f2c --- /dev/null +++ b/x-pack/plugins/endpoint/common/alert_constants.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export class AlertConstants { + /** + * The prefix for all Alert APIs + */ + static BASE_API_URL = '/api/endpoint'; + /** + * The path for the Alert's Index Pattern API. + */ + static INDEX_PATTERN_ROUTE = `${AlertConstants.BASE_API_URL}/index_pattern`; + /** + * Alert's Index pattern + */ + static ALERT_INDEX_NAME = 'events-endpoint-1'; + /** + * A paramter passed to Alert's Index Pattern. + */ + static EVENT_DATASET = 'events'; + /** + * Alert's Search API default page size + */ + static DEFAULT_TOTAL_HITS = 10000; + /** + * Alerts + **/ + static ALERT_LIST_DEFAULT_PAGE_SIZE = 10; + static ALERT_LIST_DEFAULT_SORT = '@timestamp'; + static MAX_LONG_INT = '9223372036854775807'; // 2^63-1 +} diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 4f5a6eaeb0a5eb..e40fc3e386bc89 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -9,9 +9,9 @@ import seedrandom from 'seedrandom'; import { AlertEvent, EndpointEvent, - HostFields, + Host, HostMetadata, - OSFields, + HostOS, PolicyData, HostPolicyResponse, HostPolicyResponseActionStatus, @@ -29,7 +29,7 @@ interface EventOptions { processName?: string; } -const Windows: OSFields[] = [ +const Windows: HostOS[] = [ { name: 'windows 10.0', full: 'Windows 10', @@ -56,11 +56,11 @@ const Windows: OSFields[] = [ }, ]; -const Linux: OSFields[] = []; +const Linux: HostOS[] = []; -const Mac: OSFields[] = []; +const Mac: HostOS[] = []; -const OS: OSFields[] = [...Windows, ...Mac, ...Linux]; +const OS: HostOS[] = [...Windows, ...Mac, ...Linux]; const POLICIES: Array<{ name: string; id: string }> = [ { @@ -102,7 +102,7 @@ interface HostInfo { version: string; id: string; }; - host: HostFields; + host: Host; endpoint: { policy: { id: string; @@ -307,7 +307,7 @@ export class EndpointDocGenerator { process: { entity_id: options.entityID ? options.entityID : this.randomString(10), parent: options.parentEntityID ? { entity_id: options.parentEntityID } : undefined, - name: options.processName ? options.processName : 'powershell.exe', + name: options.processName ? options.processName : randomProcessName(), }, }; } @@ -645,3 +645,16 @@ export class EndpointDocGenerator { return uuid.v4({ random: [...this.randomNGenerator(255, 16)] }); } } + +const fakeProcessNames = [ + 'lsass.exe', + 'notepad.exe', + 'mimikatz.exe', + 'powershell.exe', + 'iexlorer.exe', + 'explorer.exe', +]; +/** Return a random fake process name */ +function randomProcessName(): string { + return fakeProcessNames[Math.floor(Math.random() * fakeProcessNames.length)]; +} diff --git a/x-pack/plugins/endpoint/common/models/event.ts b/x-pack/plugins/endpoint/common/models/event.ts index 650486f3c3858d..47f39d2d117976 100644 --- a/x-pack/plugins/endpoint/common/models/event.ts +++ b/x-pack/plugins/endpoint/common/models/event.ts @@ -4,17 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EndpointEvent, LegacyEndpointEvent } from '../types'; +import { LegacyEndpointEvent, ResolverEvent } from '../types'; -export function isLegacyEvent( - event: EndpointEvent | LegacyEndpointEvent -): event is LegacyEndpointEvent { +export function isLegacyEvent(event: ResolverEvent): event is LegacyEndpointEvent { return (event as LegacyEndpointEvent).endgame !== undefined; } -export function eventTimestamp( - event: EndpointEvent | LegacyEndpointEvent -): string | undefined | number { +export function eventTimestamp(event: ResolverEvent): string | undefined | number { if (isLegacyEvent(event)) { return event.endgame.timestamp_utc; } else { @@ -22,10 +18,31 @@ export function eventTimestamp( } } -export function eventName(event: EndpointEvent | LegacyEndpointEvent): string { +export function eventName(event: ResolverEvent): string { if (isLegacyEvent(event)) { return event.endgame.process_name ? event.endgame.process_name : ''; } else { return event.process.name; } } + +export function eventId(event: ResolverEvent): string { + if (isLegacyEvent(event)) { + return event.endgame.serial_event_id ? String(event.endgame.serial_event_id) : ''; + } + return event.event.id; +} + +export function entityId(event: ResolverEvent): string { + if (isLegacyEvent(event)) { + return event.endgame.unique_pid ? String(event.endgame.unique_pid) : ''; + } + return event.process.entity_id; +} + +export function parentEntityId(event: ResolverEvent): string | undefined { + if (isLegacyEvent(event)) { + return event.endgame.unique_ppid ? String(event.endgame.unique_ppid) : undefined; + } + return event.process.parent?.entity_id; +} diff --git a/x-pack/plugins/endpoint/common/schema/alert_index.ts b/x-pack/plugins/endpoint/common/schema/alert_index.ts index 7b48780f2d86bb..cffc00661515f6 100644 --- a/x-pack/plugins/endpoint/common/schema/alert_index.ts +++ b/x-pack/plugins/endpoint/common/schema/alert_index.ts @@ -7,7 +7,7 @@ import { schema, Type } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import { decode } from 'rison-node'; -import { EndpointAppConstants } from '../types'; +import { AlertConstants } from '../alert_constants'; /** * Used to validate GET requests against the index of the alerting APIs. @@ -18,7 +18,7 @@ export const alertingIndexGetQuerySchema = schema.object( schema.number({ min: 1, max: 100, - defaultValue: EndpointAppConstants.ALERT_LIST_DEFAULT_PAGE_SIZE, + defaultValue: AlertConstants.ALERT_LIST_DEFAULT_PAGE_SIZE, }) ), page_index: schema.maybe( diff --git a/x-pack/plugins/endpoint/common/schema/resolver.ts b/x-pack/plugins/endpoint/common/schema/resolver.ts new file mode 100644 index 00000000000000..f21307e407fd0a --- /dev/null +++ b/x-pack/plugins/endpoint/common/schema/resolver.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 { schema } from '@kbn/config-schema'; + +/** + * Used to validate GET requests for a complete resolver tree. + */ +export const validateTree = { + params: schema.object({ id: schema.string() }), + query: schema.object({ + children: schema.number({ defaultValue: 10, min: 0, max: 100 }), + generations: schema.number({ defaultValue: 3, min: 0, max: 3 }), + ancestors: schema.number({ defaultValue: 3, min: 0, max: 5 }), + events: schema.number({ defaultValue: 100, min: 0, max: 1000 }), + afterEvent: schema.maybe(schema.string()), + afterChild: schema.maybe(schema.string()), + legacyEndpointID: schema.maybe(schema.string()), + }), +}; + +/** + * Used to validate GET requests for non process events for a specific event. + */ +export const validateEvents = { + params: schema.object({ id: schema.string() }), + query: schema.object({ + events: schema.number({ defaultValue: 100, min: 1, max: 1000 }), + afterEvent: schema.maybe(schema.string()), + legacyEndpointID: schema.maybe(schema.string()), + }), +}; + +/** + * Used to validate GET requests for the ancestors of a process event. + */ +export const validateAncestry = { + params: schema.object({ id: schema.string() }), + query: schema.object({ + ancestors: schema.number({ defaultValue: 0, min: 0, max: 10 }), + legacyEndpointID: schema.maybe(schema.string()), + }), +}; + +/** + * Used to validate GET requests for children of a specified process event. + */ +export const validateChildren = { + params: schema.object({ id: schema.string() }), + query: schema.object({ + children: schema.number({ defaultValue: 10, min: 10, max: 100 }), + generations: schema.number({ defaultValue: 3, min: 0, max: 3 }), + afterChild: schema.maybe(schema.string()), + legacyEndpointID: schema.maybe(schema.string()), + }), +}; diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 4da4f9bc797ca6..8fce15d1c794c8 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -25,32 +25,44 @@ export type Immutable = T extends undefined | null | boolean | string | numbe ? ImmutableSet : ImmutableObject; -export type ImmutableArray = ReadonlyArray>; -export type ImmutableMap = ReadonlyMap, Immutable>; -export type ImmutableSet = ReadonlySet>; -export type ImmutableObject = { readonly [K in keyof T]: Immutable }; - -export type Direction = 'asc' | 'desc'; - -export class EndpointAppConstants { - static BASE_API_URL = '/api/endpoint'; - static INDEX_PATTERN_ROUTE = `${EndpointAppConstants.BASE_API_URL}/index_pattern`; - static ALERT_INDEX_NAME = 'events-endpoint-1'; - static EVENT_DATASET = 'events'; - static DEFAULT_TOTAL_HITS = 10000; - /** - * Legacy events are stored in indices with endgame-* prefix - */ - static LEGACY_EVENT_INDEX_NAME = 'endgame-*'; +type ImmutableArray = ReadonlyArray>; +type ImmutableMap = ReadonlyMap, Immutable>; +type ImmutableSet = ReadonlySet>; +type ImmutableObject = { readonly [K in keyof T]: Immutable }; - /** - * Alerts - **/ - static ALERT_LIST_DEFAULT_PAGE_SIZE = 10; - static ALERT_LIST_DEFAULT_SORT = '@timestamp'; - static MAX_LONG_INT = '9223372036854775807'; // 2^63-1 +/** + * Values for the Alert APIs 'order' and 'direction' parameters. + */ +export type AlertAPIOrdering = 'asc' | 'desc'; + +export interface ResolverNodeStats { + totalEvents: number; + totalAlerts: number; +} + +export interface ResolverNodePagination { + nextChild?: string | null; + nextEvent?: string | null; + nextAncestor?: string | null; + nextAlert?: string | null; } +/** + * A node that contains pointers to other nodes, arrrays of resolver events, and any metadata associated with resolver specific data + */ +export interface ResolverNode { + id: string; + children: ResolverNode[]; + events: ResolverEvent[]; + lifecycle: ResolverEvent[]; + ancestors?: ResolverNode[]; + pagination: ResolverNodePagination; + stats?: ResolverNodeStats; +} + +/** + * Returned by 'api/endpoint/alerts' + */ export interface AlertResultList { /** * The alerts restricted by page size. @@ -88,6 +100,9 @@ export interface AlertResultList { prev: string | null; } +/** + * Returned by the server via /api/endpoint/metadata + */ export interface HostResultList { /* the hosts restricted by the page size */ hosts: HostInfo[]; @@ -99,43 +114,61 @@ export interface HostResultList { request_page_index: number; } -export interface OSFields { +/** + * Operating System metadata for a host. + */ +export interface HostOS { full: string; name: string; version: string; variant: string; } -export interface HostFields { + +/** + * Host metadata. Describes an endpoint host. + */ +export interface Host { id: string; hostname: string; ip: string[]; mac: string[]; - os: OSFields; + os: HostOS; } -export interface HashFields { + +/** + * A record of hashes for something. Provides hashes in multiple formats. A favorite structure of the Elastic Endpoint. + */ +interface Hashes { + /** + * A hash in MD5 format. + */ md5: string; + /** + * A hash in SHA-1 format. + */ sha1: string; + /** + * A hash in SHA-256 format. + */ sha256: string; } -export interface MalwareClassificationFields { + +interface MalwareClassification { identifier: string; score: number; threshold: number; version: string; } -export interface PrivilegesFields { - description: string; - name: string; - enabled: boolean; -} -export interface ThreadFields { + +interface ThreadFields { id: number; service_name: string; start: number; start_address: number; start_address_module: string; } -export interface DllFields { + +interface DllFields { pe: { architecture: string; imphash: string; @@ -145,8 +178,8 @@ export interface DllFields { trusted: boolean; }; compile_time: number; - hash: HashFields; - malware_classification: MalwareClassificationFields; + hash: Hashes; + malware_classification: MalwareClassification; mapped_address: number; mapped_size: number; path: string; @@ -154,7 +187,6 @@ export interface DllFields { /** * Describes an Alert Event. - * Should be in line with ECS schema. */ export type AlertEvent = Immutable<{ '@timestamp': number; @@ -191,14 +223,14 @@ export type AlertEvent = Immutable<{ entity_id: string; }; name: string; - hash: HashFields; + hash: Hashes; pe?: { imphash: string; }; executable: string; sid?: string; start: number; - malware_classification?: MalwareClassificationFields; + malware_classification?: MalwareClassification; token: { domain: string; type: string; @@ -206,7 +238,11 @@ export type AlertEvent = Immutable<{ sid: string; integrity_level: number; integrity_level_name: string; - privileges?: PrivilegesFields[]; + privileges?: Array<{ + description: string; + name: string; + enabled: boolean; + }>; }; thread?: ThreadFields[]; uptime: number; @@ -220,7 +256,7 @@ export type AlertEvent = Immutable<{ mtime: number; created: number; size: number; - hash: HashFields; + hash: Hashes; pe?: { imphash: string; }; @@ -228,10 +264,10 @@ export type AlertEvent = Immutable<{ trusted: boolean; subject_name: string; }; - malware_classification: MalwareClassificationFields; + malware_classification: MalwareClassification; temp_file_path: string; }; - host: HostFields; + host: Host; dll?: DllFields[]; }>; @@ -249,9 +285,6 @@ interface AlertState { }; } -/** - * Union of alert data and metadata. - */ export type AlertData = AlertEvent & AlertMetadata; export type AlertDetails = AlertData & AlertState; @@ -301,7 +334,7 @@ export type HostMetadata = Immutable<{ id: string; version: string; }; - host: HostFields; + host: Host; }>; /** @@ -365,7 +398,7 @@ export interface EndpointEvent { hostname: string; ip: string[]; mac: string[]; - os: OSFields; + os: HostOS; }; process: { entity_id: string; @@ -500,28 +533,22 @@ export interface PolicyConfig { }; } -/** - * Windows-specific policy configuration that is supported via the UI - */ -type WindowsPolicyConfig = Pick; - -/** - * Mac-specific policy configuration that is supported via the UI - */ -type MacPolicyConfig = Pick; - -/** - * Linux-specific policy configuration that is supported via the UI - */ -type LinuxPolicyConfig = Pick; - /** * The set of Policy configuration settings that are show/edited via the UI */ export interface UIPolicyConfig { - windows: WindowsPolicyConfig; - mac: MacPolicyConfig; - linux: LinuxPolicyConfig; + /** + * Windows-specific policy configuration that is supported via the UI + */ + windows: Pick; + /** + * Mac-specific policy configuration that is supported via the UI + */ + mac: Pick; + /** + * Linux-specific policy configuration that is supported via the UI + */ + linux: Pick; } interface PolicyConfigAdvancedOptions { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts index 90d6b8b82198af..6bc728db99819c 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/middleware.ts @@ -9,7 +9,7 @@ import { AlertResultList, AlertDetails } from '../../../../../common/types'; import { ImmutableMiddlewareFactory, AlertListState } from '../../types'; import { isOnAlertPage, apiQueryParams, hasSelectedAlert, uiQueryParams } from './selectors'; import { cloneHttpFetchQuery } from '../../../../common/clone_http_fetch_query'; -import { EndpointAppConstants } from '../../../../../common/types'; +import { AlertConstants } from '../../../../../common/alert_constants'; export const alertMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart, @@ -18,7 +18,7 @@ export const alertMiddlewareFactory: ImmutableMiddlewareFactory async function fetchIndexPatterns(): Promise { const { indexPatterns } = depsStart.data; const eventsPattern: { indexPattern: string } = await coreStart.http.get( - `${EndpointAppConstants.INDEX_PATTERN_ROUTE}/${EndpointAppConstants.EVENT_DATASET}` + `${AlertConstants.INDEX_PATTERN_ROUTE}/${AlertConstants.EVENT_DATASET}` ); const fields = await indexPatterns.getFieldsForWildcard({ pattern: eventsPattern.indexPattern, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts index 69b11fb3c1f0ea..9912c9a81e6e14 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts @@ -16,6 +16,7 @@ import { setPolicyListApiMockImplementation } from './test_mock_utils'; import { INGEST_API_DATASOURCES } from './services/ingest'; import { Immutable } from '../../../../../common/types'; import { createSpyMiddleware, MiddlewareActionSpyHelper } from '../test_utils'; +import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../ingest_manager/common'; describe('policy list store concerns', () => { let fakeCoreStart: ReturnType; @@ -121,7 +122,11 @@ describe('policy list store concerns', () => { }); await waitForAction('serverReturnedPolicyListData'); expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_DATASOURCES, { - query: { kuery: 'datasources.package.name: endpoint', page: 1, perPage: 10 }, + query: { + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, + page: 1, + perPage: 10, + }, }); }); @@ -140,7 +145,11 @@ describe('policy list store concerns', () => { dispatchUserChangedUrl('?page_size=50&page_index=0'); await waitForAction('serverReturnedPolicyListData'); expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_DATASOURCES, { - query: { kuery: 'datasources.package.name: endpoint', page: 1, perPage: 50 }, + query: { + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, + page: 1, + perPage: 50, + }, }); }); it('uses defaults for params not in url', async () => { @@ -159,21 +168,33 @@ describe('policy list store concerns', () => { dispatchUserChangedUrl('?page_size=-50&page_index=-99'); await waitForAction('serverReturnedPolicyListData'); expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_DATASOURCES, { - query: { kuery: 'datasources.package.name: endpoint', page: 1, perPage: 10 }, + query: { + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, + page: 1, + perPage: 10, + }, }); }); it('it ignores non-numeric values for page_index and page_size', async () => { dispatchUserChangedUrl('?page_size=fifty&page_index=ten'); await waitForAction('serverReturnedPolicyListData'); expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_DATASOURCES, { - query: { kuery: 'datasources.package.name: endpoint', page: 1, perPage: 10 }, + query: { + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, + page: 1, + perPage: 10, + }, }); }); it('accepts only known values for `page_size`', async () => { dispatchUserChangedUrl('?page_size=300&page_index=10'); await waitForAction('serverReturnedPolicyListData'); expect(fakeCoreStart.http.get).toHaveBeenCalledWith(INGEST_API_DATASOURCES, { - query: { kuery: 'datasources.package.name: endpoint', page: 11, perPage: 10 }, + query: { + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, + page: 11, + perPage: 10, + }, }); }); it(`ignores unknown url search params`, async () => { @@ -186,8 +207,8 @@ describe('policy list store concerns', () => { it(`uses last param value if param is defined multiple times`, async () => { dispatchUserChangedUrl('?page_size=20&page_size=50&page_index=20&page_index=40'); expect(urlSearchParams(getState())).toEqual({ - page_index: 20, - page_size: 20, + page_index: 40, + page_size: 50, }); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts index 6d2e952fa07bba..4986a342cca19e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts @@ -46,12 +46,16 @@ export const urlSearchParams: ( const query = parse(location.search); // Search params can appear multiple times in the URL, in which case the value for them, - // once parsed, would be an array. In these case, we take the first value defined + // once parsed, would be an array. In these case, we take the last value defined searchParams.page_index = Number( - (Array.isArray(query.page_index) ? query.page_index[0] : query.page_index) ?? 0 + (Array.isArray(query.page_index) + ? query.page_index[query.page_index.length - 1] + : query.page_index) ?? 0 ); searchParams.page_size = Number( - (Array.isArray(query.page_size) ? query.page_size[0] : query.page_size) ?? 10 + (Array.isArray(query.page_size) + ? query.page_size[query.page_size.length - 1] + : query.page_size) ?? 10 ); // If pageIndex is not a valid positive integer, set it to 0 diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.test.ts index c2865d36c95f2d..46f4c09e05a745 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.test.ts @@ -6,6 +6,7 @@ import { sendGetDatasource, sendGetEndpointSpecificDatasources } from './ingest'; import { httpServiceMock } from '../../../../../../../../../src/core/public/mocks'; +import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../../../../ingest_manager/common'; describe('ingest service', () => { let http: ReturnType; @@ -19,7 +20,7 @@ describe('ingest service', () => { await sendGetEndpointSpecificDatasources(http); expect(http.get).toHaveBeenCalledWith('/api/ingest_manager/datasources', { query: { - kuery: 'datasources.package.name: endpoint', + kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, }, }); }); @@ -29,7 +30,7 @@ describe('ingest service', () => { }); expect(http.get).toHaveBeenCalledWith('/api/ingest_manager/datasources', { query: { - kuery: 'someValueHere and datasources.package.name: endpoint', + kuery: `someValueHere and ${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, perPage: 10, page: 1, }, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.ts index 4356517e43c2cb..5c27680d6a35c7 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/services/ingest.ts @@ -8,6 +8,7 @@ import { HttpFetchOptions, HttpStart } from 'kibana/public'; import { GetDatasourcesRequest, GetAgentStatusResponse, + DATASOURCE_SAVED_OBJECT_TYPE, } from '../../../../../../../ingest_manager/common'; import { GetPolicyListResponse, GetPolicyResponse, UpdatePolicyResponse } from '../../../types'; import { NewPolicyData } from '../../../../../../common/types'; @@ -33,7 +34,7 @@ export const sendGetEndpointSpecificDatasources = ( ...options.query, kuery: `${ options?.query?.kuery ? options.query.kuery + ' and ' : '' - }datasources.package.name: endpoint`, + }${DATASOURCE_SAVED_OBJECT_TYPE}.package.name: endpoint`, }, }); }; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 82b812b79670f7..58e706f20ec8e0 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -17,7 +17,6 @@ import { AlertData, AlertResultList, Immutable, - ImmutableArray, AlertDetails, MalwareFields, UIPolicyConfig, @@ -312,7 +311,7 @@ export type AlertListData = AlertResultList; export interface AlertListState { /** Array of alert items. */ - readonly alerts: ImmutableArray; + readonly alerts: Immutable; /** The total number of alerts on the page. */ readonly total: number; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx index d18bc59a35f52e..d32ad4dd9defc6 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx @@ -33,5 +33,6 @@ export const AlertDetailResolver = styled( width: 100%; display: flex; flex-grow: 1; - min-height: 500px; + /* gross demo hack */ + min-height: calc(100vh - 505px); `; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx index ea9eb292dba1a9..d9bb7eabcf7b02 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx @@ -52,7 +52,7 @@ export const PolicyDetails = React.memo(() => { const [showConfirm, setShowConfirm] = useState(false); const policyName = policyItem?.name ?? ''; - // Handle showing udpate statuses + // Handle showing update statuses useEffect(() => { if (policyUpdateStatus) { if (policyUpdateStatus.success) { @@ -79,7 +79,7 @@ export const PolicyDetails = React.memo(() => { }); } } - }, [notifications.toasts, policyItem, policyName, policyUpdateStatus]); + }, [notifications.toasts, policyName, policyUpdateStatus]); const handleBackToListOnClick = useNavigateByRouterEventHandler('/policy'); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/events/windows.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/events/windows.tsx index 7f946de9614ca7..9d73f12869058f 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/events/windows.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/events/windows.tsx @@ -17,18 +17,18 @@ import { } from '../../../../store/policy_details/selectors'; import { ConfigForm } from '../config_form'; import { setIn, getIn } from '../../../../models/policy_details_config'; -import { UIPolicyConfig, ImmutableArray } from '../../../../../../../common/types'; +import { UIPolicyConfig, Immutable } from '../../../../../../../common/types'; export const WindowsEvents = React.memo(() => { const selected = usePolicyDetailsSelector(selectedWindowsEvents); const total = usePolicyDetailsSelector(totalWindowsEvents); const checkboxes = useMemo(() => { - const items: ImmutableArray<{ + const items: Immutable = [ + }>> = [ { name: i18n.translate('xpack.endpoint.policyDetailsConfig.windows.events.dllDriverLoad', { defaultMessage: 'DLL and Driver Load', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/protections/malware.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/protections/malware.tsx index 14871c71ec0386..4b2a6ece9f58fc 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/protections/malware.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_forms/protections/malware.tsx @@ -11,7 +11,7 @@ import { EuiRadio, EuiSwitch, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { htmlIdGenerator } from '@elastic/eui'; -import { Immutable, ProtectionModes, ImmutableArray } from '../../../../../../../common/types'; +import { Immutable, ProtectionModes } from '../../../../../../../common/types'; import { OS, MalwareProtectionOSes } from '../../../../types'; import { ConfigForm } from '../config_form'; import { policyConfig } from '../../../../store/policy_details/selectors'; @@ -73,11 +73,11 @@ export const MalwareProtections = React.memo(() => { // currently just taking windows.malware, but both windows.malware and mac.malware should be the same value const selected = policyDetailsConfig && policyDetailsConfig.windows.malware.mode; - const radios: ImmutableArray<{ + const radios: Immutable = useMemo(() => { + }>> = useMemo(() => { return [ { id: ProtectionModes.detect, diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts index 373afa89921dcd..3ec15f2f1985da 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts @@ -8,13 +8,11 @@ import { ResolverEvent } from '../../../../../common/types'; interface ServerReturnedResolverData { readonly type: 'serverReturnedResolverData'; - readonly payload: { - readonly data: { - readonly result: { - readonly search_results: readonly ResolverEvent[]; - }; - }; - }; + readonly payload: ResolverEvent[]; } -export type DataAction = ServerReturnedResolverData; +interface ServerFailedToReturnResolverData { + readonly type: 'serverFailedToReturnResolverData'; +} + +export type DataAction = ServerReturnedResolverData | ServerFailedToReturnResolverData; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts index f01136fe20ebf9..f95ecc63d2a666 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/graphing.test.ts @@ -8,7 +8,7 @@ import { Store, createStore } from 'redux'; import { DataAction } from './action'; import { dataReducer } from './reducer'; import { DataState } from '../../types'; -import { LegacyEndpointEvent } from '../../../../../common/types'; +import { LegacyEndpointEvent, ResolverEvent } from '../../../../../common/types'; import { graphableProcesses, processNodePositionsAndEdgeLineSegments } from './selectors'; import { mockProcessEvent } from '../../models/process_event_test_helpers'; @@ -113,13 +113,7 @@ describe('resolver graph layout', () => { }); describe('when rendering no nodes', () => { beforeEach(() => { - const payload = { - data: { - result: { - search_results: [], - }, - }, - }; + const payload: ResolverEvent[] = []; const action: DataAction = { type: 'serverReturnedResolverData', payload }; store.dispatch(action); }); @@ -133,13 +127,7 @@ describe('resolver graph layout', () => { }); describe('when rendering one node', () => { beforeEach(() => { - const payload = { - data: { - result: { - search_results: [processA], - }, - }, - }; + const payload = [processA]; const action: DataAction = { type: 'serverReturnedResolverData', payload }; store.dispatch(action); }); @@ -153,13 +141,7 @@ describe('resolver graph layout', () => { }); describe('when rendering two nodes, one being the parent of the other', () => { beforeEach(() => { - const payload = { - data: { - result: { - search_results: [processA, processB], - }, - }, - }; + const payload = [processA, processB]; const action: DataAction = { type: 'serverReturnedResolverData', payload }; store.dispatch(action); }); @@ -173,23 +155,17 @@ describe('resolver graph layout', () => { }); describe('when rendering two forks, and one fork has an extra long tine', () => { beforeEach(() => { - const payload = { - data: { - result: { - search_results: [ - processA, - processB, - processC, - processD, - processE, - processF, - processG, - processH, - processI, - ], - }, - }, - }; + const payload = [ + processA, + processB, + processC, + processD, + processE, + processF, + processG, + processH, + processI, + ]; const action: DataAction = { type: 'serverReturnedResolverData', payload }; store.dispatch(action); }); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts index a3184389a794e8..fc307002819a92 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/reducer.ts @@ -11,25 +11,28 @@ function initialState(): DataState { return { results: [], isLoading: false, + hasError: false, }; } export const dataReducer: Reducer = (state = initialState(), action) => { if (action.type === 'serverReturnedResolverData') { - const { - data: { - result: { search_results }, - }, - } = action.payload; return { ...state, - results: search_results, + results: action.payload, isLoading: false, + hasError: false, }; } else if (action.type === 'appRequestedResolverData') { return { ...state, isLoading: true, + hasError: false, + }; + } else if (action.type === 'serverFailedToReturnResolverData') { + return { + ...state, + hasError: true, }; } else { return state; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts index 5dda54d4ed0298..59ee4b3b875055 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts @@ -34,6 +34,10 @@ export function isLoading(state: DataState) { return state.isLoading; } +export function hasError(state: DataState) { + return state.hasError; +} + /** * An isometric projection is a method for representing three dimensional objects in 2 dimensions. * More information about isometric projections can be found here https://en.wikipedia.org/wiki/Isometric_projection. @@ -293,7 +297,7 @@ function* levelOrderWithWidths( metadata.firstChildWidth = width; } else { const firstChildWidth = widths.get(siblings[0]); - const lastChildWidth = widths.get(siblings[0]); + const lastChildWidth = widths.get(siblings[siblings.length - 1]); if (firstChildWidth === undefined || lastChildWidth === undefined) { /** * All widths have been precalcluated, so this will not happen. diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts index 4e57212e5c0c29..c7177c6387e7a9 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts @@ -8,7 +8,7 @@ import { Dispatch, MiddlewareAPI } from 'redux'; import { KibanaReactContextValue } from '../../../../../../../src/plugins/kibana_react/public'; import { EndpointPluginServices } from '../../../plugin'; import { ResolverState, ResolverAction } from '../types'; -import { ResolverEvent } from '../../../../common/types'; +import { ResolverEvent, ResolverNode } from '../../../../common/types'; import * as event from '../../../../common/models/event'; type MiddlewareFactory = ( @@ -16,18 +16,18 @@ type MiddlewareFactory = ( ) => ( api: MiddlewareAPI, S> ) => (next: Dispatch) => (action: ResolverAction) => unknown; -interface Lifecycle { - lifecycle: ResolverEvent[]; -} -type ChildResponse = [Lifecycle]; -function flattenEvents(events: ChildResponse): ResolverEvent[] { - return events - .map((child: Lifecycle) => child.lifecycle) - .reduce( - (accumulator: ResolverEvent[], value: ResolverEvent[]) => accumulator.concat(value), - [] - ); +function flattenEvents(children: ResolverNode[], events: ResolverEvent[] = []): ResolverEvent[] { + return children.reduce((flattenedEvents, currentNode) => { + if (currentNode.lifecycle && currentNode.lifecycle.length > 0) { + flattenedEvents.push(...currentNode.lifecycle); + } + if (currentNode.children && currentNode.children.length > 0) { + return flattenEvents(currentNode.children, events); + } else { + return flattenedEvents; + } + }, events); } export const resolverMiddlewareFactory: MiddlewareFactory = context => { @@ -39,53 +39,43 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => { */ if (context?.services.http && action.payload.selectedEvent) { api.dispatch({ type: 'appRequestedResolverData' }); - let response = []; - let lifecycle: ResolverEvent[]; - let childEvents: ResolverEvent[]; - let relatedEvents: ResolverEvent[]; - let children = []; - const ancestors: ResolverEvent[] = []; - const maxAncestors = 5; - if (event.isLegacyEvent(action.payload.selectedEvent)) { - const uniquePid = action.payload.selectedEvent?.endgame?.unique_pid; - const legacyEndpointID = action.payload.selectedEvent?.agent?.id; - [{ lifecycle }, { children }, { events: relatedEvents }] = await Promise.all([ - context.services.http.get(`/api/endpoint/resolver/${uniquePid}`, { - query: { legacyEndpointID }, - }), - context.services.http.get(`/api/endpoint/resolver/${uniquePid}/children`, { - query: { legacyEndpointID }, - }), - context.services.http.get(`/api/endpoint/resolver/${uniquePid}/related`, { - query: { legacyEndpointID }, - }), - ]); - childEvents = children.length > 0 ? flattenEvents(children) : []; - } else { - const uniquePid = action.payload.selectedEvent.process.entity_id; - const ppid = action.payload.selectedEvent.process.parent?.entity_id; - async function getAncestors(pid: string | undefined) { - if (ancestors.length < maxAncestors && pid !== undefined) { - const parent = await context?.services.http.get(`/api/endpoint/resolver/${pid}`); - ancestors.push(parent.lifecycle[0]); - if (parent.lifecycle[0].process?.parent?.entity_id) { - await getAncestors(parent.lifecycle[0].process.parent.entity_id); - } - } + try { + let lifecycle: ResolverEvent[]; + let children: ResolverNode[]; + let ancestors: ResolverNode[]; + if (event.isLegacyEvent(action.payload.selectedEvent)) { + const entityId = action.payload.selectedEvent?.endgame?.unique_pid; + const legacyEndpointID = action.payload.selectedEvent?.agent?.id; + [{ lifecycle, children, ancestors }] = await Promise.all([ + context.services.http.get(`/api/endpoint/resolver/${entityId}`, { + query: { legacyEndpointID, children: 5, ancestors: 5 }, + }), + ]); + } else { + const entityId = action.payload.selectedEvent.process.entity_id; + [{ lifecycle, children, ancestors }] = await Promise.all([ + context.services.http.get(`/api/endpoint/resolver/${entityId}`, { + query: { + children: 5, + ancestors: 5, + }, + }), + ]); } - [{ lifecycle }, { children }, { events: relatedEvents }] = await Promise.all([ - context.services.http.get(`/api/endpoint/resolver/${uniquePid}`), - context.services.http.get(`/api/endpoint/resolver/${uniquePid}/children`), - context.services.http.get(`/api/endpoint/resolver/${uniquePid}/related`), - getAncestors(ppid), - ]); + const response: ResolverEvent[] = [ + ...lifecycle, + ...flattenEvents(children), + ...flattenEvents(ancestors), + ]; + api.dispatch({ + type: 'serverReturnedResolverData', + payload: response, + }); + } catch (error) { + api.dispatch({ + type: 'serverFailedToReturnResolverData', + }); } - childEvents = children.length > 0 ? flattenEvents(children) : []; - response = [...lifecycle, ...childEvents, ...relatedEvents, ...ancestors]; - api.dispatch({ - type: 'serverReturnedResolverData', - payload: { data: { result: { search_results: response } } }, - }); } } }; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts index e8ae3d08e5cb61..7d09d90881da94 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/selectors.ts @@ -102,6 +102,11 @@ function uiStateSelector(state: ResolverState) { */ export const isLoading = composeSelectors(dataStateSelector, dataSelectors.isLoading); +/** + * Whether or not the resolver encountered an error while fetching data + */ +export const hasError = composeSelectors(dataStateSelector, dataSelectors.hasError); + /** * Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a * concern-specific selector. `selector` should return the concern-specific state. diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts index d370bda0d18424..17aa598720c593 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts @@ -136,6 +136,7 @@ export type CameraState = { export interface DataState { readonly results: readonly ResolverEvent[]; isLoading: boolean; + hasError: boolean; } export type Vector2 = readonly [number, number]; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx index 36155ece57a9c4..2e7ca65c92dc1e 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx @@ -8,6 +8,7 @@ import React, { useLayoutEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import styled from 'styled-components'; import { EuiLoadingSpinner } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import * as selectors from '../store/selectors'; import { EdgeLine } from './edge_line'; import { Panel } from './panel'; @@ -59,6 +60,7 @@ export const Resolver = styled( const { projectionMatrix, ref, onMouseDown } = useCamera(); const isLoading = useSelector(selectors.isLoading); + const hasError = useSelector(selectors.hasError); const activeDescendantId = useSelector(selectors.uiActiveDescendantId); useLayoutEffect(() => { @@ -74,6 +76,16 @@ export const Resolver = styled(

+ ) : hasError ? ( +
+
+ {' '} + +
+
) : ( { } const serverResponseAction: ResolverAction = { type: 'serverReturnedResolverData', - payload: { - data: { - result: { - search_results: events, - }, - }, - }, + payload: events, }; act(() => { store.dispatch(serverResponseAction); diff --git a/x-pack/plugins/endpoint/server/index_pattern.ts b/x-pack/plugins/endpoint/server/index_pattern.ts index dcedd27fc5a3d4..903d48746bfb31 100644 --- a/x-pack/plugins/endpoint/server/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/index_pattern.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger, LoggerFactory, RequestHandlerContext } from 'kibana/server'; -import { EndpointAppConstants } from '../common/types'; +import { AlertConstants } from '../common/alert_constants'; import { ESIndexPatternService } from '../../ingest_manager/server'; export interface IndexPatternRetriever { @@ -33,7 +33,7 @@ export class IngestIndexPatternRetriever implements IndexPatternRetriever { * @returns a string representing the index pattern (e.g. `events-endpoint-*`) */ async getEventIndexPattern(ctx: RequestHandlerContext) { - return await this.getIndexPattern(ctx, EndpointAppConstants.EVENT_DATASET); + return await this.getIndexPattern(ctx, AlertConstants.EVENT_DATASET); } /** diff --git a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts index e438ab853f3b52..92f8aacbf26a29 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/details/handlers.ts @@ -5,7 +5,8 @@ */ import { GetResponse } from 'elasticsearch'; import { KibanaRequest, RequestHandler } from 'kibana/server'; -import { AlertEvent, EndpointAppConstants } from '../../../../common/types'; +import { AlertEvent } from '../../../../common/types'; +import { AlertConstants } from '../../../../common/alert_constants'; import { EndpointAppContext } from '../../../types'; import { AlertDetailsRequestParams } from '../types'; import { AlertDetailsPagination } from './lib'; @@ -22,7 +23,7 @@ export const alertDetailsHandlerWrapper = function( try { const alertId = req.params.id; const response = (await ctx.core.elasticsearch.dataClient.callAsCurrentUser('get', { - index: EndpointAppConstants.ALERT_INDEX_NAME, + index: AlertConstants.ALERT_INDEX_NAME, id: alertId, })) as GetResponse; diff --git a/x-pack/plugins/endpoint/server/routes/alerts/details/lib/pagination.ts b/x-pack/plugins/endpoint/server/routes/alerts/details/lib/pagination.ts index d482da03872c62..0f69e1bb60c44e 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/details/lib/pagination.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/details/lib/pagination.ts @@ -5,12 +5,8 @@ */ import { GetResponse, SearchResponse } from 'elasticsearch'; -import { - AlertEvent, - AlertHits, - Direction, - EndpointAppConstants, -} from '../../../../../common/types'; +import { AlertEvent, AlertHits, AlertAPIOrdering } from '../../../../../common/types'; +import { AlertConstants } from '../../../../../common/alert_constants'; import { EndpointConfigType } from '../../../../config'; import { searchESForAlerts, Pagination } from '../../lib'; import { AlertSearchQuery, SearchCursor, AlertDetailsRequestParams } from '../../types'; @@ -36,12 +32,12 @@ export class AlertDetailsPagination extends Pagination< } protected async doSearch( - direction: Direction, + direction: AlertAPIOrdering, cursor: SearchCursor ): Promise> { const reqData: AlertSearchQuery = { pageSize: 1, - sort: EndpointAppConstants.ALERT_LIST_DEFAULT_SORT, + sort: AlertConstants.ALERT_LIST_DEFAULT_SORT, order: 'desc', query: { query: '', language: 'kuery' }, filters: [] as Filter[], diff --git a/x-pack/plugins/endpoint/server/routes/alerts/index.ts b/x-pack/plugins/endpoint/server/routes/alerts/index.ts index 09de11897813b5..b61f90b5b17f5a 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/index.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/index.ts @@ -5,12 +5,12 @@ */ import { IRouter } from 'kibana/server'; import { EndpointAppContext } from '../../types'; -import { EndpointAppConstants } from '../../../common/types'; +import { AlertConstants } from '../../../common/alert_constants'; import { alertListHandlerWrapper } from './list'; import { alertDetailsHandlerWrapper, alertDetailsReqSchema } from './details'; import { alertingIndexGetQuerySchema } from '../../../common/schema/alert_index'; -export const BASE_ALERTS_ROUTE = `${EndpointAppConstants.BASE_API_URL}/alerts`; +export const BASE_ALERTS_ROUTE = `${AlertConstants.BASE_API_URL}/alerts`; export function registerAlertRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { router.get( diff --git a/x-pack/plugins/endpoint/server/routes/alerts/lib/index.ts b/x-pack/plugins/endpoint/server/routes/alerts/lib/index.ts index 74db24c85eab5e..7bc1c0c306ae2b 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/lib/index.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/lib/index.ts @@ -7,7 +7,8 @@ import { SearchResponse } from 'elasticsearch'; import { IScopedClusterClient } from 'kibana/server'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; import { esQuery } from '../../../../../../../src/plugins/data/server'; -import { AlertEvent, Direction, EndpointAppConstants } from '../../../../common/types'; +import { AlertEvent, AlertAPIOrdering } from '../../../../common/types'; +import { AlertConstants } from '../../../../common/alert_constants'; import { AlertSearchQuery, AlertSearchRequest, @@ -18,7 +19,7 @@ import { export { Pagination } from './pagination'; -function reverseSortDirection(order: Direction): Direction { +function reverseSortDirection(order: AlertAPIOrdering): AlertAPIOrdering { if (order === 'asc') { return 'desc'; } @@ -100,13 +101,13 @@ const buildAlertSearchQuery = async ( query: AlertSearchQuery, indexPattern: string ): Promise => { - let totalHitsMin: number = EndpointAppConstants.DEFAULT_TOTAL_HITS; + let totalHitsMin: number = AlertConstants.DEFAULT_TOTAL_HITS; // Calculate minimum total hits set to indicate there's a next page if (query.fromIndex) { totalHitsMin = Math.max( query.fromIndex + query.pageSize * 2, - EndpointAppConstants.DEFAULT_TOTAL_HITS + AlertConstants.DEFAULT_TOTAL_HITS ); } diff --git a/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts b/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts index 95c8e4662cfcea..92bd07a813d264 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts @@ -13,10 +13,10 @@ import { AlertData, AlertResultList, AlertHits, - EndpointAppConstants, ESTotal, AlertingIndexGetQueryResult, } from '../../../../../common/types'; +import { AlertConstants } from '../../../../../common/alert_constants'; import { EndpointAppContext } from '../../../../types'; import { AlertSearchQuery } from '../../types'; import { AlertListPagination } from './pagination'; @@ -28,8 +28,8 @@ export const getRequestData = async ( const config = await endpointAppContext.config(); const reqData: AlertSearchQuery = { // Defaults not enforced by schema - pageSize: request.query.page_size || EndpointAppConstants.ALERT_LIST_DEFAULT_PAGE_SIZE, - sort: request.query.sort || EndpointAppConstants.ALERT_LIST_DEFAULT_SORT, + pageSize: request.query.page_size || AlertConstants.ALERT_LIST_DEFAULT_PAGE_SIZE, + sort: request.query.sort || AlertConstants.ALERT_LIST_DEFAULT_SORT, order: request.query.order || 'desc', dateRange: ((request.query.date_range !== undefined ? decode(request.query.date_range) @@ -67,7 +67,7 @@ export const getRequestData = async ( reqData.searchBefore[0] === '' && reqData.emptyStringIsUndefined ) { - reqData.searchBefore[0] = EndpointAppConstants.MAX_LONG_INT; + reqData.searchBefore[0] = AlertConstants.MAX_LONG_INT; } if ( @@ -75,7 +75,7 @@ export const getRequestData = async ( reqData.searchAfter[0] === '' && reqData.emptyStringIsUndefined ) { - reqData.searchAfter[0] = EndpointAppConstants.MAX_LONG_INT; + reqData.searchAfter[0] = AlertConstants.MAX_LONG_INT; } return reqData; diff --git a/x-pack/plugins/endpoint/server/routes/alerts/types.ts b/x-pack/plugins/endpoint/server/routes/alerts/types.ts index cf4eac5d25f800..5aefc35b5758f1 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/types.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/types.ts @@ -5,14 +5,14 @@ */ import { Query, Filter, TimeRange } from '../../../../../../src/plugins/data/server'; import { JsonObject } from '../../../../../../src/plugins/kibana_utils/public'; -import { Direction } from '../../../common/types'; +import { AlertAPIOrdering } from '../../../common/types'; /** * Sort parameters for alerts in ES. */ export interface AlertSortParam { [key: string]: { - order: Direction; + order: AlertAPIOrdering; missing?: UndefinedResultPosition; }; } @@ -38,7 +38,7 @@ export interface AlertSearchQuery { filters: Filter[]; dateRange?: TimeRange; sort: string; - order: Direction; + order: AlertAPIOrdering; searchAfter?: SearchCursor; searchBefore?: SearchCursor; emptyStringIsUndefined?: boolean; @@ -83,7 +83,7 @@ export interface AlertListRequestQuery { filters?: string; date_range: string; sort: string; - order: Direction; + order: AlertAPIOrdering; after?: SearchCursor; before?: SearchCursor; empty_string_is_undefined?: boolean; diff --git a/x-pack/plugins/endpoint/server/routes/index_pattern.ts b/x-pack/plugins/endpoint/server/routes/index_pattern.ts index 79083f5f05e149..7e78caaf178e46 100644 --- a/x-pack/plugins/endpoint/server/routes/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/routes/index_pattern.ts @@ -6,7 +6,8 @@ import { IRouter, Logger, RequestHandler } from 'kibana/server'; import { EndpointAppContext } from '../types'; -import { IndexPatternGetParamsResult, EndpointAppConstants } from '../../common/types'; +import { IndexPatternGetParamsResult } from '../../common/types'; +import { AlertConstants } from '../../common/alert_constants'; import { indexPatternGetParamsSchema } from '../../common/schema/index_pattern'; function handleIndexPattern( @@ -33,7 +34,7 @@ export function registerIndexPatternRoute(router: IRouter, endpointAppContext: E router.get( { - path: `${EndpointAppConstants.INDEX_PATTERN_ROUTE}/{datasetPath}`, + path: `${AlertConstants.INDEX_PATTERN_ROUTE}/{datasetPath}`, validate: { params: indexPatternGetParamsSchema }, options: { authRequired: true }, }, diff --git a/x-pack/plugins/endpoint/server/routes/resolver.ts b/x-pack/plugins/endpoint/server/routes/resolver.ts index a96d431225b150..3599acacb4f59c 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver.ts @@ -6,20 +6,27 @@ import { IRouter } from 'kibana/server'; import { EndpointAppContext } from '../types'; -import { handleRelatedEvents, validateRelatedEvents } from './resolver/related_events'; -import { handleChildren, validateChildren } from './resolver/children'; -import { handleLifecycle, validateLifecycle } from './resolver/lifecycle'; +import { + validateTree, + validateEvents, + validateChildren, + validateAncestry, +} from '../../common/schema/resolver'; +import { handleEvents } from './resolver/events'; +import { handleChildren } from './resolver/children'; +import { handleAncestry } from './resolver/ancestry'; +import { handleTree } from './resolver/tree'; export function registerResolverRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { const log = endpointAppContext.logFactory.get('resolver'); router.get( { - path: '/api/endpoint/resolver/{id}/related', - validate: validateRelatedEvents, + path: '/api/endpoint/resolver/{id}/events', + validate: validateEvents, options: { authRequired: true }, }, - handleRelatedEvents(log, endpointAppContext) + handleEvents(log, endpointAppContext) ); router.get( @@ -31,12 +38,21 @@ export function registerResolverRoutes(router: IRouter, endpointAppContext: Endp handleChildren(log, endpointAppContext) ); + router.get( + { + path: '/api/endpoint/resolver/{id}/ancestry', + validate: validateAncestry, + options: { authRequired: true }, + }, + handleAncestry(log, endpointAppContext) + ); + router.get( { path: '/api/endpoint/resolver/{id}', - validate: validateLifecycle, + validate: validateTree, options: { authRequired: true }, }, - handleLifecycle(log, endpointAppContext) + handleTree(log, endpointAppContext) ); } diff --git a/x-pack/plugins/endpoint/server/routes/resolver/ancestry.ts b/x-pack/plugins/endpoint/server/routes/resolver/ancestry.ts new file mode 100644 index 00000000000000..6648dc5b9e4938 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/ancestry.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 { RequestHandler, Logger } from 'kibana/server'; +import { TypeOf } from '@kbn/config-schema'; +import { validateAncestry } from '../../../common/schema/resolver'; +import { Fetcher } from './utils/fetch'; +import { EndpointAppContext } from '../../types'; + +export function handleAncestry( + log: Logger, + endpointAppContext: EndpointAppContext +): RequestHandler, TypeOf> { + return async (context, req, res) => { + const { + params: { id }, + query: { ancestors, legacyEndpointID: endpointID }, + } = req; + try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); + + const client = context.core.elasticsearch.dataClient; + const indexPattern = await indexRetriever.getEventIndexPattern(context); + + const fetcher = new Fetcher(client, id, indexPattern, endpointID); + const tree = await fetcher.ancestors(ancestors + 1); + + return res.ok({ + body: tree.render(), + }); + } catch (err) { + log.warn(err); + return res.internalError({ body: err }); + } + }; +} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/children.ts b/x-pack/plugins/endpoint/server/routes/resolver/children.ts index c3b19b6c912b61..bb18b29a4b9479 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/children.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/children.ts @@ -4,87 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; -import { schema } from '@kbn/config-schema'; import { RequestHandler, Logger } from 'kibana/server'; -import { extractEntityID } from './utils/normalize'; -import { getPaginationParams } from './utils/pagination'; -import { LifecycleQuery } from './queries/lifecycle'; -import { ChildrenQuery } from './queries/children'; +import { TypeOf } from '@kbn/config-schema'; +import { validateChildren } from '../../../common/schema/resolver'; +import { Fetcher } from './utils/fetch'; import { EndpointAppContext } from '../../types'; -interface ChildrenQueryParams { - after?: string; - limit: number; - /** - * legacyEndpointID is optional because there are two different types of identifiers: - * - * Legacy - * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if - * it's looking at a legacy event and use those fields when making requests to the backend. The - * request would be /resolver/{id}?legacyEndpointID=and the {id} would be the unique_pid. - * - * Elastic Endpoint - * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a - * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id} - * and the {id} would be entityID stored in the event's process.entity_id field. - */ - legacyEndpointID?: string; -} - -interface ChildrenPathParams { - id: string; -} - -export const validateChildren = { - params: schema.object({ id: schema.string() }), - query: schema.object({ - after: schema.maybe(schema.string()), - limit: schema.number({ defaultValue: 10, min: 1, max: 100 }), - legacyEndpointID: schema.maybe(schema.string()), - }), -}; - export function handleChildren( log: Logger, endpointAppContext: EndpointAppContext -): RequestHandler { +): RequestHandler, TypeOf> { return async (context, req, res) => { const { params: { id }, - query: { limit, after, legacyEndpointID }, + query: { children, generations, afterChild, legacyEndpointID: endpointID }, } = req; try { const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); - const pagination = getPaginationParams(limit, after); const indexPattern = await indexRetriever.getEventIndexPattern(context); const client = context.core.elasticsearch.dataClient; - const childrenQuery = new ChildrenQuery(indexPattern, legacyEndpointID, pagination); - const lifecycleQuery = new LifecycleQuery(indexPattern, legacyEndpointID); - - // Retrieve the related child process events for a given process - const { total, results: events, nextCursor } = await childrenQuery.search(client, id); - const childIDs = events.map(extractEntityID); - - // Retrieve the lifecycle events for the child processes (e.g. started, terminated etc) - // this needs to fire after the above since we don't yet have the entity ids until we - // run the first query - const { results: lifecycleEvents } = await lifecycleQuery.search(client, ...childIDs); - // group all of the lifecycle events by the child process id - const lifecycleGroups = Object.values(_.groupBy(lifecycleEvents, extractEntityID)); - const children = lifecycleGroups.map(group => ({ lifecycle: group })); + const fetcher = new Fetcher(client, id, indexPattern, endpointID); + const tree = await fetcher.children(children, generations, afterChild); return res.ok({ - body: { - children, - pagination: { - total, - next: nextCursor, - limit, - }, - }, + body: tree.render(), }); } catch (err) { log.warn(err); diff --git a/x-pack/plugins/endpoint/server/routes/resolver/events.ts b/x-pack/plugins/endpoint/server/routes/resolver/events.ts new file mode 100644 index 00000000000000..a70a6e8d097d08 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/events.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 { TypeOf } from '@kbn/config-schema'; +import { RequestHandler, Logger } from 'kibana/server'; +import { validateEvents } from '../../../common/schema/resolver'; +import { Fetcher } from './utils/fetch'; +import { EndpointAppContext } from '../../types'; + +export function handleEvents( + log: Logger, + endpointAppContext: EndpointAppContext +): RequestHandler, TypeOf> { + return async (context, req, res) => { + const { + params: { id }, + query: { events, afterEvent, legacyEndpointID: endpointID }, + } = req; + try { + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); + const client = context.core.elasticsearch.dataClient; + const indexPattern = await indexRetriever.getEventIndexPattern(context); + + const fetcher = new Fetcher(client, id, indexPattern, endpointID); + const tree = await fetcher.events(events, afterEvent); + + return res.ok({ + body: tree.render(), + }); + } catch (err) { + log.warn(err); + return res.internalError({ body: err }); + } + }; +} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts b/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.ts deleted file mode 100644 index 91a4c5d49bc54d..00000000000000 --- a/x-pack/plugins/endpoint/server/routes/resolver/lifecycle.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 { schema } from '@kbn/config-schema'; -import { RequestHandler, Logger } from 'kibana/server'; -import { extractParentEntityID } from './utils/normalize'; -import { LifecycleQuery } from './queries/lifecycle'; -import { ResolverEvent } from '../../../common/types'; -import { EndpointAppContext } from '../../types'; - -interface LifecycleQueryParams { - ancestors: number; - /** - * legacyEndpointID is optional because there are two different types of identifiers: - * - * Legacy - * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if - * it's looking at a legacy event and use those fields when making requests to the backend. The - * request would be /resolver/{id}?legacyEndpointID=and the {id} would be the unique_pid. - * - * Elastic Endpoint - * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a - * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id} - * and the {id} would be entityID stored in the event's process.entity_id field. - */ - legacyEndpointID?: string; -} - -interface LifecyclePathParams { - id: string; -} - -export const validateLifecycle = { - params: schema.object({ id: schema.string() }), - query: schema.object({ - ancestors: schema.number({ defaultValue: 0, min: 0, max: 10 }), - legacyEndpointID: schema.maybe(schema.string()), - }), -}; - -function getParentEntityID(results: ResolverEvent[]) { - return results.length === 0 ? undefined : extractParentEntityID(results[0]); -} - -export function handleLifecycle( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler { - return async (context, req, res) => { - const { - params: { id }, - query: { ancestors, legacyEndpointID }, - } = req; - try { - const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); - const ancestorLifecycles = []; - const client = context.core.elasticsearch.dataClient; - const indexPattern = await indexRetriever.getEventIndexPattern(context); - const lifecycleQuery = new LifecycleQuery(indexPattern, legacyEndpointID); - const { results: processLifecycle } = await lifecycleQuery.search(client, id); - let nextParentID = getParentEntityID(processLifecycle); - - if (nextParentID) { - for (let i = 0; i < ancestors; i++) { - const { results: lifecycle } = await lifecycleQuery.search(client, nextParentID); - nextParentID = getParentEntityID(lifecycle); - - if (!nextParentID) { - break; - } - - ancestorLifecycles.push({ - lifecycle, - }); - } - } - - return res.ok({ - body: { - lifecycle: processLifecycle, - ancestors: ancestorLifecycles, - pagination: { - next: nextParentID || null, - ancestors, - }, - }, - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: err }); - } - }; -} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts index b049439207e508..eba4e5581c1367 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/base.ts @@ -4,10 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SearchResponse } from 'elasticsearch'; import { IScopedClusterClient } from 'kibana/server'; -import { EndpointAppConstants } from '../../../../common/types'; -import { paginate, paginatedResults, PaginationParams } from '../utils/pagination'; +import { ResolverEvent } from '../../../../common/types'; +import { + paginate, + paginatedResults, + PaginationParams, + PaginatedResults, +} from '../utils/pagination'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; +import { legacyEventIndexPattern } from './legacy_event_index_pattern'; export abstract class ResolverQuery { constructor( @@ -16,22 +23,28 @@ export abstract class ResolverQuery { private readonly pagination?: PaginationParams ) {} - protected paginateBy(field: string, query: JsonObject) { - if (!this.pagination) { - return query; - } - return paginate(this.pagination, field, query); + protected paginateBy(tiebreaker: string, aggregator: string) { + return (query: JsonObject) => { + if (!this.pagination) { + return query; + } + return paginate(this.pagination, tiebreaker, aggregator, query); + }; } build(...ids: string[]) { if (this.endpointID) { - return this.legacyQuery(this.endpointID, ids, EndpointAppConstants.LEGACY_EVENT_INDEX_NAME); + return this.legacyQuery(this.endpointID, ids, legacyEventIndexPattern); } return this.query(ids, this.indexPattern); } async search(client: IScopedClusterClient, ...ids: string[]) { - return paginatedResults(await client.callAsCurrentUser('search', this.build(...ids))); + return this.postSearch(await client.callAsCurrentUser('search', this.build(...ids))); + } + + protected postSearch(response: SearchResponse): PaginatedResults { + return paginatedResults(response); } protected abstract legacyQuery( diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts index e73053d53dee08..2a097e87c38b29 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ChildrenQuery } from './children'; -import { EndpointAppConstants } from '../../../../common/types'; +import { legacyEventIndexPattern } from './legacy_event_index_pattern'; export const fakeEventIndexPattern = 'events-endpoint-*'; @@ -12,7 +12,7 @@ describe('children events query', () => { it('generates the correct legacy queries', () => { const timestamp = new Date().getTime(); expect( - new ChildrenQuery(EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, 'awesome-id', { + new ChildrenQuery(legacyEventIndexPattern, 'awesome-id', { size: 1, timestamp, eventID: 'foo', @@ -32,15 +32,28 @@ describe('children events query', () => { term: { 'event.category': 'process' }, }, { - term: { 'event.type': 'process_start' }, + term: { 'event.kind': 'event' }, + }, + { + bool: { + should: [ + { + term: { 'event.type': 'process_start' }, + }, + { + term: { 'event.action': 'fork_event' }, + }, + ], + }, }, ], }, }, aggs: { - total: { - value_count: { - field: 'endgame.serial_event_id', + totals: { + terms: { + field: 'endgame.unique_ppid', + size: 1, }, }, }, @@ -48,7 +61,7 @@ describe('children events query', () => { size: 1, sort: [{ '@timestamp': 'asc' }, { 'endgame.serial_event_id': 'asc' }], }, - index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, + index: legacyEventIndexPattern, }); }); @@ -67,20 +80,14 @@ describe('children events query', () => { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.parent.entity_id': ['baz'] }, - }, - { - terms: { 'process.parent.entity_id': ['baz'] }, - }, - ], - }, + terms: { 'process.parent.entity_id': ['baz'] }, }, { term: { 'event.category': 'process' }, }, + { + term: { 'event.kind': 'event' }, + }, { term: { 'event.type': 'start' }, }, @@ -88,9 +95,10 @@ describe('children events query', () => { }, }, aggs: { - total: { - value_count: { - field: 'event.id', + totals: { + terms: { + field: 'process.parent.entity_id', + size: 1, }, }, }, diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts index 6d084a0cf20e5e..690c926d7e6d6d 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/children.ts @@ -7,8 +7,9 @@ import { ResolverQuery } from './base'; export class ChildrenQuery extends ResolverQuery { protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string) { + const paginator = this.paginateBy('endgame.serial_event_id', 'endgame.unique_ppid'); return { - body: this.paginateBy('endgame.serial_event_id', { + body: paginator({ query: { bool: { filter: [ @@ -22,11 +23,19 @@ export class ChildrenQuery extends ResolverQuery { term: { 'event.category': 'process' }, }, { - // Corner case, we could only have a process_running or process_terminated - // so to solve this we'll probably want to either search for all of them and only return one if that's - // possible in elastic search or in memory pull out a single event to return - // https://github.com/elastic/endpoint-app-team/issues/168 - term: { 'event.type': 'process_start' }, + term: { 'event.kind': 'event' }, + }, + { + bool: { + should: [ + { + term: { 'event.type': 'process_start' }, + }, + { + term: { 'event.action': 'fork_event' }, + }, + ], + }, }, ], }, @@ -37,31 +46,22 @@ export class ChildrenQuery extends ResolverQuery { } protected query(entityIDs: string[], index: string) { + const paginator = this.paginateBy('event.id', 'process.parent.entity_id'); return { - body: this.paginateBy('event.id', { + body: paginator({ query: { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.parent.entity_id': entityIDs }, - }, - { - terms: { 'process.parent.entity_id': entityIDs }, - }, - ], - }, + terms: { 'process.parent.entity_id': entityIDs }, }, { term: { 'event.category': 'process' }, }, { - // Corner case, we could only have a process_running or process_terminated - // so to solve this we'll probably want to either search for all of them and only return one if that's - // possible in elastic search or in memory pull out a single event to return - // https://github.com/elastic/endpoint-app-team/issues/168 + term: { 'event.kind': 'event' }, + }, + { term: { 'event.type': 'start' }, }, ], diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/events.test.ts similarity index 71% rename from x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts rename to x-pack/plugins/endpoint/server/routes/resolver/queries/events.test.ts index 5caef935ce6214..78e5ee9226581d 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.test.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/events.test.ts @@ -3,15 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { RelatedEventsQuery } from './related_events'; -import { EndpointAppConstants } from '../../../../common/types'; +import { EventsQuery } from './events'; import { fakeEventIndexPattern } from './children.test'; +import { legacyEventIndexPattern } from './legacy_event_index_pattern'; describe('related events query', () => { it('generates the correct legacy queries', () => { const timestamp = new Date().getTime(); expect( - new RelatedEventsQuery(EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, 'awesome-id', { + new EventsQuery(legacyEventIndexPattern, 'awesome-id', { size: 1, timestamp, eventID: 'foo', @@ -27,6 +27,9 @@ describe('related events query', () => { { term: { 'agent.id': 'awesome-id' }, }, + { + term: { 'event.kind': 'event' }, + }, { bool: { must_not: { @@ -38,9 +41,10 @@ describe('related events query', () => { }, }, aggs: { - total: { - value_count: { - field: 'endgame.serial_event_id', + totals: { + terms: { + field: 'endgame.unique_pid', + size: 1, }, }, }, @@ -48,7 +52,7 @@ describe('related events query', () => { size: 1, sort: [{ '@timestamp': 'asc' }, { 'endgame.serial_event_id': 'asc' }], }, - index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, + index: legacyEventIndexPattern, }); }); @@ -56,7 +60,7 @@ describe('related events query', () => { const timestamp = new Date().getTime(); expect( - new RelatedEventsQuery(fakeEventIndexPattern, undefined, { + new EventsQuery(fakeEventIndexPattern, undefined, { size: 1, timestamp, eventID: 'bar', @@ -67,16 +71,10 @@ describe('related events query', () => { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.entity_id': ['baz'] }, - }, - { - terms: { 'process.entity_id': ['baz'] }, - }, - ], - }, + terms: { 'process.entity_id': ['baz'] }, + }, + { + term: { 'event.kind': 'event' }, }, { bool: { @@ -89,9 +87,10 @@ describe('related events query', () => { }, }, aggs: { - total: { - value_count: { - field: 'event.id', + totals: { + terms: { + field: 'process.entity_id', + size: 1, }, }, }, diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/events.ts similarity index 74% rename from x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts rename to x-pack/plugins/endpoint/server/routes/resolver/queries/events.ts index cc5afe8face8d5..b622cb8a211111 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/related_events.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/events.ts @@ -6,10 +6,11 @@ import { ResolverQuery } from './base'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; -export class RelatedEventsQuery extends ResolverQuery { +export class EventsQuery extends ResolverQuery { protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string): JsonObject { + const paginator = this.paginateBy('endgame.serial_event_id', 'endgame.unique_pid'); return { - body: this.paginateBy('endgame.serial_event_id', { + body: paginator({ query: { bool: { filter: [ @@ -19,6 +20,9 @@ export class RelatedEventsQuery extends ResolverQuery { { term: { 'agent.id': endpointID }, }, + { + term: { 'event.kind': 'event' }, + }, { bool: { must_not: { @@ -35,22 +39,17 @@ export class RelatedEventsQuery extends ResolverQuery { } protected query(entityIDs: string[], index: string): JsonObject { + const paginator = this.paginateBy('event.id', 'process.entity_id'); return { - body: this.paginateBy('event.id', { + body: paginator({ query: { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.entity_id': entityIDs }, - }, - { - terms: { 'process.entity_id': entityIDs }, - }, - ], - }, + terms: { 'process.entity_id': entityIDs }, + }, + { + term: { 'event.kind': 'event' }, }, { bool: { diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/legacy_event_index_pattern.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/legacy_event_index_pattern.ts new file mode 100644 index 00000000000000..01e818c89b3efc --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/legacy_event_index_pattern.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. + */ + +/** + * Legacy events are stored in indices with endgame-* prefix + */ +export const legacyEventIndexPattern = 'endgame-*'; diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts index 8a3955706b2781..296135af83b72e 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.test.ts @@ -3,15 +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 { EndpointAppConstants } from '../../../../common/types'; + import { LifecycleQuery } from './lifecycle'; import { fakeEventIndexPattern } from './children.test'; +import { legacyEventIndexPattern } from './legacy_event_index_pattern'; describe('lifecycle query', () => { it('generates the correct legacy queries', () => { - expect( - new LifecycleQuery(EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, 'awesome-id').build('5') - ).toStrictEqual({ + expect(new LifecycleQuery(legacyEventIndexPattern, 'awesome-id').build('5')).toStrictEqual({ body: { query: { bool: { @@ -22,15 +21,19 @@ describe('lifecycle query', () => { { term: { 'agent.id': 'awesome-id' }, }, + { + term: { 'event.kind': 'event' }, + }, { term: { 'event.category': 'process' }, }, ], }, }, + size: 10000, sort: [{ '@timestamp': 'asc' }], }, - index: EndpointAppConstants.LEGACY_EVENT_INDEX_NAME, + index: legacyEventIndexPattern, }); }); @@ -41,16 +44,10 @@ describe('lifecycle query', () => { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.entity_id': ['baz'] }, - }, - { - terms: { 'process.entity_id': ['baz'] }, - }, - ], - }, + terms: { 'process.entity_id': ['baz'] }, + }, + { + term: { 'event.kind': 'event' }, }, { term: { 'event.category': 'process' }, @@ -58,6 +55,7 @@ describe('lifecycle query', () => { ], }, }, + size: 10000, sort: [{ '@timestamp': 'asc' }], }, index: fakeEventIndexPattern, diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts index 290c601e0e9d8c..e775b0cf9b6d2c 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/lifecycle.ts @@ -6,7 +6,6 @@ import { ResolverQuery } from './base'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; -// consider limiting the response size to a reasonable value in case we have a bunch of lifecycle events export class LifecycleQuery extends ResolverQuery { protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string): JsonObject { return { @@ -20,12 +19,16 @@ export class LifecycleQuery extends ResolverQuery { { term: { 'agent.id': endpointID }, }, + { + term: { 'event.kind': 'event' }, + }, { term: { 'event.category': 'process' }, }, ], }, }, + size: 10000, sort: [{ '@timestamp': 'asc' }], }, index, @@ -39,16 +42,10 @@ export class LifecycleQuery extends ResolverQuery { bool: { filter: [ { - bool: { - should: [ - { - terms: { 'endpoint.process.entity_id': entityIDs }, - }, - { - terms: { 'process.entity_id': entityIDs }, - }, - ], - }, + terms: { 'process.entity_id': entityIDs }, + }, + { + term: { 'event.kind': 'event' }, }, { term: { 'event.category': 'process' }, @@ -56,6 +53,7 @@ export class LifecycleQuery extends ResolverQuery { ], }, }, + size: 10000, sort: [{ '@timestamp': 'asc' }], }, index, diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.test.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.test.ts new file mode 100644 index 00000000000000..17a158aec7cf56 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.test.ts @@ -0,0 +1,188 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { legacyEventIndexPattern } from './legacy_event_index_pattern'; +import { StatsQuery } from './stats'; +import { fakeEventIndexPattern } from './children.test'; + +describe('stats query', () => { + it('generates the correct legacy queries', () => { + expect(new StatsQuery(legacyEventIndexPattern, 'awesome-id').build('5')).toStrictEqual({ + body: { + size: 0, + query: { + bool: { + filter: [ + { + term: { + 'agent.id': 'awesome-id', + }, + }, + { + bool: { + should: [ + { + bool: { + filter: [ + { + term: { + 'event.kind': 'event', + }, + }, + { + terms: { + 'endgame.unique_pid': ['5'], + }, + }, + { + bool: { + must_not: { + term: { + 'event.category': 'process', + }, + }, + }, + }, + ], + }, + }, + { + bool: { + filter: [ + { + term: { + 'event.kind': 'alert', + }, + }, + { + terms: { + 'endgame.data.alert_details.acting_process.unique_pid': ['5'], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + alerts: { + filter: { + term: { + 'event.kind': 'alert', + }, + }, + aggs: { + ids: { + terms: { + field: 'endgame.data.alert_details.acting_process.unique_pid', + }, + }, + }, + }, + events: { + filter: { + term: { + 'event.kind': 'event', + }, + }, + aggs: { + ids: { + terms: { + field: 'endgame.unique_pid', + }, + }, + }, + }, + }, + }, + index: legacyEventIndexPattern, + }); + }); + + it('generates the correct non-legacy queries', () => { + expect(new StatsQuery(fakeEventIndexPattern).build('baz')).toStrictEqual({ + body: { + size: 0, + query: { + bool: { + filter: [ + { + terms: { + 'process.entity_id': ['baz'], + }, + }, + { + bool: { + should: [ + { + bool: { + filter: [ + { + term: { + 'event.kind': 'event', + }, + }, + { + bool: { + must_not: { + term: { + 'event.category': 'process', + }, + }, + }, + }, + ], + }, + }, + { + term: { + 'event.kind': 'alert', + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + alerts: { + filter: { + term: { + 'event.kind': 'alert', + }, + }, + aggs: { + ids: { + terms: { + field: 'process.entity_id', + }, + }, + }, + }, + events: { + filter: { + term: { + 'event.kind': 'event', + }, + }, + aggs: { + ids: { + terms: { + field: 'process.entity_id', + }, + }, + }, + }, + }, + }, + index: fakeEventIndexPattern, + }); + }); +}); diff --git a/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.ts b/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.ts new file mode 100644 index 00000000000000..7db3ab2b0cb1fe --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/queries/stats.ts @@ -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 { SearchResponse } from 'elasticsearch'; +import { ResolverQuery } from './base'; +import { ResolverEvent } from '../../../../common/types'; +import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; +import { PaginatedResults } from '../utils/pagination'; + +export class StatsQuery extends ResolverQuery { + protected postSearch(response: SearchResponse): PaginatedResults { + const alerts = response.aggregations.alerts.ids.buckets.reduce( + (cummulative: any, bucket: any) => ({ ...cummulative, [bucket.key]: bucket.doc_count }), + {} + ); + const events = response.aggregations.events.ids.buckets.reduce( + (cummulative: any, bucket: any) => ({ ...cummulative, [bucket.key]: bucket.doc_count }), + {} + ); + return { + totals: {}, + results: [], + extras: { + alerts, + events, + }, + }; + } + + protected legacyQuery(endpointID: string, uniquePIDs: string[], index: string): JsonObject { + return { + body: { + size: 0, + query: { + bool: { + filter: [ + { + term: { 'agent.id': endpointID }, + }, + { + bool: { + should: [ + { + bool: { + filter: [ + { term: { 'event.kind': 'event' } }, + { terms: { 'endgame.unique_pid': uniquePIDs } }, + { + bool: { + must_not: { + term: { 'event.category': 'process' }, + }, + }, + }, + ], + }, + }, + { + bool: { + filter: [ + { term: { 'event.kind': 'alert' } }, + { + terms: { + 'endgame.data.alert_details.acting_process.unique_pid': uniquePIDs, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + aggs: { + alerts: { + filter: { term: { 'event.kind': 'alert' } }, + aggs: { + ids: { terms: { field: 'endgame.data.alert_details.acting_process.unique_pid' } }, + }, + }, + events: { + filter: { term: { 'event.kind': 'event' } }, + aggs: { + ids: { terms: { field: 'endgame.unique_pid' } }, + }, + }, + }, + }, + index, + }; + } + + protected query(entityIDs: string[], index: string): JsonObject { + return { + body: { + size: 0, + query: { + bool: { + filter: [ + { terms: { 'process.entity_id': entityIDs } }, + { + bool: { + should: [ + { + bool: { + filter: [ + { term: { 'event.kind': 'event' } }, + { + bool: { + must_not: { + term: { 'event.category': 'process' }, + }, + }, + }, + ], + }, + }, + { term: { 'event.kind': 'alert' } }, + ], + }, + }, + ], + }, + }, + aggs: { + alerts: { + filter: { term: { 'event.kind': 'alert' } }, + aggs: { + ids: { terms: { field: 'process.entity_id' } }, + }, + }, + events: { + filter: { term: { 'event.kind': 'event' } }, + aggs: { + ids: { terms: { field: 'process.entity_id' } }, + }, + }, + }, + }, + index, + }; + } +} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts b/x-pack/plugins/endpoint/server/routes/resolver/related_events.ts deleted file mode 100644 index 83e111a1e62e66..00000000000000 --- a/x-pack/plugins/endpoint/server/routes/resolver/related_events.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 { schema } from '@kbn/config-schema'; -import { RequestHandler, Logger } from 'kibana/server'; -import { getPaginationParams } from './utils/pagination'; -import { RelatedEventsQuery } from './queries/related_events'; -import { EndpointAppContext } from '../../types'; - -interface RelatedEventsQueryParams { - after?: string; - limit: number; - /** - * legacyEndpointID is optional because there are two different types of identifiers: - * - * Legacy - * A legacy Entity ID is made up of the agent.id and unique_pid fields. The client will need to identify if - * it's looking at a legacy event and use those fields when making requests to the backend. The - * request would be /resolver/{id}?legacyEndpointID=and the {id} would be the unique_pid. - * - * Elastic Endpoint - * When interacting with the new form of data the client doesn't need the legacyEndpointID because it's already a - * part of the entityID in the new type of event. So for the same request the client would just hit resolver/{id} - * and the {id} would be entityID stored in the event's process.entity_id field. - */ - legacyEndpointID?: string; -} - -interface RelatedEventsPathParams { - id: string; -} - -export const validateRelatedEvents = { - params: schema.object({ id: schema.string() }), - query: schema.object({ - after: schema.maybe(schema.string()), - limit: schema.number({ defaultValue: 100, min: 1, max: 1000 }), - legacyEndpointID: schema.maybe(schema.string()), - }), -}; - -export function handleRelatedEvents( - log: Logger, - endpointAppContext: EndpointAppContext -): RequestHandler { - return async (context, req, res) => { - const { - params: { id }, - query: { limit, after, legacyEndpointID }, - } = req; - try { - const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); - const pagination = getPaginationParams(limit, after); - - const client = context.core.elasticsearch.dataClient; - const indexPattern = await indexRetriever.getEventIndexPattern(context); - // Retrieve the related non-process events for a given process - const relatedEventsQuery = new RelatedEventsQuery(indexPattern, legacyEndpointID, pagination); - const relatedEvents = await relatedEventsQuery.search(client, id); - - const { total, results: events, nextCursor } = relatedEvents; - - return res.ok({ - body: { - events, - pagination: { total, next: nextCursor, limit }, - }, - }); - } catch (err) { - log.warn(err); - return res.internalError({ body: err }); - } - }; -} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/tree.ts b/x-pack/plugins/endpoint/server/routes/resolver/tree.ts new file mode 100644 index 00000000000000..25f15586341d5e --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/tree.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 { RequestHandler, Logger } from 'kibana/server'; +import { TypeOf } from '@kbn/config-schema'; +import { validateTree } from '../../../common/schema/resolver'; +import { Fetcher } from './utils/fetch'; +import { Tree } from './utils/tree'; +import { EndpointAppContext } from '../../types'; + +export function handleTree( + log: Logger, + endpointAppContext: EndpointAppContext +): RequestHandler, TypeOf> { + return async (context, req, res) => { + const { + params: { id }, + query: { + children, + generations, + ancestors, + events, + afterEvent, + afterChild, + legacyEndpointID: endpointID, + }, + } = req; + try { + const client = context.core.elasticsearch.dataClient; + const indexRetriever = endpointAppContext.service.getIndexPatternRetriever(); + const indexPattern = await indexRetriever.getEventIndexPattern(context); + + const fetcher = new Fetcher(client, id, indexPattern, endpointID); + const tree = await Tree.merge( + fetcher.children(children, generations, afterChild), + fetcher.ancestors(ancestors + 1), + fetcher.events(events, afterEvent) + ); + + const enrichedTree = await fetcher.stats(tree); + + return res.ok({ + body: enrichedTree.render(), + }); + } catch (err) { + log.warn(err); + return res.internalError({ body: 'Error retrieving tree.' }); + } + }; +} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/fetch.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/fetch.ts new file mode 100644 index 00000000000000..7315b4ee6c618f --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/fetch.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IScopedClusterClient } from 'kibana/server'; +import { entityId, parentEntityId } from '../../../../common/models/event'; +import { getPaginationParams } from './pagination'; +import { Tree } from './tree'; +import { LifecycleQuery } from '../queries/lifecycle'; +import { ChildrenQuery } from '../queries/children'; +import { EventsQuery } from '../queries/events'; +import { StatsQuery } from '../queries/stats'; + +export class Fetcher { + constructor( + private readonly client: IScopedClusterClient, + private readonly id: string, + private readonly indexPattern: string, + private readonly endpointID?: string + ) {} + + public async ancestors(limit: number): Promise { + const tree = new Tree(this.id); + await this.doAncestors(tree, this.id, this.id, limit); + return tree; + } + + public async children(limit: number, generations: number, after?: string): Promise { + const tree = new Tree(this.id); + await this.doChildren(tree, [this.id], limit, generations, after); + return tree; + } + + public async events(limit: number, after?: string): Promise { + const tree = new Tree(this.id); + await this.doEvents(tree, limit, after); + return tree; + } + + public async stats(tree: Tree): Promise { + await this.doStats(tree); + return tree; + } + + private async doAncestors(tree: Tree, curNode: string, previousNode: string, levels: number) { + if (levels === 0) { + tree.setNextAncestor(curNode); + return; + } + + const query = new LifecycleQuery(this.indexPattern, this.endpointID); + const { results } = await query.search(this.client, curNode); + + if (results.length === 0) { + tree.setNextAncestor(null); + return; + } + tree.addAncestor(previousNode, ...results); + + const next = parentEntityId(results[0]); + if (next !== undefined) { + await this.doAncestors(tree, next, curNode, levels - 1); + } + } + + private async doEvents(tree: Tree, limit: number, after?: string) { + const query = new EventsQuery( + this.indexPattern, + this.endpointID, + getPaginationParams(limit, after) + ); + + const { totals, results } = await query.search(this.client, this.id); + tree.addEvent(...results); + tree.paginateEvents(totals, results); + if (results.length === 0) tree.setNextEvent(null); + } + + private async doChildren( + tree: Tree, + ids: string[], + limit: number, + levels: number, + after?: string + ) { + if (levels === 0 || ids.length === 0) return; + + const childrenQuery = new ChildrenQuery( + this.indexPattern, + this.endpointID, + getPaginationParams(limit, after) + ); + const lifecycleQuery = new LifecycleQuery(this.indexPattern, this.endpointID); + + const { totals, results } = await childrenQuery.search(this.client, ...ids); + if (results.length === 0) { + tree.markLeafNode(...ids); + return; + } + + const childIDs = results.map(entityId); + const children = (await lifecycleQuery.search(this.client, ...childIDs)).results; + + tree.addChild(...children); + tree.paginateChildren(totals, results); + tree.markLeafNode(...childIDs); + + await this.doChildren(tree, childIDs, limit * limit, levels - 1); + } + + private async doStats(tree: Tree) { + const statsQuery = new StatsQuery(this.indexPattern, this.endpointID); + const ids = tree.ids(); + const { extras } = await statsQuery.search(this.client, ...ids); + const alerts = extras?.alerts || {}; + const events = extras?.events || {}; + ids.forEach(id => { + tree.addStats(id, { totalAlerts: alerts[id] || 0, totalEvents: events[id] || 0 }); + }); + } +} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.ts deleted file mode 100644 index 6d5ac8efdc1da5..00000000000000 --- a/x-pack/plugins/endpoint/server/routes/resolver/utils/normalize.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 { ResolverEvent } from '../../../../common/types'; -import { isLegacyEvent } from '../../../../common/models/event'; - -export function extractEventID(event: ResolverEvent) { - if (isLegacyEvent(event)) { - return String(event.endgame.serial_event_id); - } - return event.event.id; -} - -export function extractEntityID(event: ResolverEvent) { - if (isLegacyEvent(event)) { - return String(event.endgame.unique_pid); - } - return event.process.entity_id; -} - -export function extractParentEntityID(event: ResolverEvent) { - if (isLegacyEvent(event)) { - const ppid = event.endgame.unique_ppid; - return ppid && String(ppid); // if unique_ppid is undefined return undefined - } - return event.process.parent?.entity_id; -} diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts index 5a64f3ff9ddb61..20249b81660bba 100644 --- a/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts +++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/pagination.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ResolverEvent } from '../../../../common/types'; -import { extractEventID } from './normalize'; +import { entityId } from '../../../../common/models/event'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; export interface PaginationParams { @@ -15,12 +15,19 @@ export interface PaginationParams { eventID?: string; } +export interface PaginatedResults { + totals: Record; + results: ResolverEvent[]; + // content holder for any other extra aggregation counts + extras?: Record>; +} + interface PaginationCursor { timestamp: number; eventID: string; } -function urlEncodeCursor(data: PaginationCursor) { +function urlEncodeCursor(data: PaginationCursor): string { const value = JSON.stringify(data); return Buffer.from(value, 'utf8') .toString('base64') @@ -56,10 +63,16 @@ export function getPaginationParams(limit: number, after?: string): PaginationPa return { size: limit }; } -export function paginate(pagination: PaginationParams, field: string, query: JsonObject) { +export function paginate( + pagination: PaginationParams, + tiebreaker: string, + aggregator: string, + query: JsonObject +): JsonObject { const { size, timestamp, eventID } = pagination; - query.sort = [{ '@timestamp': 'asc' }, { [field]: 'asc' }]; - query.aggs = { total: { value_count: { field } } }; + query.sort = [{ '@timestamp': 'asc' }, { [tiebreaker]: 'asc' }]; + query.aggs = query.aggs || {}; + query.aggs = Object.assign({}, query.aggs, { totals: { terms: { field: aggregator, size } } }); query.size = size; if (timestamp && eventID) { query.search_after = [timestamp, eventID] as Array; @@ -67,25 +80,28 @@ export function paginate(pagination: PaginationParams, field: string, query: Jso return query; } -export function paginatedResults( - response: SearchResponse -): { total: number; results: ResolverEvent[]; nextCursor: string | null } { - const total = response.aggregations?.total?.value || 0; - if (response.hits.hits.length === 0) { - return { total, results: [], nextCursor: null }; +export function buildPaginationCursor(total: number, results: ResolverEvent[]): string | null { + if (total > results.length && results.length > 0) { + const lastResult = results[results.length - 1]; + const cursor = { + timestamp: lastResult['@timestamp'], + eventID: entityId(lastResult), + }; + return urlEncodeCursor(cursor); } + return null; +} - const results: ResolverEvent[] = []; - for (const hit of response.hits.hits) { - results.push(hit._source); +export function paginatedResults(response: SearchResponse): PaginatedResults { + if (response.hits.hits.length === 0) { + return { totals: {}, results: [] }; } - // results will be at least 1 because of length check at the top of the function - const next = results[results.length - 1]; - const cursor = { - timestamp: next['@timestamp'], - eventID: extractEventID(next), - }; + const totals = response.aggregations?.totals?.buckets?.reduce( + (cummulative: any, bucket: any) => ({ ...cummulative, [bucket.key]: bucket.doc_count }), + {} + ); - return { total, results, nextCursor: urlEncodeCursor(cursor) }; + const results = response.hits.hits.map(hit => hit._source); + return { totals, results }; } diff --git a/x-pack/plugins/endpoint/server/routes/resolver/utils/tree.ts b/x-pack/plugins/endpoint/server/routes/resolver/utils/tree.ts new file mode 100644 index 00000000000000..5a55c23b908733 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/resolver/utils/tree.ts @@ -0,0 +1,230 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + ResolverEvent, + ResolverNode, + ResolverNodeStats, + ResolverNodePagination, +} from '../../../../common/types'; +import { entityId, parentEntityId } from '../../../../common/models/event'; +import { buildPaginationCursor } from './pagination'; + +type ExtractFunction = (event: ResolverEvent) => string | undefined; + +function createNode(id: string): ResolverNode { + return { id, children: [], pagination: {}, events: [], lifecycle: [] }; +} +/** + * This class aids in constructing a tree of process events. It works in the following way: + * + * 1. We construct a tree structure starting with the root node for the event we're requesting. + * 2. We leverage the ability to pass hashes and arrays by reference to construct a fast cache of + * process identifiers that updates the tree structure as we push values into the cache. + * + * When we query a single level of results for child process events we have a flattened, sorted result + * list that we need to add into a constructed tree. We also need to signal in an API response whether + * or not there are more child processes events that we have not yet retrieved, and, if so, for what parent + * process. So, at the end of our tree construction we have a relational layout of the events with no + * pagination information for the given parent nodes. In order to actually construct both the tree and + * insert the pagination information we basically do the following: + * + * 1. Using a terms aggregation query, we return an approximate roll-up of the number of child process + * "creation" events, this gives us an estimation of the number of associated children per parent + * 2. We feed these child process creation event "unique identifiers" (basically a process.entity_id) + * into a second query to get the current state of the process via its "lifecycle" events. + * 3. We construct the tree above with the "lifecycle" events. + * 4. Using the terms query results, we mark each non-leaf node with the number of expected children, if our + * tree has less children than expected, we create a pagination cursor to indicate "we have a truncated set + * of values". + * 5. We mark each leaf node (the last level of the tree we're constructing) with a "null" for the expected + * number of children to indicate "we have not yet attempted to get any children". + * + * Following this scheme, we use exactly 2 queries per level of children that we return--one for the pagination + * and one for the lifecycle events of the processes. The downside to this is that we need to dynamically expand + * the number of documents we can retrieve per level due to the exponential fanout of child processes, + * what this means is that noisy neighbors for a given level may hide other child process events that occur later + * temporally in the same level--so, while a heavily forking process might get shown, maybe the actually malicious + * event doesn't show up in the tree at the beginning. + */ + +export class Tree { + protected cache: Map; + protected root: ResolverNode; + protected id: string; + + constructor(id: string) { + const root = createNode(id); + this.id = id; + this.cache = new Map(); + this.root = root; + this.cache.set(id, root); + } + + public render(): ResolverNode { + return this.root; + } + + public ids(): string[] { + return [...this.cache.keys()]; + } + + public static async merge( + childrenPromise: Promise, + ancestorsPromise: Promise, + eventsPromise: Promise + ): Promise { + const [children, ancestors, events] = await Promise.all([ + childrenPromise, + ancestorsPromise, + eventsPromise, + ]); + + /* + * we only allow for merging when we have partial trees that + * represent the same root node + */ + const rootID = children.id; + if (rootID !== ancestors.id || rootID !== events.id) { + throw new Error('cannot merge trees with different roots'); + } + + Object.entries(ancestors.cache).forEach(([id, node]) => { + if (rootID !== id) { + children.cache.set(id, node); + } + }); + + children.root.lifecycle = ancestors.root.lifecycle; + children.root.ancestors = ancestors.root.ancestors; + children.root.events = events.root.events; + + Object.assign(children.root.pagination, ancestors.root.pagination, events.root.pagination); + + return children; + } + + public addEvent(...events: ResolverEvent[]): void { + events.forEach(event => { + const id = entityId(event); + + this.ensureCache(id); + const currentNode = this.cache.get(id); + if (currentNode !== undefined) { + currentNode.events.push(event); + } + }); + } + + public addAncestor(id: string, ...events: ResolverEvent[]): void { + events.forEach(event => { + const ancestorID = entityId(event); + if (this.cache.get(ancestorID) === undefined) { + const newParent = createNode(ancestorID); + this.cache.set(ancestorID, newParent); + if (!this.root.ancestors) { + this.root.ancestors = []; + } + this.root.ancestors.push(newParent); + } + const currentAncestor = this.cache.get(ancestorID); + if (currentAncestor !== undefined) { + currentAncestor.lifecycle.push(event); + } + }); + } + + public addStats(id: string, stats: ResolverNodeStats): void { + this.ensureCache(id); + const currentNode = this.cache.get(id); + if (currentNode !== undefined) { + currentNode.stats = stats; + } + } + + public setNextAncestor(next: string | null): void { + this.root.pagination.nextAncestor = next; + } + + public setNextEvent(next: string | null): void { + this.root.pagination.nextEvent = next; + } + + public setNextAlert(next: string | null): void { + this.root.pagination.nextAlert = next; + } + + public addChild(...events: ResolverEvent[]): void { + events.forEach(event => { + const id = entityId(event); + const parentID = parentEntityId(event); + + this.ensureCache(parentID); + let currentNode = this.cache.get(id); + + if (currentNode === undefined) { + currentNode = createNode(id); + this.cache.set(id, currentNode); + if (parentID !== undefined) { + const parentNode = this.cache.get(parentID); + if (parentNode !== undefined) { + parentNode.children.push(currentNode); + } + } + } + currentNode.lifecycle.push(event); + }); + } + + public markLeafNode(...ids: string[]): void { + ids.forEach(id => { + this.ensureCache(id); + const currentNode = this.cache.get(id); + if (currentNode !== undefined && !currentNode.pagination.nextChild) { + currentNode.pagination.nextChild = null; + } + }); + } + + public paginateEvents(totals: Record, events: ResolverEvent[]): void { + return this.paginate(entityId, 'nextEvent', totals, events); + } + + public paginateChildren(totals: Record, children: ResolverEvent[]): void { + return this.paginate(parentEntityId, 'nextChild', totals, children); + } + + private paginate( + grouper: ExtractFunction, + attribute: keyof ResolverNodePagination, + totals: Record, + records: ResolverEvent[] + ): void { + const grouped = _.groupBy(records, grouper); + Object.entries(totals).forEach(([id, total]) => { + if (this.cache.get(id) !== undefined) { + if (grouped[id]) { + /* + * if we have any results, attempt to build a pagination cursor, the function + * below hands back a null value if no cursor is necessary because we have + * all of the records. + */ + const currentNode = this.cache.get(id); + if (currentNode !== undefined) { + currentNode.pagination[attribute] = buildPaginationCursor(total, grouped[id]); + } + } + } + }); + } + + private ensureCache(id: string | undefined): void { + if (id === undefined || this.cache.get(id) === undefined) { + throw new Error('dangling node'); + } + } +} diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index f487e9262e50e7..0a858969c4f6af 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -86,6 +86,10 @@ }, "saved_objects": { "properties": { + "rel": { + "type": "keyword", + "ignore_above": 1024 + }, "namespace": { "type": "keyword", "ignore_above": 1024 diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 9c923fe77d0358..57fe90a8e876ed 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -65,6 +65,7 @@ export const EventSchema = schema.maybe( saved_objects: schema.maybe( schema.arrayOf( schema.object({ + rel: ecsString(), namespace: ecsString(), id: ecsString(), type: ecsString(), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 8cc2c74b60e57b..fd149d132031e1 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -24,6 +24,11 @@ exports.EcsKibanaExtensionsMappings = { saved_objects: { type: 'nested', properties: { + // relation; currently only supports "primary" or not set + rel: { + type: 'keyword', + ignore_above: 1024, + }, // relevant kibana space namespace: { type: 'keyword', @@ -58,6 +63,7 @@ exports.EcsEventLogProperties = [ 'user.name', 'kibana.server_uuid', 'kibana.alerting.instance_id', + 'kibana.saved_objects.rel', 'kibana.saved_objects.namespace', 'kibana.saved_objects.id', 'kibana.saved_objects.name', diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index 470123ada48ea4..66c16d0ddf3838 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -226,7 +226,7 @@ describe('queryEventsBySavedObject', () => { body: { from: 0, size: 10, - sort: { 'event.start': { order: 'asc' } }, + sort: { '@timestamp': { order: 'asc' } }, query: { bool: { must: [ @@ -236,6 +236,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -319,6 +326,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -340,7 +354,7 @@ describe('queryEventsBySavedObject', () => { }, { range: { - 'event.start': { + '@timestamp': { gte: start, }, }, @@ -388,6 +402,13 @@ describe('queryEventsBySavedObject', () => { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: 'primary', + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -409,14 +430,14 @@ describe('queryEventsBySavedObject', () => { }, { range: { - 'event.start': { + '@timestamp': { gte: start, }, }, }, { range: { - 'event.end': { + '@timestamp': { lte: end, }, }, diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index 6d5c6b31a637c3..c0ff87234c09db 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -7,7 +7,7 @@ import { reject, isUndefined } from 'lodash'; import { SearchResponse, Client } from 'elasticsearch'; import { Logger, ClusterClient } from '../../../../../src/core/server'; -import { IEvent } from '../types'; +import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '../types'; import { FindOptionsType } from '../event_log_client'; export type EsClusterClient = Pick; @@ -155,6 +155,13 @@ export class ClusterClientAdapter { query: { bool: { must: [ + { + term: { + 'kibana.saved_objects.rel': { + value: SAVED_OBJECT_REL_PRIMARY, + }, + }, + }, { term: { 'kibana.saved_objects.type': { @@ -176,14 +183,14 @@ export class ClusterClientAdapter { }, start && { range: { - 'event.start': { + '@timestamp': { gte: start, }, }, }, end && { range: { - 'event.end': { + '@timestamp': { lte: end, }, }, diff --git a/x-pack/plugins/event_log/server/event_log_client.test.ts b/x-pack/plugins/event_log/server/event_log_client.test.ts index 6d4c9b67abc1ba..17c073c4b27f9b 100644 --- a/x-pack/plugins/event_log/server/event_log_client.test.ts +++ b/x-pack/plugins/event_log/server/event_log_client.test.ts @@ -112,7 +112,7 @@ describe('EventLogStart', () => { { page: 1, per_page: 10, - sort_field: 'event.start', + sort_field: '@timestamp', sort_order: 'asc', } ); @@ -193,7 +193,7 @@ describe('EventLogStart', () => { { page: 1, per_page: 10, - sort_field: 'event.start', + sort_field: '@timestamp', sort_order: 'asc', start, end, diff --git a/x-pack/plugins/event_log/server/event_log_client.ts b/x-pack/plugins/event_log/server/event_log_client.ts index 765f0895f8e0d1..8ef245e60aadc4 100644 --- a/x-pack/plugins/event_log/server/event_log_client.ts +++ b/x-pack/plugins/event_log/server/event_log_client.ts @@ -36,6 +36,7 @@ export const findOptionsSchema = schema.object({ end: optionalDateFieldSchema, sort_field: schema.oneOf( [ + schema.literal('@timestamp'), schema.literal('event.start'), schema.literal('event.end'), schema.literal('event.provider'), @@ -44,7 +45,7 @@ export const findOptionsSchema = schema.object({ schema.literal('message'), ], { - defaultValue: 'event.start', + defaultValue: '@timestamp', } ), sort_order: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { diff --git a/x-pack/plugins/event_log/server/event_logger.test.ts b/x-pack/plugins/event_log/server/event_logger.test.ts index 6a745931420c0c..2bda194a65d133 100644 --- a/x-pack/plugins/event_log/server/event_logger.test.ts +++ b/x-pack/plugins/event_log/server/event_logger.test.ts @@ -150,6 +150,35 @@ describe('EventLogger', () => { message = await waitForLogMessage(systemLogger); expect(message).toMatch(/invalid event logged.*action.*undefined.*/); }); + + test('logs warnings when writing invalid events', async () => { + service.registerProviderActions('provider', ['action-a']); + eventLogger = service.getLogger({}); + + eventLogger.logEvent(({ event: { PROVIDER: 'provider' } } as unknown) as IEvent); + let message = await waitForLogMessage(systemLogger); + expect(message).toMatch(/invalid event logged.*provider.*undefined.*/); + + const event: IEvent = { + event: { + provider: 'provider', + action: 'action-a', + }, + kibana: { + saved_objects: [ + { + rel: 'ZZZ-primary', + namespace: 'default', + type: 'event_log_test', + id: '123', + }, + ], + }, + }; + eventLogger.logEvent(event); + message = await waitForLogMessage(systemLogger); + expect(message).toMatch(/invalid rel property.*ZZZ-primary.*/); + }); }); // return the next logged event; throw if not an event diff --git a/x-pack/plugins/event_log/server/event_logger.ts b/x-pack/plugins/event_log/server/event_logger.ts index bcfd7bd45a6f5c..1a710a6fa48653 100644 --- a/x-pack/plugins/event_log/server/event_logger.ts +++ b/x-pack/plugins/event_log/server/event_logger.ts @@ -19,6 +19,7 @@ import { ECS_VERSION, EventSchema, } from './types'; +import { SAVED_OBJECT_REL_PRIMARY } from './types'; type SystemLogger = Plugin['systemLogger']; @@ -118,6 +119,8 @@ const RequiredEventSchema = schema.object({ action: schema.string({ minLength: 1 }), }); +const ValidSavedObjectRels = new Set([undefined, SAVED_OBJECT_REL_PRIMARY]); + function validateEvent(eventLogService: IEventLogService, event: IEvent): IValidatedEvent { if (event?.event == null) { throw new Error(`no "event" property`); @@ -137,7 +140,17 @@ function validateEvent(eventLogService: IEventLogService, event: IEvent): IValid } // could throw an error - return EventSchema.validate(event); + const result = EventSchema.validate(event); + + if (result?.kibana?.saved_objects?.length) { + for (const so of result?.kibana?.saved_objects) { + if (!ValidSavedObjectRels.has(so.rel)) { + throw new Error(`invalid rel property in saved_objects: "${so.rel}"`); + } + } + } + + return result; } export const EVENT_LOGGED_PREFIX = `event logged: `; diff --git a/x-pack/plugins/event_log/server/index.ts b/x-pack/plugins/event_log/server/index.ts index b7fa25cb6eb9cc..0612b5319c15b2 100644 --- a/x-pack/plugins/event_log/server/index.ts +++ b/x-pack/plugins/event_log/server/index.ts @@ -8,6 +8,12 @@ import { PluginInitializerContext } from 'src/core/server'; import { ConfigSchema } from './types'; import { Plugin } from './plugin'; -export { IEventLogService, IEventLogger, IEventLogClientService, IEvent } from './types'; +export { + IEventLogService, + IEventLogger, + IEventLogClientService, + IEvent, + SAVED_OBJECT_REL_PRIMARY, +} from './types'; export const config = { schema: ConfigSchema }; export const plugin = (context: PluginInitializerContext) => new Plugin(context); diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index baf53ef4479141..58be6707b03730 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -13,6 +13,8 @@ import { IEvent } from '../generated/schemas'; import { FindOptionsType } from './event_log_client'; import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter'; +export const SAVED_OBJECT_REL_PRIMARY = 'primary'; + export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), logEntries: schema.boolean({ defaultValue: false }), diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index 35ec0bb2bf6ce1..fa479b1b06d5ef 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -10,7 +10,7 @@ import angular from 'angular'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; import '../../../../webpackShims/ace'; -// required for i18nIdDirective +// required for i18nIdDirective and `ngSanitize` angular module import 'angular-sanitize'; // required for ngRoute import 'angular-route'; diff --git a/x-pack/plugins/graph/server/saved_objects/migrations.ts b/x-pack/plugins/graph/server/saved_objects/migrations.ts index e77d2ea0fb7c97..beb31d548c6702 100644 --- a/x-pack/plugins/graph/server/saved_objects/migrations.ts +++ b/x-pack/plugins/graph/server/saved_objects/migrations.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import { SavedObjectUnsanitizedDoc } from 'kibana/server'; export const graphMigrations = { - '7.0.0': (doc: SavedObjectUnsanitizedDoc) => { + '7.0.0': (doc: SavedObjectUnsanitizedDoc) => { // Set new "references" attribute doc.references = doc.references || []; // Migrate index pattern diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx index cdefffeb35c156..d4d53b81109c61 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useMemo, useEffect, useState } from 'react'; +import React, { ChangeEvent, useCallback, useMemo, useEffect, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -13,6 +13,7 @@ import { EuiText, EuiFormRow, EuiButtonEmpty, + EuiFieldSearch, } from '@elastic/eui'; import { IFieldType } from 'src/plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -147,7 +148,7 @@ export const Expressions: React.FC = props => { const onGroupByChange = useCallback( (group: string | null) => { - setAlertParams('groupBy', group || undefined); + setAlertParams('groupBy', group || ''); }, [setAlertParams] ); @@ -215,10 +216,20 @@ export const Expressions: React.FC = props => { } setAlertParams('sourceId', source?.id); } else { - setAlertParams('criteria', [defaultExpression]); + if (!alertParams.criteria) { + setAlertParams('criteria', [defaultExpression]); + } + if (!alertParams.sourceId) { + setAlertParams('sourceId', source?.id || 'default'); + } } }, [alertsContext.metadata, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps + const handleFieldSearchChange = useCallback( + (e: ChangeEvent) => onFilterQuerySubmit(e.target.value), + [onFilterQuerySubmit] + ); + return ( <> @@ -273,48 +284,52 @@ export const Expressions: React.FC = props => { - {alertsContext.metadata && ( - <> - - - - - - + {(alertsContext.metadata && ( + + )) || ( + - - - - )} + /> + )} + + + + + + ); }; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 946f1c14bf593a..cf691f73bdc2cc 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -51,17 +51,19 @@ const getCurrentValueFromAggregations = ( const getParsedFilterQuery: ( filterQuery: string | undefined -) => Record = filterQuery => { +) => Record | Array> = filterQuery => { if (!filterQuery) return {}; try { return JSON.parse(filterQuery).bool; } catch (e) { - return { - query_string: { - query: filterQuery, - analyze_wildcard: true, + return [ + { + query_string: { + query: filterQuery, + analyze_wildcard: true, + }, }, - }; + ]; } }; @@ -159,8 +161,12 @@ export const getElasticsearchMetricQuery = ( return { query: { bool: { - filter: [...rangeFilters, ...metricFieldFilters], - ...parsedFilterQuery, + filter: [ + ...rangeFilters, + ...metricFieldFilters, + ...(Array.isArray(parsedFilterQuery) ? parsedFilterQuery : []), + ], + ...(!Array.isArray(parsedFilterQuery) ? parsedFilterQuery : {}), }, }, size: 0, @@ -233,6 +239,7 @@ const getMetric: ( body: searchBody, index, }); + return { '*': getCurrentValueFromAggregations(result.aggregations, aggType) }; } catch (e) { return { '*': undefined }; // Trigger an Error state diff --git a/x-pack/plugins/ingest_manager/common/constants/agent_config.ts b/x-pack/plugins/ingest_manager/common/constants/agent_config.ts index c5067480fb9538..9bc1293799d3c5 100644 --- a/x-pack/plugins/ingest_manager/common/constants/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/constants/agent_config.ts @@ -14,6 +14,7 @@ export const DEFAULT_AGENT_CONFIG = { status: AgentConfigStatus.Active, datasources: [], is_default: true, + monitoring_enabled: ['logs', 'metrics'] as Array<'logs' | 'metrics'>, }; export const DEFAULT_AGENT_CONFIGS_PACKAGES = [DefaultPackages.system]; diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts index a7d4e36d16f2ad..bff799798ff6e8 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts @@ -3,11 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Datasource, NewDatasource, DatasourceInput } from '../types'; +import { Datasource, DatasourceInput } from '../types'; import { storedDatasourceToAgentDatasource } from './datasource_to_agent_datasource'; describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { - const mockNewDatasource: NewDatasource = { + const mockDatasource: Datasource = { + id: 'some-uuid', name: 'mock-datasource', description: '', config_id: '', @@ -15,11 +16,6 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { output_id: '', namespace: 'default', inputs: [], - }; - - const mockDatasource: Datasource = { - ...mockNewDatasource, - id: 'some-uuid', revision: 1, }; @@ -107,17 +103,6 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }); }); - it('uses name for id when id is not provided in case of new datasource', () => { - expect(storedDatasourceToAgentDatasource(mockNewDatasource)).toEqual({ - id: 'mock-datasource', - name: 'mock-datasource', - namespace: 'default', - enabled: true, - use_output: 'default', - inputs: [], - }); - }); - it('returns agent datasource config with flattened input and package stream', () => { expect(storedDatasourceToAgentDatasource({ ...mockDatasource, inputs: [mockInput] })).toEqual({ id: 'some-uuid', diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts index 5deb33ccf10f1b..620b663451ea39 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts @@ -3,16 +3,16 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Datasource, NewDatasource, FullAgentConfigDatasource } from '../types'; +import { Datasource, FullAgentConfigDatasource } from '../types'; import { DEFAULT_OUTPUT } from '../constants'; export const storedDatasourceToAgentDatasource = ( - datasource: Datasource | NewDatasource + datasource: Datasource ): FullAgentConfigDatasource => { - const { name, namespace, enabled, package: pkg, inputs } = datasource; + const { id, name, namespace, enabled, package: pkg, inputs } = datasource; const fullDatasource: FullAgentConfigDatasource = { - id: 'id' in datasource ? datasource.id : name, + id: id || name, name, namespace, enabled, diff --git a/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts b/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts index cb290e61b17e51..a977a1a66e059a 100644 --- a/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts @@ -11,6 +11,7 @@ describe('Ingest Manager - packageToConfig', () => { name: 'mock-package', title: 'Mock package', version: '0.0.0', + latestVersion: '0.0.0', description: 'description', type: 'mock', categories: [], diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts index 2372caee512af6..96121251b133eb 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts @@ -3,8 +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 { SavedObjectAttributes } from 'src/core/public'; import { Datasource, DatasourcePackage, @@ -23,9 +21,10 @@ export interface NewAgentConfig { namespace?: string; description?: string; is_default?: boolean; + monitoring_enabled?: Array<'logs' | 'metrics'>; } -export interface AgentConfig extends NewAgentConfig, SavedObjectAttributes { +export interface AgentConfig extends NewAgentConfig { id: string; status: AgentConfigStatus; datasources: string[] | Datasource[]; @@ -60,4 +59,12 @@ export interface FullAgentConfig { }; datasources: FullAgentConfigDatasource[]; revision?: number; + settings?: { + monitoring: { + use_output?: string; + enabled: boolean; + metrics: boolean; + logs: boolean; + }; + }; } diff --git a/x-pack/plugins/ingest_manager/common/types/models/datasource.ts b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts index 885e0a9316d793..ca61a93d9be93e 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/datasource.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/datasource.ts @@ -17,22 +17,29 @@ export interface DatasourceConfigRecordEntry { export type DatasourceConfigRecord = Record; -export interface DatasourceInputStream { +export interface NewDatasourceInputStream { id: string; enabled: boolean; dataset: string; processors?: string[]; config?: DatasourceConfigRecord; vars?: DatasourceConfigRecord; +} + +export interface DatasourceInputStream extends NewDatasourceInputStream { agent_stream?: any; } -export interface DatasourceInput { +export interface NewDatasourceInput { type: string; enabled: boolean; processors?: string[]; config?: DatasourceConfigRecord; vars?: DatasourceConfigRecord; + streams: NewDatasourceInputStream[]; +} + +export interface DatasourceInput extends Omit { streams: DatasourceInputStream[]; } @@ -44,10 +51,11 @@ export interface NewDatasource { enabled: boolean; package?: DatasourcePackage; output_id: string; - inputs: DatasourceInput[]; + inputs: NewDatasourceInput[]; } -export type Datasource = NewDatasource & { +export interface Datasource extends Omit { id: string; + inputs: DatasourceInput[]; revision: number; -}; +} diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index c750aa99204fa9..f8779a879a049f 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -32,6 +32,7 @@ export enum KibanaAssetType { } export enum ElasticsearchAssetType { + componentTemplate = 'component-template', ingestPipeline = 'ingest-pipeline', indexTemplate = 'index-template', ilmPolicy = 'ilm-policy', @@ -96,6 +97,7 @@ export interface RegistryStream { description?: string; enabled?: boolean; vars?: RegistryVarsEntry[]; + template?: string; } export type RequirementVersion = string; @@ -202,6 +204,8 @@ export interface RegistryVarsEntry { // internal until we need them interface PackageAdditions { title: string; + latestVersion: string; + installedVersion?: string; assets: AssetsGroupedByServiceByType; } @@ -246,6 +250,7 @@ export enum IngestAssetType { DataFrameTransform = 'data-frame-transform', IlmPolicy = 'ilm-policy', IndexTemplate = 'index-template', + ComponentTemplate = 'component-template', IngestPipeline = 'ingest-pipeline', MlJob = 'ml-job', RollupJob = 'rollup-job', @@ -261,12 +266,17 @@ export interface IndexTemplateMappings { properties: any; } +// This is an index template v2, see https://github.com/elastic/elasticsearch/issues/53101 +// until "proper" documentation of the new format is available. +// Ingest Manager does not use nor support the legacy index template v1 format at all export interface IndexTemplate { - order: number; + priority: number; index_patterns: string[]; - settings: any; - mappings: object; - aliases: object; + template: { + settings: any; + mappings: object; + aliases: object; + }; } export interface TemplateRef { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.tsx new file mode 100644 index 00000000000000..1e7a14e3502296 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.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 from 'react'; +import { + EuiButtonEmpty, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiFlyoutFooter, + EuiLink, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +interface Props { + onClose: () => void; +} + +export const AlphaFlyout: React.FunctionComponent = ({ onClose }) => { + return ( + + + +

+ +

+
+
+ + +

+ +

+ + + + ), + forumLink: ( + + + + ), + }} + /> +

+ +

+ + + + ), + }} + /> +

+
+
+ + + + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx index 0f3ddee29fa443..5a06a9a8794412 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx @@ -3,35 +3,45 @@ * 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 React, { useState } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiText } from '@elastic/eui'; +import { EuiText, EuiLink } from '@elastic/eui'; +import { AlphaFlyout } from './alpha_flyout'; const Message = styled(EuiText).attrs(props => ({ color: 'subdued', textAlign: 'center', + size: 's', }))` padding: ${props => props.theme.eui.paddingSizes.m}; `; -export const AlphaMessaging: React.FC<{}> = () => ( - -

- - +export const AlphaMessaging: React.FC<{}> = () => { + const [isAlphaFlyoutOpen, setIsAlphaFlyoutOpen] = useState(false); + + return ( + <> + +

+ + + + {' – '} - - {' – '} - - -

-
-); + />{' '} + setIsAlphaFlyoutOpen(true)}> + View more details. + +

+ + {isAlphaFlyoutOpen && setIsAlphaFlyoutOpen(false)} />} + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts index d16d835f8c701c..bed3f994005ad2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts @@ -4,7 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpFetchQuery } from 'src/core/public'; -import { useRequest, sendRequest } from './use_request'; +import { + useRequest, + sendRequest, + useConditionalRequest, + SendConditionalRequestConfig, +} from './use_request'; import { agentConfigRouteService } from '../../services'; import { GetAgentConfigsResponse, @@ -25,11 +30,12 @@ export const useGetAgentConfigs = (query: HttpFetchQuery = {}) => { }); }; -export const useGetOneAgentConfig = (agentConfigId: string) => { - return useRequest({ - path: agentConfigRouteService.getInfoPath(agentConfigId), +export const useGetOneAgentConfig = (agentConfigId: string | undefined) => { + return useConditionalRequest({ + path: agentConfigId ? agentConfigRouteService.getInfoPath(agentConfigId) : undefined, method: 'get', - }); + shouldSendRequest: !!agentConfigId, + } as SendConditionalRequestConfig); }; export const useGetOneAgentConfigFull = (agentConfigId: string) => { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/datasource.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/datasource.ts index 0d19ecd0cb7351..e2fc190e158f97 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/datasource.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/datasource.ts @@ -5,12 +5,18 @@ */ import { sendRequest, useRequest } from './use_request'; import { datasourceRouteService } from '../../services'; -import { CreateDatasourceRequest, CreateDatasourceResponse } from '../../types'; +import { + CreateDatasourceRequest, + CreateDatasourceResponse, + UpdateDatasourceRequest, + UpdateDatasourceResponse, +} from '../../types'; import { DeleteDatasourcesRequest, DeleteDatasourcesResponse, GetDatasourcesRequest, GetDatasourcesResponse, + GetOneDatasourceResponse, } from '../../../../../common/types/rest_spec'; export const sendCreateDatasource = (body: CreateDatasourceRequest['body']) => { @@ -21,6 +27,17 @@ export const sendCreateDatasource = (body: CreateDatasourceRequest['body']) => { }); }; +export const sendUpdateDatasource = ( + datasourceId: string, + body: UpdateDatasourceRequest['body'] +) => { + return sendRequest({ + path: datasourceRouteService.getUpdatePath(datasourceId), + method: 'put', + body: JSON.stringify(body), + }); +}; + export const sendDeleteDatasource = (body: DeleteDatasourcesRequest['body']) => { return sendRequest({ path: datasourceRouteService.getDeletePath(), @@ -36,3 +53,10 @@ export function useGetDatasources(query: GetDatasourcesRequest['query']) { query, }); } + +export const sendGetOneDatasource = (datasourceId: string) => { + return sendRequest({ + path: datasourceRouteService.getInfoPath(datasourceId), + method: 'get', + }); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/enrollment_api_keys.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/enrollment_api_keys.ts index e4abb4ccd22cb5..10d9e03e986e10 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/enrollment_api_keys.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/enrollment_api_keys.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useRequest, UseRequestConfig, sendRequest } from './use_request'; +import { + useRequest, + UseRequestConfig, + sendRequest, + useConditionalRequest, + SendConditionalRequestConfig, +} from './use_request'; import { enrollmentAPIKeyRouteService } from '../../services'; import { GetOneEnrollmentAPIKeyResponse, @@ -14,12 +20,12 @@ import { type RequestOptions = Pick, 'pollIntervalMs'>; -export function useGetOneEnrollmentAPIKey(keyId: string, options?: RequestOptions) { - return useRequest({ +export function useGetOneEnrollmentAPIKey(keyId: string | undefined) { + return useConditionalRequest({ method: 'get', - path: enrollmentAPIKeyRouteService.getInfoPath(keyId), - ...options, - }); + path: keyId ? enrollmentAPIKeyRouteService.getInfoPath(keyId) : undefined, + shouldSendRequest: !!keyId, + } as SendConditionalRequestConfig); } export function sendGetOneEnrollmentAPIKey(keyId: string, options?: RequestOptions) { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts index c63383637e792f..fbbc482fb96af2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/use_request.ts @@ -3,6 +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 { useState, useEffect } from 'react'; import { HttpSetup } from 'src/core/public'; import { SendRequestConfig, @@ -35,3 +36,68 @@ export const useRequest = (config: UseRequestConfig) => { } return _useRequest(httpClient, config); }; + +export type SendConditionalRequestConfig = + | (SendRequestConfig & { shouldSendRequest: true }) + | (Partial & { shouldSendRequest: false }); + +export const useConditionalRequest = (config: SendConditionalRequestConfig) => { + const [state, setState] = useState<{ + error: Error | null; + data: D | null; + isLoading: boolean; + }>({ + error: null, + data: null, + isLoading: false, + }); + + const { path, method, shouldSendRequest, query, body } = config; + + async function sendGetOneEnrollmentAPIKeyRequest() { + if (!config.shouldSendRequest) { + setState({ + data: null, + isLoading: false, + error: null, + }); + return; + } + + try { + setState({ + data: null, + isLoading: true, + error: null, + }); + const res = await sendRequest({ + method: config.method, + path: config.path, + query: config.query, + body: config.body, + }); + if (res.error) { + throw res.error; + } + setState({ + data: res.data, + isLoading: false, + error: null, + }); + return res; + } catch (error) { + setState({ + data: null, + isLoading: false, + error, + }); + } + } + + useEffect(() => { + sendGetOneEnrollmentAPIKeyRequest(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [path, method, shouldSendRequest, JSON.stringify(query), JSON.stringify(body)]); + + return { ...state, sendRequest: sendGetOneEnrollmentAPIKeyRequest }; +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx index 0d53ca34a1fefd..92c44d86e47c65 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx @@ -18,6 +18,7 @@ import { EuiText, EuiComboBox, EuiIconTip, + EuiCheckboxGroup, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -30,7 +31,7 @@ interface ValidationResults { const StyledEuiAccordion = styled(EuiAccordion)` .ingest-active-button { - color: ${props => props.theme.eui.euiColorPrimary}}; + color: ${props => props.theme.eui.euiColorPrimary}; } `; @@ -244,6 +245,68 @@ export const AgentConfigForm: React.FunctionComponent = ({ )} + + + + +

+ +

+
+ + + + +
+ + { + acc[key] = true; + return acc; + }, + { logs: false, metrics: false } + )} + onChange={id => { + if (id !== 'logs' && id !== 'metrics') { + return; + } + + const hasLogs = + agentConfig.monitoring_enabled && agentConfig.monitoring_enabled.indexOf(id) >= 0; + + const previousValues = agentConfig.monitoring_enabled || []; + updateAgentConfig({ + monitoring_enabled: hasLogs + ? previousValues.filter(type => type !== id) + : [...previousValues, id], + }); + }} + /> + +
); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx index 39d882f7fdf65e..f1e3fea6a07423 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx @@ -39,17 +39,29 @@ export const CreateDatasourcePageLayout: React.FunctionComponent<{

- + {from === 'edit' ? ( + + ) : ( + + )}

- {from === 'config' ? ( + {from === 'edit' ? ( + + ) : from === 'config' ? ( - {agentConfig && from === 'config' ? ( + {agentConfig && (from === 'config' || from === 'edit') ? ( { const updatePackageInfo = (updatedPackageInfo: PackageInfo | undefined) => { if (updatedPackageInfo) { setPackageInfo(updatedPackageInfo); + setFormState('VALID'); } else { setFormState('INVALID'); setPackageInfo(undefined); @@ -152,9 +153,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { const cancelUrl = from === 'config' ? CONFIG_URL : PACKAGE_URL; // Save datasource - const [formState, setFormState] = useState< - 'VALID' | 'INVALID' | 'CONFIRM' | 'LOADING' | 'SUBMITTED' - >('INVALID'); + const [formState, setFormState] = useState('INVALID'); const saveDatasource = async () => { setFormState('LOADING'); const result = await sendCreateDatasource(datasource); @@ -174,6 +173,23 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { const { error } = await saveDatasource(); if (!error) { history.push(`${AGENT_CONFIG_DETAILS_PATH}${agentConfig ? agentConfig.id : configId}`); + notifications.toasts.addSuccess({ + title: i18n.translate('xpack.ingestManager.createDatasource.addedNotificationTitle', { + defaultMessage: `Successfully added '{datasourceName}'`, + values: { + datasourceName: datasource.name, + }, + }), + text: + agentCount && agentConfig + ? i18n.translate('xpack.ingestManager.createDatasource.addedNotificationMessage', { + defaultMessage: `Fleet will deploy updates to all agents that use the '{agentConfigName}' configuration`, + values: { + agentConfigName: agentConfig.name, + }, + }) + : undefined, + }); } else { notifications.toasts.addError(error, { title: 'Error', @@ -229,6 +245,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { packageInfo={packageInfo} datasource={datasource} updateDatasource={updateDatasource} + validationResults={validationResults!} /> ) : null, }, @@ -240,7 +257,6 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { children: agentConfig && packageInfo ? ( ) => void; validationResults: DatasourceValidationResults; submitAttempted: boolean; -}> = ({ - agentConfig, - packageInfo, - datasource, - updateDatasource, - validationResults, - submitAttempted, -}) => { - // Form show/hide states - +}> = ({ packageInfo, datasource, updateDatasource, validationResults, submitAttempted }) => { const hasErrors = validationResults ? validationHasErrors(validationResults) : false; - // Update datasource's package and config info - useEffect(() => { - const dsPackage = datasource.package; - const currentPkgKey = dsPackage ? `${dsPackage.name}-${dsPackage.version}` : ''; - const pkgKey = `${packageInfo.name}-${packageInfo.version}`; - - // If package has changed, create shell datasource with input&stream values based on package info - if (currentPkgKey !== pkgKey) { - // Existing datasources on the agent config using the package name, retrieve highest number appended to datasource name - const dsPackageNamePattern = new RegExp(`${packageInfo.name}-(\\d+)`); - const dsWithMatchingNames = (agentConfig.datasources as Datasource[]) - .filter(ds => Boolean(ds.name.match(dsPackageNamePattern))) - .map(ds => parseInt(ds.name.match(dsPackageNamePattern)![1], 10)) - .sort(); - - updateDatasource({ - name: `${packageInfo.name}-${ - dsWithMatchingNames.length ? dsWithMatchingNames[dsWithMatchingNames.length - 1] + 1 : 1 - }`, - package: { - name: packageInfo.name, - title: packageInfo.title, - version: packageInfo.version, - }, - inputs: packageToConfigDatasourceInputs(packageInfo), - }); - } - - // If agent config has changed, update datasource's config ID and namespace - if (datasource.config_id !== agentConfig.id) { - updateDatasource({ - config_id: agentConfig.id, - namespace: agentConfig.namespace, - }); - } - }, [datasource.package, datasource.config_id, agentConfig, packageInfo, updateDatasource]); - - // Step B, configure inputs (and their streams) + // Configure inputs (and their streams) // Assume packages only export one datasource for now const renderConfigureInputs = () => packageInfo.datasources && diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_define_datasource.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_define_datasource.tsx index 792389381eaf04..c4d602c2c20819 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_define_datasource.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_define_datasource.tsx @@ -17,13 +17,16 @@ import { } from '@elastic/eui'; import { AgentConfig, PackageInfo, Datasource, NewDatasource } from '../../../types'; import { packageToConfigDatasourceInputs } from '../../../services'; +import { Loading } from '../../../components'; +import { DatasourceValidationResults } from './services'; export const StepDefineDatasource: React.FunctionComponent<{ agentConfig: AgentConfig; packageInfo: PackageInfo; datasource: NewDatasource; updateDatasource: (fields: Partial) => void; -}> = ({ agentConfig, packageInfo, datasource, updateDatasource }) => { + validationResults: DatasourceValidationResults; +}> = ({ agentConfig, packageInfo, datasource, updateDatasource, validationResults }) => { // Form show/hide states const [isShowingAdvancedDefine, setIsShowingAdvancedDefine] = useState(false); @@ -64,11 +67,13 @@ export const StepDefineDatasource: React.FunctionComponent<{ } }, [datasource.package, datasource.config_id, agentConfig, packageInfo, updateDatasource]); - return ( + return validationResults ? ( <> } + isInvalid={!!validationResults.description} + error={validationResults.description} > ) : null} + ) : ( + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts index 85cc758fc4c464..10b30a5696d834 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export type CreateDatasourceFrom = 'package' | 'config'; +export type CreateDatasourceFrom = 'package' | 'config' | 'edit'; +export type DatasourceFormState = 'VALID' | 'INVALID' | 'CONFIRM' | 'LOADING' | 'SUBMITTED'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx index 1eee9f6b0c3462..a0418c5f256c48 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx @@ -19,7 +19,7 @@ import { import { AgentConfig, Datasource } from '../../../../../types'; import { TableRowActions } from '../../../components/table_row_actions'; import { DangerEuiContextMenuItem } from '../../../components/danger_eui_context_menu_item'; -import { useCapabilities } from '../../../../../hooks'; +import { useCapabilities, useLink } from '../../../../../hooks'; import { useAgentConfigLink } from '../../hooks/use_details_uri'; import { DatasourceDeleteProvider } from '../../../components/datasource_delete_provider'; import { useConfigRefresh } from '../../hooks/use_config'; @@ -56,6 +56,7 @@ export const DatasourcesTable: React.FunctionComponent = ({ }) => { const hasWriteCapabilities = useCapabilities().write; const addDatasourceLink = useAgentConfigLink('add-datasource', { configId: config.id }); + const editDatasourceLink = useLink(`/configs/${config.id}/edit-datasource`); const refreshConfig = useConfigRefresh(); // With the datasources provided on input, generate the list of datasources @@ -201,22 +202,21 @@ export const DatasourcesTable: React.FunctionComponent = ({ {}} + // key="datasourceView" + // > + // + // , {}} - key="datasourceView" - > - - , - // FIXME: implement Edit datasource action - {}} + href={`${editDatasourceLink}/${datasource.id}`} key="datasourceEdit" > = ({ /> , // FIXME: implement Copy datasource action - {}} key="datasourceCopy"> - - , + // {}} key="datasourceCopy"> + // + // , {deleteDatasourcePrompt => { return ( @@ -256,7 +256,7 @@ export const DatasourcesTable: React.FunctionComponent = ({ ], }, ], - [config, hasWriteCapabilities, refreshConfig] + [config, editDatasourceLink, hasWriteCapabilities, refreshConfig] ); return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx index ad27c590d5eaae..f1d7bd5dbc0396 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx @@ -45,7 +45,7 @@ export const ConfigYamlView = memo<{ config: AgentConfig }>(({ config }) => { page: 1, perPage: 1000, }); - const apiKeyRequest = useGetOneEnrollmentAPIKey(apiKeysRequest.data?.list?.[0]?.id as string); + const apiKeyRequest = useGetOneEnrollmentAPIKey(apiKeysRequest.data?.list?.[0]?.id); if (fullConfigRequest.isLoading && !fullConfigRequest.data) { return ; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx new file mode 100644 index 00000000000000..d4c39f21a1ea6c --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx @@ -0,0 +1,323 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { useRouteMatch, useHistory } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiButtonEmpty, + EuiButton, + EuiSteps, + EuiBottomBar, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, +} from '@elastic/eui'; +import { AGENT_CONFIG_DETAILS_PATH } from '../../../constants'; +import { AgentConfig, PackageInfo, NewDatasource } from '../../../types'; +import { + useLink, + useCore, + useConfig, + sendUpdateDatasource, + sendGetAgentStatus, + sendGetOneAgentConfig, + sendGetOneDatasource, + sendGetPackageInfoByKey, +} from '../../../hooks'; +import { Loading, Error } from '../../../components'; +import { + CreateDatasourcePageLayout, + ConfirmCreateDatasourceModal, +} from '../create_datasource_page/components'; +import { + DatasourceValidationResults, + validateDatasource, + validationHasErrors, +} from '../create_datasource_page/services'; +import { DatasourceFormState, CreateDatasourceFrom } from '../create_datasource_page/types'; +import { StepConfigureDatasource } from '../create_datasource_page/step_configure_datasource'; +import { StepDefineDatasource } from '../create_datasource_page/step_define_datasource'; + +export const EditDatasourcePage: React.FunctionComponent = () => { + const { notifications } = useCore(); + const { + fleet: { enabled: isFleetEnabled }, + } = useConfig(); + const { + params: { configId, datasourceId }, + } = useRouteMatch(); + const history = useHistory(); + + // Agent config, package info, and datasource states + const [isLoadingData, setIsLoadingData] = useState(true); + const [loadingError, setLoadingError] = useState(); + const [agentConfig, setAgentConfig] = useState(); + const [packageInfo, setPackageInfo] = useState(); + const [datasource, setDatasource] = useState({ + name: '', + description: '', + config_id: '', + enabled: true, + output_id: '', + inputs: [], + }); + + // Retrieve agent config, package, and datasource info + useEffect(() => { + const getData = async () => { + setIsLoadingData(true); + setLoadingError(undefined); + try { + const [{ data: agentConfigData }, { data: datasourceData }] = await Promise.all([ + sendGetOneAgentConfig(configId), + sendGetOneDatasource(datasourceId), + ]); + if (agentConfigData?.item) { + setAgentConfig(agentConfigData.item); + } + if (datasourceData?.item) { + const { id, revision, inputs, ...restOfDatasource } = datasourceData.item; + // Remove `agent_stream` from all stream info, we assign this after saving + const newDatasource = { + ...restOfDatasource, + inputs: inputs.map(input => { + const { streams, ...restOfInput } = input; + return { + ...restOfInput, + streams: streams.map(stream => { + const { agent_stream, ...restOfStream } = stream; + return restOfStream; + }), + }; + }), + }; + setDatasource(newDatasource); + if (datasourceData.item.package) { + const { data: packageData } = await sendGetPackageInfoByKey( + `${datasourceData.item.package.name}-${datasourceData.item.package.version}` + ); + if (packageData?.response) { + setPackageInfo(packageData.response); + setValidationResults(validateDatasource(newDatasource, packageData.response)); + setFormState('VALID'); + } + } + } + } catch (e) { + setLoadingError(e); + } + setIsLoadingData(false); + }; + getData(); + }, [configId, datasourceId]); + + // Retrieve agent count + const [agentCount, setAgentCount] = useState(0); + useEffect(() => { + const getAgentCount = async () => { + const { data } = await sendGetAgentStatus({ configId }); + if (data?.results.total) { + setAgentCount(data.results.total); + } + }; + + if (isFleetEnabled) { + getAgentCount(); + } + }, [configId, isFleetEnabled]); + + // Datasource validation state + const [validationResults, setValidationResults] = useState(); + const hasErrors = validationResults ? validationHasErrors(validationResults) : false; + + // Update datasource method + const updateDatasource = (updatedFields: Partial) => { + const newDatasource = { + ...datasource, + ...updatedFields, + }; + setDatasource(newDatasource); + + // eslint-disable-next-line no-console + console.debug('Datasource updated', newDatasource); + const newValidationResults = updateDatasourceValidation(newDatasource); + const hasValidationErrors = newValidationResults + ? validationHasErrors(newValidationResults) + : false; + if (!hasValidationErrors) { + setFormState('VALID'); + } + }; + + const updateDatasourceValidation = (newDatasource?: NewDatasource) => { + if (packageInfo) { + const newValidationResult = validateDatasource(newDatasource || datasource, packageInfo); + setValidationResults(newValidationResult); + // eslint-disable-next-line no-console + console.debug('Datasource validation results', newValidationResult); + + return newValidationResult; + } + }; + + // Cancel url + const CONFIG_URL = useLink(`${AGENT_CONFIG_DETAILS_PATH}${configId}`); + const cancelUrl = CONFIG_URL; + + // Save datasource + const [formState, setFormState] = useState('INVALID'); + const saveDatasource = async () => { + setFormState('LOADING'); + const result = await sendUpdateDatasource(datasourceId, datasource); + setFormState('SUBMITTED'); + return result; + }; + + const onSubmit = async () => { + if (formState === 'VALID' && hasErrors) { + setFormState('INVALID'); + return; + } + if (agentCount !== 0 && formState !== 'CONFIRM') { + setFormState('CONFIRM'); + return; + } + const { error } = await saveDatasource(); + if (!error) { + history.push(`${AGENT_CONFIG_DETAILS_PATH}${configId}`); + notifications.toasts.addSuccess({ + title: i18n.translate('xpack.ingestManager.editDatasource.updatedNotificationTitle', { + defaultMessage: `Successfully updated '{datasourceName}'`, + values: { + datasourceName: datasource.name, + }, + }), + text: + agentCount && agentConfig + ? i18n.translate('xpack.ingestManager.editDatasource.updatedNotificationMessage', { + defaultMessage: `Fleet will deploy updates to all agents that use the '{agentConfigName}' configuration`, + values: { + agentConfigName: agentConfig.name, + }, + }) + : undefined, + }); + } else { + notifications.toasts.addError(error, { + title: 'Error', + }); + setFormState('VALID'); + } + }; + + const layoutProps = { + from: 'edit' as CreateDatasourceFrom, + cancelUrl, + agentConfig, + packageInfo, + }; + + return ( + + {isLoadingData ? ( + + ) : loadingError || !agentConfig || !packageInfo ? ( + + } + error={ + loadingError || + i18n.translate('xpack.ingestManager.editDatasource.errorLoadingDataMessage', { + defaultMessage: 'There was an error loading this data source information', + }) + } + /> + ) : ( + <> + {formState === 'CONFIRM' && ( + setFormState('VALID')} + /> + )} + + ), + }, + { + title: i18n.translate( + 'xpack.ingestManager.editDatasource.stepConfgiureDatasourceTitle', + { + defaultMessage: 'Select the data you want to collect', + } + ), + children: ( + + ), + }, + ]} + /> + + + + + + + + + + + + + + + + + )} + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx index 71ada155373bfb..ef88aa5d17f1e4 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx @@ -8,10 +8,14 @@ import { HashRouter as Router, Switch, Route } from 'react-router-dom'; import { AgentConfigListPage } from './list_page'; import { AgentConfigDetailsPage } from './details_page'; import { CreateDatasourcePage } from './create_datasource_page'; +import { EditDatasourcePage } from './edit_datasource_page'; export const AgentConfigApp: React.FunctionComponent = () => ( + + + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx index 1fe116ef360901..9f582e7e2fbe69 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/components/create_config.tsx @@ -34,6 +34,7 @@ export const CreateAgentConfigFlyout: React.FunctionComponent = ({ onClos description: '', namespace: '', is_default: undefined, + monitoring_enabled: ['logs', 'metrics'], }); const [isLoading, setIsLoading] = useState(false); const [withSysMonitoring, setWithSysMonitoring] = useState(true); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx index 1ea162252c741e..3dcc19bc4a5aee 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx @@ -36,13 +36,11 @@ import { useConfig, useUrlParams, } from '../../../hooks'; -import { AgentConfigDeleteProvider } from '../components'; import { CreateAgentConfigFlyout } from './components'; import { SearchBar } from '../../../components/search_bar'; import { LinkedAgentCount } from '../components'; import { useAgentConfigLink } from '../details_page/hooks/use_details_uri'; import { TableRowActions } from '../components/table_row_actions'; -import { DangerEuiContextMenuItem } from '../components/danger_eui_context_menu_item'; const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ overflow: 'hidden', @@ -108,30 +106,12 @@ const ConfigRowActions = memo<{ config: AgentConfig; onDelete: () => void }>( defaultMessage="Create data source" /> , - - - - , - - - {deleteAgentConfigsPrompt => { - return ( - deleteAgentConfigsPrompt([config.id], onDelete)} - > - - - ); - }} - , + // + // + // , ]} /> ); @@ -156,7 +136,6 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { : urlParams.kuery ?? '' ); const { pagination, pageSizeOptions, setPagination } = usePagination(); - const [selectedAgentConfigs, setSelectedAgentConfigs] = useState([]); const history = useHistory(); const isCreateAgentConfigFlyoutOpen = 'create' in urlParams; const setIsCreateAgentConfigFlyoutOpen = useCallback( @@ -321,34 +300,6 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { /> ) : null} - {selectedAgentConfigs.length ? ( - - - {deleteAgentConfigsPrompt => ( - { - deleteAgentConfigsPrompt( - selectedAgentConfigs.map(agentConfig => agentConfig.id), - () => { - sendRequest(); - setSelectedAgentConfigs([]); - } - ); - }} - > - - - )} - - - ) : null} = () => { items={agentConfigData ? agentConfigData.items : []} itemId="id" columns={columns} - isSelectable={true} - selection={{ - selectable: (agentConfig: AgentConfig) => !agentConfig.is_default, - onSelectionChange: (newSelectedAgentConfigs: AgentConfig[]) => { - setSelectedAgentConfigs(newSelectedAgentConfigs); - }, - }} + isSelectable={false} pagination={{ pageIndex: pagination.currentPage - 1, pageSize: pagination.pageSize, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx new file mode 100644 index 00000000000000..64223efefaab8c --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/icons.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EuiIcon } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +export const StyledAlert = styled(EuiIcon)` + color: ${props => props.theme.eui.euiColorWarning}; + padding: 0 5px; +`; + +export const UpdateIcon = () => ; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx index 8ad081cbbabe40..ab7e87b3ad06cb 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx @@ -30,9 +30,15 @@ export function PackageCard({ showInstalledBadge, status, icons, + ...restProps }: PackageCardProps) { const { toDetailView } = useLinks(); - const url = toDetailView({ name, version }); + let urlVersion = version; + // if this is an installed package, link to the version installed + if ('savedObject' in restProps) { + urlVersion = restProps.savedObject.attributes.version || version; + } + const url = toDetailView({ name, version: urlVersion }); return ( = { 'ingest-pipeline': 'Ingest Pipeline', 'index-pattern': 'Index Pattern', 'index-template': 'Index Template', + 'component-template': 'Component Template', search: 'Saved Search', visualization: 'Visualization', input: 'Agent input', diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx index 0c5f45cdc47a71..244a9a2c7426e8 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx @@ -11,6 +11,7 @@ import { NotificationsStart } from 'src/core/public'; import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import { PackageInfo } from '../../../types'; import { sendInstallPackage, sendRemovePackage } from '../../../hooks'; +import { useLinks } from '.'; import { InstallStatus } from '../../../types'; interface PackagesInstall { @@ -19,31 +20,55 @@ interface PackagesInstall { interface PackageInstallItem { status: InstallStatus; + version: string | null; } -type InstallPackageProps = Pick; +type InstallPackageProps = Pick & { + fromUpdate?: boolean; +}; +type SetPackageInstallStatusProps = Pick & PackageInstallItem; function usePackageInstall({ notifications }: { notifications: NotificationsStart }) { + const { toDetailView } = useLinks(); const [packages, setPackage] = useState({}); const setPackageInstallStatus = useCallback( - ({ name, status }: { name: PackageInfo['name']; status: InstallStatus }) => { + ({ name, status, version }: SetPackageInstallStatusProps) => { + const packageProps: PackageInstallItem = { + status, + version, + }; setPackage((prev: PackagesInstall) => ({ ...prev, - [name]: { status }, + [name]: packageProps, })); }, [] ); + const getPackageInstallStatus = useCallback( + (pkg: string): PackageInstallItem => { + return packages[pkg]; + }, + [packages] + ); + const installPackage = useCallback( - async ({ name, version, title }: InstallPackageProps) => { - setPackageInstallStatus({ name, status: InstallStatus.installing }); + async ({ name, version, title, fromUpdate = false }: InstallPackageProps) => { + const currStatus = getPackageInstallStatus(name); + const newStatus = { ...currStatus, name, status: InstallStatus.installing }; + setPackageInstallStatus(newStatus); const pkgkey = `${name}-${version}`; const res = await sendInstallPackage(pkgkey); if (res.error) { - setPackageInstallStatus({ name, status: InstallStatus.notInstalled }); + if (fromUpdate) { + // if there is an error during update, set it back to the previous version + // as handling of bad update is not implemented yet + setPackageInstallStatus({ ...currStatus, name }); + } else { + setPackageInstallStatus({ name, status: InstallStatus.notInstalled, version }); + } notifications.toasts.addWarning({ title: toMountPoint( { - return packages[pkg].status; - }, - [packages] + [getPackageInstallStatus, notifications.toasts, setPackageInstallStatus, toDetailView] ); const uninstallPackage = useCallback( async ({ name, version, title }: Pick) => { - setPackageInstallStatus({ name, status: InstallStatus.uninstalling }); + setPackageInstallStatus({ name, status: InstallStatus.uninstalling, version }); const pkgkey = `${name}-${version}`; const res = await sendRemovePackage(pkgkey); if (res.error) { - setPackageInstallStatus({ name, status: InstallStatus.installed }); + setPackageInstallStatus({ name, status: InstallStatus.installed, version }); notifications.toasts.addWarning({ title: toMountPoint( ; export function ContentPanel(props: ContentPanelProps) { - const { panel, name, version, assets, title, removable } = props; + const { panel, name, version, assets, title, removable, latestVersion } = props; switch (panel) { case 'settings': return ( @@ -60,6 +60,7 @@ export function ContentPanel(props: ContentPanelProps) { assets={assets} title={title} removable={removable} + latestVersion={latestVersion} /> ); case 'data-sources': diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx index fa3245aec02c5b..c82b7ed2297a7c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx @@ -20,7 +20,7 @@ export const DataSourcesPanel = ({ name, version }: DataSourcesPanelProps) => { const packageInstallStatus = getPackageInstallStatus(name); // if they arrive at this page and the package is not installed, send them to overview // this happens if they arrive with a direct url or they uninstall while on this tab - if (packageInstallStatus !== InstallStatus.installed) + if (packageInstallStatus.status !== InstallStatus.installed) return ( props.theme.eui.euiSizeM}; `; -const StyledVersion = styled(Version)` - font-size: ${props => props.theme.eui.euiFontSizeS}; - color: ${props => props.theme.eui.euiColorDarkShade}; -`; - type HeaderProps = PackageInfo & { iconType?: IconType }; export function Header(props: HeaderProps) { - const { iconType, name, title, version } = props; + const { iconType, name, title, version, installedVersion, latestVersion } = props; const hasWriteCapabilites = useCapabilities().write; const { toListView } = useLinks(); const ADD_DATASOURCE_URI = useLink(`${EPM_PATH}/${name}-${version}/add-datasource`); - + const updateAvailable = installedVersion && installedVersion < latestVersion ? true : false; return ( @@ -59,7 +54,11 @@ export function Header(props: HeaderProps) {

{title} - + + + {version} {updateAvailable && } + +

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx index 3239d7b90e3c3c..1f3eb2cc9362e5 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx @@ -32,11 +32,12 @@ export function Detail() { const packageInfo = response.data?.response; const title = packageInfo?.title; const name = packageInfo?.name; + const installedVersion = packageInfo?.installedVersion; const status: InstallStatus = packageInfo?.status as any; // track install status state if (name) { - setPackageInstallStatus({ name, status }); + setPackageInstallStatus({ name, status, version: installedVersion || null }); } if (packageInfo) { setInfo({ ...packageInfo, title: title || '' }); @@ -64,7 +65,6 @@ type LayoutProps = PackageInfo & Pick & Pick diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/installation_button.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/installation_button.tsx index cbbf1ce53c4ea3..cdad67fd875483 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/installation_button.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/installation_button.tsx @@ -13,19 +13,21 @@ import { ConfirmPackageUninstall } from './confirm_package_uninstall'; import { ConfirmPackageInstall } from './confirm_package_install'; type InstallationButtonProps = Pick & { - disabled: boolean; + disabled?: boolean; + isUpdate?: boolean; }; export function InstallationButton(props: InstallationButtonProps) { - const { assets, name, title, version, disabled = true } = props; + const { assets, name, title, version, disabled = true, isUpdate = false } = props; const hasWriteCapabilites = useCapabilities().write; const installPackage = useInstallPackage(); const uninstallPackage = useUninstallPackage(); const getPackageInstallStatus = useGetPackageInstallStatus(); - const installationStatus = getPackageInstallStatus(name); + const { status: installationStatus } = getPackageInstallStatus(name); const isInstalling = installationStatus === InstallStatus.installing; const isRemoving = installationStatus === InstallStatus.uninstalling; const isInstalled = installationStatus === InstallStatus.installed; + const showUninstallButton = isInstalled || isRemoving; const [isModalVisible, setModalVisible] = useState(false); const toggleModal = useCallback(() => { setModalVisible(!isModalVisible); @@ -36,6 +38,10 @@ export function InstallationButton(props: InstallationButtonProps) { toggleModal(); }, [installPackage, name, title, toggleModal, version]); + const handleClickUpdate = useCallback(() => { + installPackage({ name, version, title, fromUpdate: true }); + }, [installPackage, name, title, version]); + const handleClickUninstall = useCallback(() => { uninstallPackage({ name, version, title }); toggleModal(); @@ -78,6 +84,15 @@ export function InstallationButton(props: InstallationButtonProps) { ); + const updateButton = ( + + + + ); + const uninstallButton = ( - {isInstalled || isRemoving ? uninstallButton : installButton} + {isUpdate ? updateButton : showUninstallButton ? uninstallButton : installButton} {isModalVisible && (isInstalled ? uninstallModal : installModal)}
) : null; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx index 3589a1a9444e1b..4d4dba2a64e5a6 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/settings_panel.tsx @@ -8,11 +8,22 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui'; +import styled from 'styled-components'; import { InstallStatus, PackageInfo } from '../../../../types'; import { useGetDatasources } from '../../../../hooks'; import { DATASOURCE_SAVED_OBJECT_TYPE } from '../../../../constants'; import { useGetPackageInstallStatus } from '../../hooks'; import { InstallationButton } from './installation_button'; +import { UpdateIcon } from '../../components/icons'; + +const SettingsTitleCell = styled.td` + padding-right: ${props => props.theme.eui.spacerSizes.xl}; + padding-bottom: ${props => props.theme.eui.spacerSizes.m}; +`; + +const UpdatesAvailableMsgContainer = styled.span` + padding-left: ${props => props.theme.eui.spacerSizes.s}; +`; const NoteLabel = () => ( ( defaultMessage="Note:" /> ); +const UpdatesAvailableMsg = () => ( + + + + +); + export const SettingsPanel = ( - props: Pick + props: Pick ) => { + const { name, title, removable, latestVersion, version } = props; const getPackageInstallStatus = useGetPackageInstallStatus(); const { data: datasourcesData } = useGetDatasources({ perPage: 0, page: 1, kuery: `${DATASOURCE_SAVED_OBJECT_TYPE}.package.name:${props.name}`, }); - const { name, title, removable } = props; - const packageInstallStatus = getPackageInstallStatus(name); + const { status: installationStatus, version: installedVersion } = getPackageInstallStatus(name); const packageHasDatasources = !!datasourcesData?.total; + const updateAvailable = installedVersion && installedVersion < latestVersion ? true : false; + const isViewingOldPackage = version < latestVersion; + // hide install/remove options if the user has version of the package is installed + // and this package is out of date or if they do have a version installed but it's not this one + const hideInstallOptions = + (installationStatus === InstallStatus.notInstalled && isViewingOldPackage) || + (installationStatus === InstallStatus.installed && installedVersion !== version); + + const isUpdating = installationStatus === InstallStatus.installing && installedVersion; return ( @@ -43,14 +73,13 @@ export const SettingsPanel = ( - {packageInstallStatus === InstallStatus.notInstalled || - packageInstallStatus === InstallStatus.installing ? ( + {installedVersion !== null && (

-

- -

+ + + + + + + + + + + + + + + +
+ + {installedVersion} + + {updateAvailable && } +
+ + {latestVersion} + +
+ {updateAvailable && ( +

+ +

+ )}

- ) : ( + )} + {!hideInstallOptions && !isUpdating && (
- -

+ + {installationStatus === InstallStatus.notInstalled || + installationStatus === InstallStatus.installing ? ( +
+ +

+ +

+
+ +

+ +

+
+ ) : ( +
+ +

+ +

+
+ +

+ +

+
+ )} + + +

+ +

+
+
+ {packageHasDatasources && removable === true && ( +

+ + + ), }} /> -

-
- -

- -

+

+ )} + {removable === false && ( +

+ + + + ), + }} + /> +

+ )}
)} - - -

- -

-
-
- {packageHasDatasources && removable === true && ( -

- - - - ), - }} - /> -

- )} - {removable === false && ( -

- - - - ), - }} - /> -

- )}
); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx index 05729ccfc1af42..ab168ef1530bd6 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx @@ -37,7 +37,7 @@ export function SideNavLinks({ name, version, active }: NavLinkProps) { : p.theme.eui.euiFontWeightRegular}; `; // don't display Data Sources tab if the package is not installed - if (packageInstallStatus !== InstallStatus.installed && panel === 'data-sources') + if (packageInstallStatus.status !== InstallStatus.installed && panel === 'data-sources') return null; return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx index 653e2eb9a3a3bb..b69dd6bcf84319 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx @@ -71,7 +71,7 @@ export const AgentDetailSection: React.FunctionComponent = ({ agent }) => // Fetch AgentConfig information const { isLoading: isAgentConfigLoading, data: agentConfigData } = useGetOneAgentConfig( - agent.config_id as string + agent.config_id ); const items = [ diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx index 9c14a2e9dfed13..dd34e7260b27b4 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx @@ -30,7 +30,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentConfigs = [], }) => { - const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(null); + const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(); return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/instructions.tsx index a0244c601cd963..1d2f3bd155622f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/instructions.tsx @@ -7,16 +7,15 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiText, EuiButtonGroup, EuiSteps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useEnrollmentApiKey } from '../enrollment_api_keys'; import { ShellEnrollmentInstructions, ManualInstructions, } from '../../../../../components/enrollment_instructions'; -import { useCore, useGetAgents } from '../../../../../hooks'; +import { useCore, useGetAgents, useGetOneEnrollmentAPIKey } from '../../../../../hooks'; import { Loading } from '../../../components'; interface Props { - selectedAPIKeyId: string | null; + selectedAPIKeyId: string | undefined; } function useNewEnrolledAgents() { // New enrolled agents @@ -44,7 +43,7 @@ export const EnrollmentInstructions: React.FunctionComponent = ({ selecte const core = useCore(); const [installType, setInstallType] = useState<'quickInstall' | 'manual'>('quickInstall'); - const apiKey = useEnrollmentApiKey(selectedAPIKeyId); + const apiKey = useGetOneEnrollmentAPIKey(selectedAPIKeyId); const newAgents = useNewEnrolledAgents(); if (!apiKey.data) { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/key_selection.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/key_selection.tsx index 89801bc6bee1ed..67930e51418b0c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/key_selection.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/key_selection.tsx @@ -16,17 +16,16 @@ import { EuiFieldText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useEnrollmentApiKeys } from '../enrollment_api_keys'; import { AgentConfig } from '../../../../../types'; -import { useInput, useCore, sendRequest } from '../../../../../hooks'; +import { useInput, useCore, sendRequest, useGetEnrollmentAPIKeys } from '../../../../../hooks'; import { enrollmentAPIKeyRouteService } from '../../../../../services'; interface Props { - onKeyChange: (keyId: string | null) => void; + onKeyChange: (keyId: string | undefined) => void; agentConfigs: AgentConfig[]; } -function useCreateApiKeyForm(configId: string | null, onSuccess: (keyId: string) => void) { +function useCreateApiKeyForm(configId: string | undefined, onSuccess: (keyId: string) => void) { const { notifications } = useCore(); const [isLoading, setIsLoading] = useState(false); const apiKeyNameInput = useInput(''); @@ -62,17 +61,16 @@ function useCreateApiKeyForm(configId: string | null, onSuccess: (keyId: string) } export const APIKeySelection: React.FunctionComponent = ({ onKeyChange, agentConfigs }) => { - const enrollmentAPIKeysRequest = useEnrollmentApiKeys({ - currentPage: 1, - pageSize: 1000, + const enrollmentAPIKeysRequest = useGetEnrollmentAPIKeys({ + page: 1, + perPage: 1000, }); const [selectedState, setSelectedState] = useState<{ - agentConfigId: string | null; - enrollmentAPIKeyId: string | null; + agentConfigId?: string; + enrollmentAPIKeyId?: string; }>({ - agentConfigId: agentConfigs.length ? agentConfigs[0].id : null, - enrollmentAPIKeyId: null, + agentConfigId: agentConfigs.length ? agentConfigs[0].id : undefined, }); const filteredEnrollmentAPIKeys = React.useMemo(() => { if (!selectedState.agentConfigId || !enrollmentAPIKeysRequest.data) { @@ -99,10 +97,10 @@ export const APIKeySelection: React.FunctionComponent = ({ onKeyChange, a const [showAPIKeyForm, setShowAPIKeyForm] = useState(false); const apiKeyForm = useCreateApiKeyForm(selectedState.agentConfigId, async (keyId: string) => { - const res = await enrollmentAPIKeysRequest.refresh(); + const res = await enrollmentAPIKeysRequest.sendRequest(); setSelectedState({ ...selectedState, - enrollmentAPIKeyId: res.data?.list.find(key => key.id === keyId)?.id ?? null, + enrollmentAPIKeyId: res.data?.list.find(key => key.id === keyId)?.id, }); setShowAPIKeyForm(false); }); @@ -135,7 +133,7 @@ export const APIKeySelection: React.FunctionComponent = ({ onKeyChange, a onChange={e => setSelectedState({ agentConfigId: e.target.value, - enrollmentAPIKeyId: null, + enrollmentAPIKeyId: undefined, }) } /> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/confirm_delete_modal.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/confirm_delete_modal.tsx deleted file mode 100644 index 8ce20a85e14b82..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/confirm_delete_modal.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 React from 'react'; -import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -export const ConfirmDeleteModal: React.FunctionComponent<{ - onConfirm: () => void; - onCancel: () => void; - apiKeyId: string; -}> = ({ onConfirm, onCancel, apiKeyId }) => { - return ( - - - } - onCancel={onCancel} - onConfirm={onConfirm} - cancelButtonText={ - - } - confirmButtonText={ - - } - buttonColor="danger" - /> - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/create_api_key_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/create_api_key_form.tsx deleted file mode 100644 index 009080a4da1860..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/create_api_key_form.tsx +++ /dev/null @@ -1,94 +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 { - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiFieldText, - EuiButton, - EuiSelect, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useInput, sendRequest } from '../../../../../hooks'; -import { useConfigs } from './hooks'; -import { enrollmentAPIKeyRouteService } from '../../../../../services'; - -export const CreateApiKeyForm: React.FunctionComponent<{ onChange: () => void }> = ({ - onChange, -}) => { - const { data: configs } = useConfigs(); - const { inputs, onSubmit, submitted } = useCreateApiKey(() => onChange()); - - return ( - - - - - - - - - ({ - value: config.id, - text: config.name, - }))} - /> - - - - - onSubmit()}> - - - - - - ); -}; - -function useCreateApiKey(onSuccess: () => void) { - const [submitted, setSubmitted] = React.useState(false); - const inputs = { - nameInput: useInput(), - configIdInput: useInput('default'), - }; - - const onSubmit = async () => { - setSubmitted(true); - await sendRequest({ - method: 'post', - path: enrollmentAPIKeyRouteService.getCreatePath(), - body: JSON.stringify({ - name: inputs.nameInput.value, - config_id: inputs.configIdInput.value, - }), - }); - setSubmitted(false); - onSuccess(); - }; - - return { - inputs, - onSubmit, - submitted, - }; -} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/hooks.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/hooks.tsx deleted file mode 100644 index 41c6b5912cd319..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/hooks.tsx +++ /dev/null @@ -1,43 +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 { - Pagination, - useGetAgentConfigs, - useGetEnrollmentAPIKeys, - useGetOneEnrollmentAPIKey, -} from '../../../../../hooks'; - -export function useEnrollmentApiKeys(pagination: Pagination) { - const request = useGetEnrollmentAPIKeys({ - page: pagination.currentPage, - perPage: pagination.pageSize, - }); - - return { - data: request.data, - isLoading: request.isLoading, - refresh: () => request.sendRequest(), - }; -} - -export function useConfigs() { - const request = useGetAgentConfigs(); - - return { - data: request.data ? request.data.items : [], - isLoading: request.isLoading, - }; -} - -export function useEnrollmentApiKey(apiKeyId: string | null) { - const request = useGetOneEnrollmentAPIKey(apiKeyId as string); - - return { - data: request.data, - isLoading: request.isLoading, - }; -} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/index.tsx deleted file mode 100644 index 19957e7827680b..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/enrollment_api_keys/index.tsx +++ /dev/null @@ -1,152 +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 { EuiBasicTable, EuiButtonEmpty, EuiSpacer, EuiPopover, EuiLink } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; -import { usePagination, sendRequest } from '../../../../../hooks'; -import { useEnrollmentApiKeys, useEnrollmentApiKey } from './hooks'; -import { ConfirmDeleteModal } from './confirm_delete_modal'; -import { CreateApiKeyForm } from './create_api_key_form'; -import { EnrollmentAPIKey } from '../../../../../types'; -import { useCapabilities } from '../../../../../hooks'; -import { enrollmentAPIKeyRouteService } from '../../../../../services'; -export { useEnrollmentApiKeys, useEnrollmentApiKey } from './hooks'; - -export const EnrollmentApiKeysTable: React.FunctionComponent<{ - onChange: () => void; -}> = ({ onChange }) => { - const [confirmDeleteApiKeyId, setConfirmDeleteApiKeyId] = useState(null); - const { pagination } = usePagination(); - const { data, isLoading, refresh } = useEnrollmentApiKeys(pagination); - - const columns: any[] = [ - { - field: 'name', - name: i18n.translate('xpack.ingestManager.apiKeysList.nameColumnTitle', { - defaultMessage: 'Name', - }), - width: '300px', - }, - { - field: 'config_id', - name: i18n.translate('xpack.ingestManager.apiKeysList.configColumnTitle', { - defaultMessage: 'Config', - }), - width: '100px', - }, - { - field: null, - name: i18n.translate('xpack.ingestManager.apiKeysList.apiKeyColumnTitle', { - defaultMessage: 'API Key', - }), - render: (key: EnrollmentAPIKey) => , - }, - { - field: null, - width: '50px', - render: (key: EnrollmentAPIKey) => { - return ( - setConfirmDeleteApiKeyId(key.id)} iconType={'trash'} /> - ); - }, - }, - ]; - - return ( - <> - {confirmDeleteApiKeyId && ( - setConfirmDeleteApiKeyId(null)} - onConfirm={async () => { - await sendRequest({ - method: 'delete', - path: enrollmentAPIKeyRouteService.getDeletePath(confirmDeleteApiKeyId), - }); - setConfirmDeleteApiKeyId(null); - refresh(); - }} - /> - )} - - } - items={data ? data.list : []} - itemId="id" - columns={columns} - /> - - { - refresh(); - onChange(); - }} - /> - - ); -}; - -export const CreateApiKeyButton: React.FunctionComponent<{ onChange: () => void }> = ({ - onChange, -}) => { - const hasWriteCapabilites = useCapabilities().write; - const [isOpen, setIsOpen] = React.useState(false); - - return ( - setIsOpen(true)} color="primary"> - - - } - isOpen={isOpen} - closePopover={() => setIsOpen(false)} - > - { - setIsOpen(false); - onChange(); - }} - /> - - ); - return <>; -}; - -const ApiKeyField: React.FunctionComponent<{ apiKeyId: string }> = ({ apiKeyId }) => { - const [visible, setVisible] = useState(false); - const { data } = useEnrollmentApiKey(apiKeyId); - - return ( - <> - {visible && data ? data.item.api_key : '••••••••••••••••••••••••••••'} - setVisible(!visible)}> - {visible ? ( - - ) : ( - - )} - {' '} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx index 11a049047b7877..692c60cdce38c1 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_reassign_config_flyout/index.tsx @@ -45,7 +45,7 @@ export const AgentReassignConfigFlyout: React.FunctionComponent = ({ onCl const agentConfigsRequest = useGetAgentConfigs(); const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : []; - const agentConfigRequest = useGetOneAgentConfig(selectedAgentConfigId as string); + const agentConfigRequest = useGetOneAgentConfig(selectedAgentConfigId); const agentConfig = agentConfigRequest.data ? agentConfigRequest.data.item : null; const [isSubmitting, setIsSubmitting] = useState(false); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx index 05d150fd9ae231..70d8e7d6882f88 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx @@ -8,6 +8,7 @@ import styled from 'styled-components'; import { EuiButton, EuiButtonEmpty, + EuiBetaBadge, EuiPanel, EuiText, EuiTitle, @@ -19,10 +20,11 @@ import { EuiFlexItem, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { WithHeaderLayout } from '../../layouts'; import { useLink, useGetAgentConfigs } from '../../hooks'; import { AgentEnrollmentFlyout } from '../fleet/agent_list_page/components'; -import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../../constants'; +import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH, DATA_STREAM_PATH } from '../../constants'; const OverviewPanel = styled(EuiPanel).attrs(props => ({ paddingSize: 'm', @@ -57,6 +59,11 @@ const OverviewStats = styled(EuiDescriptionList).attrs(props => ({ } `; +const AlphaBadge = styled(EuiBetaBadge)` + vertical-align: top; + margin-left: ${props => props.theme.eui.paddingSizes.s}; +`; + export const IngestManagerOverview: React.FunctionComponent = () => { // Agent enrollment flyout state const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); @@ -79,6 +86,19 @@ export const IngestManagerOverview: React.FunctionComponent = () => { id="xpack.ingestManager.overviewPageTitle" defaultMessage="Ingest Manager" /> +
@@ -213,7 +233,7 @@ export const IngestManagerOverview: React.FunctionComponent = () => { /> - + + (agentConfig: GetAgentConfigsResponseItem) => listAgents(soClient, { showInactive: true, perPage: 0, diff --git a/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts index 56d6053a1451b7..8f07e3ed1de022 100644 --- a/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/datasource/handlers.ts @@ -7,7 +7,7 @@ import { TypeOf } from '@kbn/config-schema'; import Boom from 'boom'; import { RequestHandler } from 'src/core/server'; import { appContextService, datasourceService } from '../../services'; -import { ensureInstalledPackage } from '../../services/epm/packages'; +import { ensureInstalledPackage, getPackageInfo } from '../../services/epm/packages'; import { GetDatasourcesRequestSchema, GetOneDatasourceRequestSchema, @@ -85,12 +85,13 @@ export const createDatasourceHandler: RequestHandler< pkgName: request.body.package.name, callCluster, }); - + const pkgInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: request.body.package.name, + pkgVersion: request.body.package.version, + }); newData.inputs = (await datasourceService.assignPackageStream( - { - pkgName: request.body.package.name, - pkgVersion: request.body.package.version, - }, + pkgInfo, request.body.inputs )) as TypeOf['inputs']; } @@ -127,13 +128,14 @@ export const updateDatasourceHandler: RequestHandler< const pkg = newData.package || datasource.package; const inputs = newData.inputs || datasource.inputs; if (pkg && (newData.inputs || newData.package)) { - newData.inputs = (await datasourceService.assignPackageStream( - { - pkgName: pkg.name, - pkgVersion: pkg.version, - }, - inputs - )) as TypeOf['inputs']; + const pkgInfo = await getPackageInfo({ + savedObjectsClient: soClient, + pkgName: pkg.name, + pkgVersion: pkg.version, + }); + newData.inputs = (await datasourceService.assignPackageStream(pkgInfo, inputs)) as TypeOf< + typeof CreateDatasourceRequestSchema.body + >['inputs']; } const updatedDatasource = await datasourceService.update( diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 882258e859555b..90fe68e61bb1b4 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -128,6 +128,7 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { updated_on: { type: 'keyword' }, updated_by: { type: 'keyword' }, revision: { type: 'integer' }, + monitoring_enabled: { type: 'keyword' }, }, }, }, diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.test.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.test.ts new file mode 100644 index 00000000000000..17758f6e3d7f12 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.test.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { agentConfigService } from './agent_config'; +import { Output } from '../types'; + +function getSavedObjectMock(configAttributes: any) { + const mock = savedObjectsClientMock.create(); + + mock.get.mockImplementation(async (type: string, id: string) => { + return { + type, + id, + references: [], + attributes: configAttributes, + }; + }); + + return mock; +} + +jest.mock('./output', () => { + return { + outputService: { + getDefaultOutputId: () => 'test-id', + get: (): Output => { + return { + id: 'test-id', + is_default: true, + name: 'default', + // @ts-ignore + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }; + }, + }, + }; +}); + +describe('agent config', () => { + describe('getFullConfig', () => { + it('should return a config without monitoring if not monitoring is not enabled', async () => { + const soClient = getSavedObjectMock({ + revision: 1, + }); + const config = await agentConfigService.getFullConfig(soClient, 'config'); + + expect(config).toMatchObject({ + id: 'config', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + datasources: [], + revision: 1, + settings: { + monitoring: { + enabled: false, + logs: false, + metrics: false, + }, + }, + }); + }); + + it('should return a config with monitoring if monitoring is enabled for logs', async () => { + const soClient = getSavedObjectMock({ + revision: 1, + monitoring_enabled: ['logs'], + }); + const config = await agentConfigService.getFullConfig(soClient, 'config'); + + expect(config).toMatchObject({ + id: 'config', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + datasources: [], + revision: 1, + settings: { + monitoring: { + use_output: 'default', + enabled: true, + logs: true, + metrics: false, + }, + }, + }); + }); + + it('should return a config with monitoring if monitoring is enabled for metrics', async () => { + const soClient = getSavedObjectMock({ + revision: 1, + monitoring_enabled: ['metrics'], + }); + const config = await agentConfigService.getFullConfig(soClient, 'config'); + + expect(config).toMatchObject({ + id: 'config', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + ca_sha256: undefined, + api_key: undefined, + }, + }, + datasources: [], + revision: 1, + settings: { + monitoring: { + use_output: 'default', + enabled: true, + logs: false, + metrics: true, + }, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index 75bbfc21293c2b..7ab6ef1920c18a 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -301,28 +301,49 @@ class AgentConfigService { if (!config) { return null; } + const defaultOutput = await outputService.get( + soClient, + await outputService.getDefaultOutputId(soClient) + ); const agentConfig: FullAgentConfig = { id: config.id, outputs: { // TEMPORARY as we only support a default output - ...[ - await outputService.get(soClient, await outputService.getDefaultOutputId(soClient)), - ].reduce((outputs, { config: outputConfig, name, type, hosts, ca_sha256, api_key }) => { - outputs[name] = { - type, - hosts, - ca_sha256, - api_key, - ...outputConfig, - }; - return outputs; - }, {} as FullAgentConfig['outputs']), + ...[defaultOutput].reduce( + (outputs, { config: outputConfig, name, type, hosts, ca_sha256, api_key }) => { + outputs[name] = { + type, + hosts, + ca_sha256, + api_key, + ...outputConfig, + }; + return outputs; + }, + {} as FullAgentConfig['outputs'] + ), }, datasources: (config.datasources as Datasource[]) .filter(datasource => datasource.enabled) .map(ds => storedDatasourceToAgentDatasource(ds)), revision: config.revision, + ...(config.monitoring_enabled && config.monitoring_enabled.length > 0 + ? { + settings: { + monitoring: { + use_output: defaultOutput.name, + enabled: true, + logs: config.monitoring_enabled.indexOf('logs') >= 0, + metrics: config.monitoring_enabled.indexOf('metrics') >= 0, + }, + }, + } + : { + settings: { + monitoring: { enabled: false, logs: false, metrics: false }, + }, + }), }; return agentConfig; diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.test.ts b/x-pack/plugins/ingest_manager/server/services/datasource.test.ts index 4cbbadce7f5bb9..3682ae6d1167b9 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.test.ts @@ -5,39 +5,38 @@ */ import { datasourceService } from './datasource'; +import { PackageInfo } from '../types'; -async function mockedGetAssetsData(_a: any, _b: any, dataset: string) { - if (dataset === 'dataset1') { - return [ - { - buffer: Buffer.from(` +const TEMPLATE = ` type: log metricset: ["dataset1"] paths: {{#each paths}} - {{this}} {{/each}} -`), - }, - ]; - } - return []; -} - -jest.mock('./epm/packages/assets', () => { - return { - getAssetsDataForPackageKey: mockedGetAssetsData, - }; -}); +`; describe('Datasource service', () => { describe('assignPackageStream', () => { - it('should work with cofig variables from the stream', async () => { + it('should work with config variables from the stream', async () => { const inputs = await datasourceService.assignPackageStream( - { - pkgName: 'package', - pkgVersion: '1.0.0', - }, + ({ + datasources: [ + { + inputs: [ + { + type: 'log', + streams: [ + { + dataset: 'package.dataset1', + template: TEMPLATE, + }, + ], + }, + ], + }, + ], + } as unknown) as PackageInfo, [ { type: 'log', @@ -85,10 +84,23 @@ describe('Datasource service', () => { it('should work with config variables at the input level', async () => { const inputs = await datasourceService.assignPackageStream( - { - pkgName: 'package', - pkgVersion: '1.0.0', - }, + ({ + datasources: [ + { + inputs: [ + { + type: 'log', + streams: [ + { + dataset: 'package.dataset1', + template: TEMPLATE, + }, + ], + }, + ], + }, + ], + } as unknown) as PackageInfo, [ { type: 'log', diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index 804039cf508ba3..0a5ba43e40fba9 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -4,20 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ import { SavedObjectsClientContract } from 'src/core/server'; -import { safeLoad } from 'js-yaml'; import { AuthenticatedUser } from '../../../security/server'; import { DeleteDatasourcesResponse, packageToConfigDatasource, DatasourceInput, DatasourceInputStream, + PackageInfo, } from '../../common'; import { DATASOURCE_SAVED_OBJECT_TYPE } from '../constants'; import { NewDatasource, Datasource, ListWithKuery } from '../types'; import { agentConfigService } from './agent_config'; import { getPackageInfo, getInstallation } from './epm/packages'; import { outputService } from './output'; -import { getAssetsDataForPackageKey } from './epm/packages/assets'; import { createStream } from './epm/agent/agent'; const SAVED_OBJECT_TYPE = DATASOURCE_SAVED_OBJECT_TYPE; @@ -201,20 +200,16 @@ class DatasourceService { } public async assignPackageStream( - pkgInfo: { pkgName: string; pkgVersion: string }, + pkgInfo: PackageInfo, inputs: DatasourceInput[] ): Promise { const inputsPromises = inputs.map(input => _assignPackageStreamToInput(pkgInfo, input)); + return Promise.all(inputsPromises); } } -const _isAgentStream = (p: string) => !!p.match(/agent\/stream\/stream\.yml/); - -async function _assignPackageStreamToInput( - pkgInfo: { pkgName: string; pkgVersion: string }, - input: DatasourceInput -) { +async function _assignPackageStreamToInput(pkgInfo: PackageInfo, input: DatasourceInput) { const streamsPromises = input.streams.map(stream => _assignPackageStreamToStream(pkgInfo, input, stream) ); @@ -224,7 +219,7 @@ async function _assignPackageStreamToInput( } async function _assignPackageStreamToStream( - pkgInfo: { pkgName: string; pkgVersion: string }, + pkgInfo: PackageInfo, input: DatasourceInput, stream: DatasourceInputStream ) { @@ -232,27 +227,35 @@ async function _assignPackageStreamToStream( return { ...stream, agent_stream: undefined }; } const dataset = getDataset(stream.dataset); - const assetsData = await getAssetsDataForPackageKey(pkgInfo, _isAgentStream, dataset); + const datasource = pkgInfo.datasources?.[0]; + if (!datasource) { + throw new Error('Stream template not found, no datasource'); + } - const [pkgStream] = assetsData; - if (!pkgStream || !pkgStream.buffer) { - throw new Error(`Stream template not found for dataset ${dataset}`); + const inputFromPkg = datasource.inputs.find(pkgInput => pkgInput.type === input.type); + if (!inputFromPkg) { + throw new Error(`Stream template not found, unable to found input ${input.type}`); } - // Populate template variables from input config and stream config - const data: { [k: string]: string | string[] } = {}; - if (input.vars) { - for (const key of Object.keys(input.vars)) { - data[key] = input.vars[key].value; - } + const streamFromPkg = inputFromPkg.streams.find( + pkgStream => pkgStream.dataset === stream.dataset + ); + if (!streamFromPkg) { + throw new Error(`Stream template not found, unable to found stream ${stream.dataset}`); } - if (stream.vars) { - for (const key of Object.keys(stream.vars)) { - data[key] = stream.vars[key].value; - } + + if (!streamFromPkg.template) { + throw new Error(`Stream template not found for dataset ${dataset}`); } - const yaml = safeLoad(createStream(data, pkgStream.buffer.toString())); + + const yaml = createStream( + // Populate template variables from input vars and stream vars + Object.assign({}, input.vars, stream.vars), + streamFromPkg.template + ); + stream.agent_stream = yaml; + return { ...stream }; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts index 21de625532f038..db2e4fe4746406 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.test.ts @@ -6,29 +6,61 @@ import { createStream } from './agent'; -test('Test creating a stream from template', () => { - const streamTemplate = ` -input: log -paths: -{{#each paths}} - - {{this}} -{{/each}} -exclude_files: [".gz$"] -processors: - - add_locale: ~ - `; - const vars = { - paths: ['/usr/local/var/log/nginx/access.log'], - }; +describe('createStream', () => { + it('should work', () => { + const streamTemplate = ` + input: log + paths: + {{#each paths}} + - {{this}} + {{/each}} + exclude_files: [".gz$"] + processors: + - add_locale: ~ + `; + const vars = { + paths: { value: ['/usr/local/var/log/nginx/access.log'] }, + }; - const output = createStream(vars, streamTemplate); + const output = createStream(vars, streamTemplate); + expect(output).toEqual({ + input: 'log', + paths: ['/usr/local/var/log/nginx/access.log'], + exclude_files: ['.gz$'], + processors: [{ add_locale: null }], + }); + }); - expect(output).toBe(` -input: log -paths: - - /usr/local/var/log/nginx/access.log -exclude_files: [".gz$"] -processors: - - add_locale: ~ - `); + it('should support yaml values', () => { + const streamTemplate = ` + input: redis/metrics + metricsets: ["key"] + test: null + {{#if key.patterns}} + key.patterns: {{key.patterns}} + {{/if}} + `; + const vars = { + 'key.patterns': { + type: 'yaml', + value: ` + - limit: 20 + pattern: '*' + `, + }, + }; + + const output = createStream(vars, streamTemplate); + expect(output).toEqual({ + input: 'redis/metrics', + metricsets: ['key'], + test: null, + 'key.patterns': [ + { + limit: 20, + pattern: '*', + }, + ], + }); + }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts index 5d9a6d409aa1a0..8254c0d8aaa378 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/agent/agent.ts @@ -5,12 +5,71 @@ */ import Handlebars from 'handlebars'; +import { safeLoad } from 'js-yaml'; +import { DatasourceConfigRecord } from '../../../../common'; -interface StreamVars { - [k: string]: string | string[]; +function isValidKey(key: string) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; } -export function createStream(vars: StreamVars, streamTemplate: string) { - const template = Handlebars.compile(streamTemplate); - return template(vars); +function replaceVariablesInYaml(yamlVariables: { [k: string]: any }, yaml: any) { + if (Object.keys(yamlVariables).length === 0 || !yaml) { + return yaml; + } + + Object.entries(yaml).forEach(([key, value]: [string, any]) => { + if (typeof value === 'object') { + yaml[key] = replaceVariablesInYaml(yamlVariables, value); + } + if (typeof value === 'string' && value in yamlVariables) { + yaml[key] = yamlVariables[value]; + } + }); + + return yaml; +} + +function buildTemplateVariables(variables: DatasourceConfigRecord) { + const yamlValues: { [k: string]: any } = {}; + const vars = Object.entries(variables).reduce((acc, [key, recordEntry]) => { + // support variables with . like key.patterns + const keyParts = key.split('.'); + const lastKeyPart = keyParts.pop(); + + if (!lastKeyPart || !isValidKey(lastKeyPart)) { + throw new Error('Invalid key'); + } + + let varPart = acc; + for (const keyPart of keyParts) { + if (!isValidKey(keyPart)) { + throw new Error('Invalid key'); + } + if (!varPart[keyPart]) { + varPart[keyPart] = {}; + } + varPart = varPart[keyPart]; + } + + if (recordEntry.type && recordEntry.type === 'yaml') { + const yamlKeyPlaceholder = `##${key}##`; + varPart[lastKeyPart] = `"${yamlKeyPlaceholder}"`; + yamlValues[yamlKeyPlaceholder] = recordEntry.value ? safeLoad(recordEntry.value) : null; + } else { + varPart[lastKeyPart] = recordEntry.value; + } + return acc; + }, {} as { [k: string]: any }); + + return { vars, yamlValues }; +} + +export function createStream(variables: DatasourceConfigRecord, streamTemplate: string) { + const { vars, yamlValues } = buildTemplateVariables(variables); + + const template = Handlebars.compile(streamTemplate, { noEscape: true }); + const stream = template(vars); + const yamlFromStream = safeLoad(stream, {}); + + return replaceVariablesInYaml(yamlValues, yamlFromStream); } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap index 5cf1f241a709fe..440060aff96169 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/__snapshots__/template.test.ts.snap @@ -2,850 +2,856 @@ exports[`tests loading base.yml: base.yml 1`] = ` { - "order": 1, + "priority": 1, "index_patterns": [ "foo-*" ], - "settings": { - "index": { - "lifecycle": { - "name": "logs-default" - }, - "codec": "best_compression", - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "refresh_interval": "5s", - "number_of_shards": "1", - "query": { - "default_field": [ - "message" - ] - }, - "number_of_routing_shards": "30" - } - }, - "mappings": { - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } + "template": { + "settings": { + "index": { + "lifecycle": { + "name": "logs-default" + }, + "codec": "best_compression", + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "refresh_interval": "5s", + "number_of_shards": "1", + "query": { + "default_field": [ + "message" + ] + }, + "number_of_routing_shards": "30" } - ], - "date_detection": false, - "properties": { - "user": { - "properties": { - "auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "euid": { - "ignore_above": 1024, - "type": "keyword" + }, + "mappings": { + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" } } - }, - "long": { - "properties": { - "nested": { - "properties": { - "foo": { - "type": "text" - }, - "bar": { - "type": "long" + ], + "date_detection": false, + "properties": { + "user": { + "properties": { + "auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "euid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "long": { + "properties": { + "nested": { + "properties": { + "foo": { + "type": "text" + }, + "bar": { + "type": "long" + } } } } - } - }, - "nested": { - "properties": { - "bar": { - "ignore_above": 1024, - "type": "keyword" - }, - "baz": { - "ignore_above": 1024, - "type": "keyword" + }, + "nested": { + "properties": { + "bar": { + "ignore_above": 1024, + "type": "keyword" + }, + "baz": { + "ignore_above": 1024, + "type": "keyword" + } } + }, + "myalias": { + "type": "alias", + "path": "user.euid" + }, + "validarray": { + "type": "integer" } - }, - "myalias": { - "type": "alias", - "path": "user.euid" - }, - "validarray": { - "type": "integer" } - } - }, - "aliases": {} + }, + "aliases": {} + } } `; exports[`tests loading coredns.logs.yml: coredns.logs.yml 1`] = ` { - "order": 1, + "priority": 1, "index_patterns": [ "foo-*" ], - "settings": { - "index": { - "lifecycle": { - "name": "logs-default" - }, - "codec": "best_compression", - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "refresh_interval": "5s", - "number_of_shards": "1", - "query": { - "default_field": [ - "message" - ] - }, - "number_of_routing_shards": "30" - } - }, - "mappings": { - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } + "template": { + "settings": { + "index": { + "lifecycle": { + "name": "logs-default" + }, + "codec": "best_compression", + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "refresh_interval": "5s", + "number_of_shards": "1", + "query": { + "default_field": [ + "message" + ] + }, + "number_of_routing_shards": "30" } - ], - "date_detection": false, - "properties": { - "coredns": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "query": { - "properties": { - "size": { - "type": "long" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" + }, + "mappings": { + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "date_detection": false, + "properties": { + "coredns": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "query": { + "properties": { + "size": { + "type": "long" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } } - } - }, - "response": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" + }, + "response": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + } } + }, + "dnssec_ok": { + "type": "boolean" } - }, - "dnssec_ok": { - "type": "boolean" } } } - } - }, - "aliases": {} + }, + "aliases": {} + } } `; exports[`tests loading system.yml: system.yml 1`] = ` { - "order": 1, + "priority": 1, "index_patterns": [ "whatsthis-*" ], - "settings": { - "index": { - "lifecycle": { - "name": "metrics-default" - }, - "codec": "best_compression", - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "refresh_interval": "5s", - "number_of_shards": "1", - "query": { - "default_field": [ - "message" - ] - }, - "number_of_routing_shards": "30" - } - }, - "mappings": { - "dynamic_templates": [ - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } + "template": { + "settings": { + "index": { + "lifecycle": { + "name": "metrics-default" + }, + "codec": "best_compression", + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "refresh_interval": "5s", + "number_of_shards": "1", + "query": { + "default_field": [ + "message" + ] + }, + "number_of_routing_shards": "30" } - ], - "date_detection": false, - "properties": { - "system": { - "properties": { - "core": { - "properties": { - "id": { - "type": "long" - }, - "user": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "mappings": { + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "date_detection": false, + "properties": { + "system": { + "properties": { + "core": { + "properties": { + "id": { + "type": "long" + }, + "user": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "system": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "system": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "nice": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "nice": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "idle": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "idle": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "iowait": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "iowait": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "irq": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "irq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "softirq": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "softirq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } - } - }, - "steal": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "ticks": { - "type": "long" + }, + "steal": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "ticks": { + "type": "long" + } } } } - } - }, - "cpu": { - "properties": { - "cores": { - "type": "long" - }, - "user": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "cpu": { + "properties": { + "cores": { + "type": "long" + }, + "user": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "system": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "system": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "nice": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "nice": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "idle": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "idle": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "iowait": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "iowait": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "irq": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "irq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "softirq": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "softirq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "steal": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "steal": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "total": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "total": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } } } } } - } - }, - "diskio": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "read": { - "properties": { - "count": { - "type": "long" - }, - "bytes": { - "type": "long" - }, - "time": { - "type": "long" + }, + "diskio": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "read": { + "properties": { + "count": { + "type": "long" + }, + "bytes": { + "type": "long" + }, + "time": { + "type": "long" + } } - } - }, - "write": { - "properties": { - "count": { - "type": "long" - }, - "bytes": { - "type": "long" - }, - "time": { - "type": "long" + }, + "write": { + "properties": { + "count": { + "type": "long" + }, + "bytes": { + "type": "long" + }, + "time": { + "type": "long" + } } - } - }, - "io": { - "properties": { - "time": { - "type": "long" + }, + "io": { + "properties": { + "time": { + "type": "long" + } } - } - }, - "iostat": { - "properties": { - "read": { - "properties": { - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" + }, + "iostat": { + "properties": { + "read": { + "properties": { + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } } - } - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } } + }, + "await": { + "type": "float" } - }, - "await": { - "type": "float" } - } - }, - "write": { - "properties": { - "request": { - "properties": { - "merges_per_sec": { - "type": "float" - }, - "per_sec": { - "type": "float" + }, + "write": { + "properties": { + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } } - } - }, - "per_sec": { - "properties": { - "bytes": { - "type": "float" + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } } + }, + "await": { + "type": "float" } - }, - "await": { - "type": "float" } - } - }, - "request": { - "properties": { - "avg_size": { - "type": "float" + }, + "request": { + "properties": { + "avg_size": { + "type": "float" + } } - } - }, - "queue": { - "properties": { - "avg_size": { - "type": "float" + }, + "queue": { + "properties": { + "avg_size": { + "type": "float" + } } + }, + "await": { + "type": "float" + }, + "service_time": { + "type": "float" + }, + "busy": { + "type": "float" } - }, - "await": { - "type": "float" - }, - "service_time": { - "type": "float" - }, - "busy": { - "type": "float" } } } - } - }, - "entropy": { - "properties": { - "available_bits": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "entropy": { + "properties": { + "available_bits": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } - } - }, - "filesystem": { - "properties": { - "available": { - "type": "long" - }, - "device_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mount_point": { - "ignore_above": 1024, - "type": "keyword" - }, - "files": { - "type": "long" - }, - "free": { - "type": "long" - }, - "free_files": { - "type": "long" - }, - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "filesystem": { + "properties": { + "available": { + "type": "long" + }, + "device_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mount_point": { + "ignore_above": 1024, + "type": "keyword" + }, + "files": { + "type": "long" + }, + "free": { + "type": "long" + }, + "free_files": { + "type": "long" + }, + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } } } - } - }, - "fsstat": { - "properties": { - "count": { - "type": "long" - }, - "total_files": { - "type": "long" - }, - "total_size": { - "properties": { - "free": { - "type": "long" - }, - "used": { - "type": "long" - }, - "total": { - "type": "long" + }, + "fsstat": { + "properties": { + "count": { + "type": "long" + }, + "total_files": { + "type": "long" + }, + "total_size": { + "properties": { + "free": { + "type": "long" + }, + "used": { + "type": "long" + }, + "total": { + "type": "long" + } } } } - } - }, - "load": { - "properties": { - "1": { - "type": "scaled_float", - "scaling_factor": 100 - }, - "5": { - "type": "scaled_float", - "scaling_factor": 100 - }, - "15": { - "type": "scaled_float", - "scaling_factor": 100 - }, - "norm": { - "properties": { - "1": { - "type": "scaled_float", - "scaling_factor": 100 - }, - "5": { - "type": "scaled_float", - "scaling_factor": 100 - }, - "15": { - "type": "scaled_float", - "scaling_factor": 100 + }, + "load": { + "properties": { + "1": { + "type": "scaled_float", + "scaling_factor": 100 + }, + "5": { + "type": "scaled_float", + "scaling_factor": 100 + }, + "15": { + "type": "scaled_float", + "scaling_factor": 100 + }, + "norm": { + "properties": { + "1": { + "type": "scaled_float", + "scaling_factor": 100 + }, + "5": { + "type": "scaled_float", + "scaling_factor": 100 + }, + "15": { + "type": "scaled_float", + "scaling_factor": 100 + } } + }, + "cores": { + "type": "long" } - }, - "cores": { - "type": "long" } - } - }, - "memory": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "memory": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } - } - }, - "free": { - "type": "long" - }, - "actual": { - "properties": { - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "free": { + "type": "long" + }, + "actual": { + "properties": { + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "free": { + "type": "long" } - }, - "free": { - "type": "long" } - } - }, - "swap": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "swap": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } - } - }, - "free": { - "type": "long" - }, - "out": { - "properties": { - "pages": { - "type": "long" + }, + "free": { + "type": "long" + }, + "out": { + "properties": { + "pages": { + "type": "long" + } } - } - }, - "in": { - "properties": { - "pages": { - "type": "long" + }, + "in": { + "properties": { + "pages": { + "type": "long" + } } - } - }, - "readahead": { - "properties": { - "pages": { - "type": "long" - }, - "cached": { - "type": "long" + }, + "readahead": { + "properties": { + "pages": { + "type": "long" + }, + "cached": { + "type": "long" + } } } } - } - }, - "hugepages": { - "properties": { - "total": { - "type": "long" - }, - "used": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "long" + }, + "hugepages": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "long" + } } - } - }, - "free": { - "type": "long" - }, - "reserved": { - "type": "long" - }, - "surplus": { - "type": "long" - }, - "default_size": { - "type": "long" - }, - "swap": { - "properties": { - "out": { - "properties": { - "pages": { - "type": "long" - }, - "fallback": { - "type": "long" + }, + "free": { + "type": "long" + }, + "reserved": { + "type": "long" + }, + "surplus": { + "type": "long" + }, + "default_size": { + "type": "long" + }, + "swap": { + "properties": { + "out": { + "properties": { + "pages": { + "type": "long" + }, + "fallback": { + "type": "long" + } } } } @@ -853,743 +859,743 @@ exports[`tests loading system.yml: system.yml 1`] = ` } } } - } - }, - "network": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "out": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "dropped": { - "type": "long" + }, + "network": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "dropped": { + "type": "long" + } } - } - }, - "in": { - "properties": { - "bytes": { - "type": "long" - }, - "packets": { - "type": "long" - }, - "errors": { - "type": "long" - }, - "dropped": { - "type": "long" + }, + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "dropped": { + "type": "long" + } } } } - } - }, - "network_summary": { - "properties": { - "ip": { - "properties": { - "*": { - "type": "object" + }, + "network_summary": { + "properties": { + "ip": { + "properties": { + "*": { + "type": "object" + } } - } - }, - "tcp": { - "properties": { - "*": { - "type": "object" + }, + "tcp": { + "properties": { + "*": { + "type": "object" + } } - } - }, - "udp": { - "properties": { - "*": { - "type": "object" + }, + "udp": { + "properties": { + "*": { + "type": "object" + } } - } - }, - "udp_lite": { - "properties": { - "*": { - "type": "object" + }, + "udp_lite": { + "properties": { + "*": { + "type": "object" + } } - } - }, - "icmp": { - "properties": { - "*": { - "type": "object" + }, + "icmp": { + "properties": { + "*": { + "type": "object" + } } } } - } - }, - "process": { - "properties": { - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmdline": { - "ignore_above": 2048, - "type": "keyword" - }, - "env": { - "type": "object" - }, - "cpu": { - "properties": { - "user": { - "properties": { - "ticks": { - "type": "long" + }, + "process": { + "properties": { + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmdline": { + "ignore_above": 2048, + "type": "keyword" + }, + "env": { + "type": "object" + }, + "cpu": { + "properties": { + "user": { + "properties": { + "ticks": { + "type": "long" + } } - } - }, - "total": { - "properties": { - "value": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 - }, - "norm": { - "properties": { - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "total": { + "properties": { + "value": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "ticks": { + "type": "long" } - }, - "ticks": { - "type": "long" } - } - }, - "system": { - "properties": { - "ticks": { - "type": "long" + }, + "system": { + "properties": { + "ticks": { + "type": "long" + } } + }, + "start_time": { + "type": "date" } - }, - "start_time": { - "type": "date" } - } - }, - "memory": { - "properties": { - "size": { - "type": "long" - }, - "rss": { - "properties": { - "bytes": { - "type": "long" - }, - "pct": { - "type": "scaled_float", - "scaling_factor": 1000 + }, + "memory": { + "properties": { + "size": { + "type": "long" + }, + "rss": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } } + }, + "share": { + "type": "long" } - }, - "share": { - "type": "long" } - } - }, - "fd": { - "properties": { - "open": { - "type": "long" - }, - "limit": { - "properties": { - "soft": { - "type": "long" - }, - "hard": { - "type": "long" + }, + "fd": { + "properties": { + "open": { + "type": "long" + }, + "limit": { + "properties": { + "soft": { + "type": "long" + }, + "hard": { + "type": "long" + } } } } - } - }, - "cgroup": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "cpu": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "cfs": { - "properties": { - "period": { - "properties": { - "us": { - "type": "long" + }, + "cgroup": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "cfs": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } } - } - }, - "quota": { - "properties": { - "us": { - "type": "long" + }, + "quota": { + "properties": { + "us": { + "type": "long" + } } + }, + "shares": { + "type": "long" } - }, - "shares": { - "type": "long" } - } - }, - "rt": { - "properties": { - "period": { - "properties": { - "us": { - "type": "long" + }, + "rt": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } } - } - }, - "runtime": { - "properties": { - "us": { - "type": "long" + }, + "runtime": { + "properties": { + "us": { + "type": "long" + } } } } - } - }, - "stats": { - "properties": { - "periods": { - "type": "long" - }, - "throttled": { - "properties": { - "periods": { - "type": "long" - }, - "ns": { - "type": "long" + }, + "stats": { + "properties": { + "periods": { + "type": "long" + }, + "throttled": { + "properties": { + "periods": { + "type": "long" + }, + "ns": { + "type": "long" + } } } } } } - } - }, - "cpuacct": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "ns": { - "type": "long" + }, + "cpuacct": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "properties": { + "ns": { + "type": "long" + } } - } - }, - "stats": { - "properties": { - "user": { - "properties": { - "ns": { - "type": "long" + }, + "stats": { + "properties": { + "user": { + "properties": { + "ns": { + "type": "long" + } } - } - }, - "system": { - "properties": { - "ns": { - "type": "long" + }, + "system": { + "properties": { + "ns": { + "type": "long" + } } } } + }, + "percpu": { + "type": "object" } - }, - "percpu": { - "type": "object" } - } - }, - "memory": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "mem": { - "properties": { - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" + }, + "memory": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "mem": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } } } } - } - }, - "limit": { - "properties": { - "bytes": { - "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } } + }, + "failures": { + "type": "long" } - }, - "failures": { - "type": "long" } - } - }, - "memsw": { - "properties": { - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" + }, + "memsw": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } } } } - } - }, - "limit": { - "properties": { - "bytes": { - "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } } + }, + "failures": { + "type": "long" } - }, - "failures": { - "type": "long" } - } - }, - "kmem": { - "properties": { - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" + }, + "kmem": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } } } } - } - }, - "limit": { - "properties": { - "bytes": { - "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } } + }, + "failures": { + "type": "long" } - }, - "failures": { - "type": "long" } - } - }, - "kmem_tcp": { - "properties": { - "usage": { - "properties": { - "bytes": { - "type": "long" - }, - "max": { - "properties": { - "bytes": { - "type": "long" + }, + "kmem_tcp": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } } } } - } - }, - "limit": { - "properties": { - "bytes": { - "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } } + }, + "failures": { + "type": "long" } - }, - "failures": { - "type": "long" } - } - }, - "stats": { - "properties": { - "active_anon": { - "properties": { - "bytes": { - "type": "long" + }, + "stats": { + "properties": { + "active_anon": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "active_file": { - "properties": { - "bytes": { - "type": "long" + }, + "active_file": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "cache": { - "properties": { - "bytes": { - "type": "long" + }, + "cache": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "hierarchical_memory_limit": { - "properties": { - "bytes": { - "type": "long" + }, + "hierarchical_memory_limit": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "hierarchical_memsw_limit": { - "properties": { - "bytes": { - "type": "long" + }, + "hierarchical_memsw_limit": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "inactive_anon": { - "properties": { - "bytes": { - "type": "long" + }, + "inactive_anon": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "inactive_file": { - "properties": { - "bytes": { - "type": "long" + }, + "inactive_file": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "mapped_file": { - "properties": { - "bytes": { - "type": "long" + }, + "mapped_file": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "page_faults": { - "type": "long" - }, - "major_page_faults": { - "type": "long" - }, - "pages_in": { - "type": "long" - }, - "pages_out": { - "type": "long" - }, - "rss": { - "properties": { - "bytes": { - "type": "long" + }, + "page_faults": { + "type": "long" + }, + "major_page_faults": { + "type": "long" + }, + "pages_in": { + "type": "long" + }, + "pages_out": { + "type": "long" + }, + "rss": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "rss_huge": { - "properties": { - "bytes": { - "type": "long" + }, + "rss_huge": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "swap": { - "properties": { - "bytes": { - "type": "long" + }, + "swap": { + "properties": { + "bytes": { + "type": "long" + } } - } - }, - "unevictable": { - "properties": { - "bytes": { - "type": "long" + }, + "unevictable": { + "properties": { + "bytes": { + "type": "long" + } } } } } } - } - }, - "blkio": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "total": { - "properties": { - "bytes": { - "type": "long" - }, - "ios": { - "type": "long" + }, + "blkio": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "properties": { + "bytes": { + "type": "long" + }, + "ios": { + "type": "long" + } } } } } } - } - }, - "summary": { - "properties": { - "total": { - "type": "long" - }, - "running": { - "type": "long" - }, - "idle": { - "type": "long" - }, - "sleeping": { - "type": "long" - }, - "stopped": { - "type": "long" - }, - "zombie": { - "type": "long" - }, - "dead": { - "type": "long" - }, - "unknown": { - "type": "long" + }, + "summary": { + "properties": { + "total": { + "type": "long" + }, + "running": { + "type": "long" + }, + "idle": { + "type": "long" + }, + "sleeping": { + "type": "long" + }, + "stopped": { + "type": "long" + }, + "zombie": { + "type": "long" + }, + "dead": { + "type": "long" + }, + "unknown": { + "type": "long" + } } } } - } - }, - "raid": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync_action": { - "ignore_above": 1024, - "type": "keyword" - }, - "disks": { - "properties": { - "active": { - "type": "long" - }, - "total": { - "type": "long" - }, - "spare": { - "type": "long" - }, - "failed": { - "type": "long" - }, - "states": { - "properties": { - "*": { - "type": "object" + }, + "raid": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "sync_action": { + "ignore_above": 1024, + "type": "keyword" + }, + "disks": { + "properties": { + "active": { + "type": "long" + }, + "total": { + "type": "long" + }, + "spare": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "states": { + "properties": { + "*": { + "type": "object" + } } } } - } - }, - "blocks": { - "properties": { - "total": { - "type": "long" - }, - "synced": { - "type": "long" + }, + "blocks": { + "properties": { + "total": { + "type": "long" + }, + "synced": { + "type": "long" + } } } } - } - }, - "socket": { - "properties": { - "local": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" + }, + "socket": { + "properties": { + "local": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } } - } - }, - "remote": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - }, - "host": { - "ignore_above": 1024, - "type": "keyword" - }, - "etld_plus_one": { - "ignore_above": 1024, - "type": "keyword" - }, - "host_error": { - "ignore_above": 1024, - "type": "keyword" + }, + "remote": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "etld_plus_one": { + "ignore_above": 1024, + "type": "keyword" + }, + "host_error": { + "ignore_above": 1024, + "type": "keyword" + } } - } - }, - "process": { - "properties": { - "cmdline": { - "ignore_above": 1024, - "type": "keyword" + }, + "process": { + "properties": { + "cmdline": { + "ignore_above": 1024, + "type": "keyword" + } } - } - }, - "user": { - "properties": {} - }, - "summary": { - "properties": { - "all": { - "properties": { - "count": { - "type": "long" - }, - "listening": { - "type": "long" + }, + "user": { + "properties": {} + }, + "summary": { + "properties": { + "all": { + "properties": { + "count": { + "type": "long" + }, + "listening": { + "type": "long" + } } - } - }, - "tcp": { - "properties": { - "memory": { - "type": "long" - }, - "all": { - "properties": { - "orphan": { - "type": "long" - }, - "count": { - "type": "long" - }, - "listening": { - "type": "long" - }, - "established": { - "type": "long" - }, - "close_wait": { - "type": "long" - }, - "time_wait": { - "type": "long" - }, - "syn_sent": { - "type": "long" - }, - "syn_recv": { - "type": "long" - }, - "fin_wait1": { - "type": "long" - }, - "fin_wait2": { - "type": "long" - }, - "last_ack": { - "type": "long" - }, - "closing": { - "type": "long" + }, + "tcp": { + "properties": { + "memory": { + "type": "long" + }, + "all": { + "properties": { + "orphan": { + "type": "long" + }, + "count": { + "type": "long" + }, + "listening": { + "type": "long" + }, + "established": { + "type": "long" + }, + "close_wait": { + "type": "long" + }, + "time_wait": { + "type": "long" + }, + "syn_sent": { + "type": "long" + }, + "syn_recv": { + "type": "long" + }, + "fin_wait1": { + "type": "long" + }, + "fin_wait2": { + "type": "long" + }, + "last_ack": { + "type": "long" + }, + "closing": { + "type": "long" + } } } } - } - }, - "udp": { - "properties": { - "memory": { - "type": "long" - }, - "all": { - "properties": { - "count": { - "type": "long" + }, + "udp": { + "properties": { + "memory": { + "type": "long" + }, + "all": { + "properties": { + "count": { + "type": "long" + } } } } @@ -1597,65 +1603,65 @@ exports[`tests loading system.yml: system.yml 1`] = ` } } } - } - }, - "uptime": { - "properties": { - "duration": { - "properties": { - "ms": { - "type": "long" + }, + "uptime": { + "properties": { + "duration": { + "properties": { + "ms": { + "type": "long" + } } } } - } - }, - "users": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "seat": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "ignore_above": 1024, - "type": "keyword" - }, - "remote": { - "type": "boolean" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "leader": { - "type": "long" - }, - "remote_host": { - "ignore_above": 1024, - "type": "keyword" + }, + "users": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "seat": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "remote": { + "type": "boolean" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "leader": { + "type": "long" + }, + "remote_host": { + "ignore_above": 1024, + "type": "keyword" + } } } } } } - } - }, - "aliases": {} + }, + "aliases": {} + } } `; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts index 4df626259ece76..6ef6f863753b55 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts @@ -18,7 +18,9 @@ export const installTemplates = async ( pkgVersion: string ): Promise => { // install any pre-built index template assets, - // atm, this is only the base package's global template + // atm, this is only the base package's global index templates + // Install component templates first, as they are used by the index templates + installPreBuiltComponentTemplates(pkgName, pkgVersion, callCluster); installPreBuiltTemplates(pkgName, pkgVersion, callCluster); // build templates per dataset from yml files @@ -41,7 +43,6 @@ export const installTemplates = async ( return []; }; -// this is temporary until we update the registry to use index templates v2 structure const installPreBuiltTemplates = async ( pkgName: string, pkgVersion: string, @@ -52,20 +53,91 @@ const installPreBuiltTemplates = async ( pkgVersion, (entry: Registry.ArchiveEntry) => isTemplate(entry) ); + // templatePaths.forEach(async path => { + // const { file } = Registry.pathParts(path); + // const templateName = file.substr(0, file.lastIndexOf('.')); + // const content = JSON.parse(Registry.getAsset(path).toString('utf8')); + // await callCluster('indices.putTemplate', { + // name: templateName, + // body: content, + // }); + // }); templatePaths.forEach(async path => { const { file } = Registry.pathParts(path); const templateName = file.substr(0, file.lastIndexOf('.')); const content = JSON.parse(Registry.getAsset(path).toString('utf8')); - await callCluster('indices.putTemplate', { - name: templateName, + let templateAPIPath = '_template'; + + // v2 index templates need to be installed through the new API endpoint. + // Checking for 'template' and 'composed_of' should catch them all. + // For the new v2 format, see https://github.com/elastic/elasticsearch/issues/53101 + if (content.hasOwnProperty('template') || content.hasOwnProperty('composed_of')) { + templateAPIPath = '_index_template'; + } + + const callClusterParams: { + method: string; + path: string; + ignore: number[]; + body: any; + } = { + method: 'PUT', + path: `/${templateAPIPath}/${templateName}`, + ignore: [404], body: content, - }); + }; + // This uses the catch-all endpoint 'transport.request' because there is no + // convenience endpoint using the new _index_template API yet. + // The existing convenience endpoint `indices.putTemplate` only sends to _template, + // which does not support v2 templates. + // See src/core/server/elasticsearch/api_types.ts for available endpoints. + await callCluster('transport.request', callClusterParams); }); }; + +const installPreBuiltComponentTemplates = async ( + pkgName: string, + pkgVersion: string, + callCluster: CallESAsCurrentUser +) => { + const templatePaths = await Registry.getArchiveInfo( + pkgName, + pkgVersion, + (entry: Registry.ArchiveEntry) => isComponentTemplate(entry) + ); + templatePaths.forEach(async path => { + const { file } = Registry.pathParts(path); + const templateName = file.substr(0, file.lastIndexOf('.')); + const content = JSON.parse(Registry.getAsset(path).toString('utf8')); + + const callClusterParams: { + method: string; + path: string; + ignore: number[]; + body: any; + } = { + method: 'PUT', + path: `/_component_template/${templateName}`, + ignore: [404], + body: content, + }; + // This uses the catch-all endpoint 'transport.request' because there is no + // convenience endpoint for component templates yet. + // See src/core/server/elasticsearch/api_types.ts for available endpoints. + await callCluster('transport.request', callClusterParams); + }); +}; + const isTemplate = ({ path }: Registry.ArchiveEntry) => { const pathParts = Registry.pathParts(path); return pathParts.type === ElasticsearchAssetType.indexTemplate; }; + +const isComponentTemplate = ({ path }: Registry.ArchiveEntry) => { + const pathParts = Registry.pathParts(path); + return pathParts.type === ElasticsearchAssetType.componentTemplate; +}; + /** * installTemplatesForDataset installs one template for each dataset * @@ -113,10 +185,23 @@ export async function installTemplate({ } const template = getTemplate(dataset.type, templateName, mappings, pipelineName); // TODO: Check return values for errors - await callCluster('indices.putTemplate', { - name: templateName, + const callClusterParams: { + method: string; + path: string; + ignore: number[]; + body: any; + } = { + method: 'PUT', + path: `/_index_template/${templateName}`, + ignore: [404], body: template, - }); + }; + // This uses the catch-all endpoint 'transport.request' because there is no + // convenience endpoint using the new _index_template API yet. + // The existing convenience endpoint `indices.putTemplate` only sends to _template, + // which does not support v2 templates. + // See src/core/server/elasticsearch/api_types.ts for available endpoints. + await callCluster('transport.request', callClusterParams); return { templateName, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts index 1a73c9581a2de7..25180244b02147 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts @@ -259,3 +259,71 @@ test('tests processing object field with dynamic set to strict', () => { const mappings = generateMappings(processedFields); expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldDynamicStrictMapping)); }); + +test('tests processing object field with property', () => { + const objectFieldWithPropertyLiteralYml = ` +- name: a + type: object +- name: a.b + type: keyword + `; + const objectFieldWithPropertyMapping = { + properties: { + a: { + properties: { + b: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(objectFieldWithPropertyLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldWithPropertyMapping)); +}); + +test('tests processing object field with property, reverse order', () => { + const objectFieldWithPropertyReversedLiteralYml = ` +- name: a.b + type: keyword +- name: a + type: object + `; + const objectFieldWithPropertyReversedMapping = { + properties: { + a: { + properties: { + b: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(objectFieldWithPropertyReversedLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldWithPropertyReversedMapping)); +}); + +test('tests constant_keyword field type handling', () => { + const constantKeywordLiteralYaml = ` +- name: constantKeyword + type: constant_keyword + `; + const constantKeywordMapping = { + properties: { + constantKeyword: { + type: 'constant_keyword', + }, + }, + }; + const fields: Field[] = safeLoad(constantKeywordLiteralYaml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(JSON.stringify(mappings)).toEqual(JSON.stringify(constantKeywordMapping)); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 46b6923962462e..9736f6d1cbd3cd 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -45,7 +45,7 @@ export function getTemplate( ): IndexTemplate { const template = getBaseTemplate(type, templateName, mappings); if (pipelineName) { - template.settings.index.default_pipeline = pipelineName; + template.template.settings.index.default_pipeline = pipelineName; } return template; } @@ -212,59 +212,61 @@ function getBaseTemplate( mappings: IndexTemplateMappings ): IndexTemplate { return { - // We need to decide which order we use for the templates - order: 1, + // This takes precedence over all index templates installed with the 'base' package + priority: 1, // To be completed with the correct index patterns index_patterns: [`${templateName}-*`], - settings: { - index: { - // ILM Policy must be added here, for now point to the default global ILM policy name - lifecycle: { - name: `${type}-default`, - }, - // What should be our default for the compression? - codec: 'best_compression', - // W - mapping: { - total_fields: { - limit: '10000', + template: { + settings: { + index: { + // ILM Policy must be added here, for now point to the default global ILM policy name + lifecycle: { + name: `${type}-default`, }, + // What should be our default for the compression? + codec: 'best_compression', + // W + mapping: { + total_fields: { + limit: '10000', + }, + }, + // This is the default from Beats? So far seems to be a good value + refresh_interval: '5s', + // Default in the stack now, still good to have it in + number_of_shards: '1', + // All the default fields which should be queried have to be added here. + // So far we add all keyword and text fields here. + query: { + default_field: ['message'], + }, + // We are setting 30 because it can be devided by several numbers. Useful when shrinking. + number_of_routing_shards: '30', }, - // This is the default from Beats? So far seems to be a good value - refresh_interval: '5s', - // Default in the stack now, still good to have it in - number_of_shards: '1', - // All the default fields which should be queried have to be added here. - // So far we add all keyword and text fields here. - query: { - default_field: ['message'], - }, - // We are setting 30 because it can be devided by several numbers. Useful when shrinking. - number_of_routing_shards: '30', }, - }, - mappings: { - // All the dynamic field mappings - dynamic_templates: [ - // This makes sure all mappings are keywords by default - { - strings_as_keyword: { - mapping: { - ignore_above: 1024, - type: 'keyword', + mappings: { + // All the dynamic field mappings + dynamic_templates: [ + // This makes sure all mappings are keywords by default + { + strings_as_keyword: { + mapping: { + ignore_above: 1024, + type: 'keyword', + }, + match_mapping_type: 'string', }, - match_mapping_type: 'string', }, - }, - ], - // As we define fields ahead, we don't need any automatic field detection - // This makes sure all the fields are mapped to keyword by default to prevent mapping conflicts - date_detection: false, - // All the properties we know from the fields.yml file - properties: mappings.properties, + ], + // As we define fields ahead, we don't need any automatic field detection + // This makes sure all the fields are mapped to keyword by default to prevent mapping conflicts + date_detection: false, + // All the properties we know from the fields.yml file + properties: mappings.properties, + }, + // To be filled with the aliases that we need + aliases: {}, }, - // To be filled with the aliases that we need - aliases: {}, }; } @@ -322,7 +324,7 @@ const updateExistingIndex = async ({ callCluster: CallESAsCurrentUser; indexTemplate: IndexTemplate; }) => { - const { settings, mappings } = indexTemplate; + const { settings, mappings } = indexTemplate.template; // try to update the mappings first // for now we assume updates are compatible try { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts index e3aef6077dbc30..42989bb1e3ac91 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts @@ -179,4 +179,35 @@ describe('processFields', () => { JSON.stringify(mixedFieldsExpanded) ); }); + + const objectFieldWithProperty = [ + { + name: 'a', + type: 'object', + dynamic: true, + }, + { + name: 'a.b', + type: 'keyword', + }, + ]; + + const objectFieldWithPropertyExpanded = [ + { + name: 'a', + type: 'group', + dynamic: true, + fields: [ + { + name: 'b', + type: 'keyword', + }, + ], + }, + ]; + test('correctly handles properties of object type fields', () => { + expect(JSON.stringify(processFields(objectFieldWithProperty))).toEqual( + JSON.stringify(objectFieldWithPropertyExpanded) + ); + }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts index 9c9843e0454ab5..edf7624d3f0d5b 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts @@ -108,7 +108,15 @@ function dedupFields(fields: Fields): Fields { return f.name === field.name; }); if (found) { - if (found.type === 'group' && field.type === 'group' && found.fields && field.fields) { + if ( + (found.type === 'group' || found.type === 'object') && + field.type === 'group' && + field.fields + ) { + if (!found.fields) { + found.fields = []; + } + found.type = 'group'; found.fields = dedupFields(found.fields.concat(field.fields)); } else { // only 'group' fields can be merged in this way diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts index bc1694348b4c2f..f1660fbc015913 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts @@ -150,6 +150,7 @@ describe('creating index patterns from yaml fields', () => { { fields: [{ name: 'testField', type: 'text' }], expect: 'string' }, { fields: [{ name: 'testField', type: 'date' }], expect: 'date' }, { fields: [{ name: 'testField', type: 'geo_point' }], expect: 'geo_point' }, + { fields: [{ name: 'testField', type: 'constant_keyword' }], expect: 'string' }, ]; tests.forEach(test => { @@ -191,6 +192,7 @@ describe('creating index patterns from yaml fields', () => { attr: 'aggregatable', }, { fields: [{ name, type: 'keyword' }], expect: true, attr: 'aggregatable' }, + { fields: [{ name, type: 'constant_keyword' }], expect: true, attr: 'aggregatable' }, { fields: [{ name, type: 'text', aggregatable: true }], expect: false, attr: 'aggregatable' }, { fields: [{ name, type: 'text' }], expect: false, attr: 'aggregatable' }, { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts index 05e64c6565dc67..ec657820a22251 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts @@ -47,6 +47,7 @@ const typeMap: TypeMap = { date: 'date', ip: 'ip', boolean: 'boolean', + constant_keyword: 'string', }; export interface IndexPatternField { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts index d76584225877c2..da8d79a04b97cd 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts @@ -67,9 +67,10 @@ export async function getPackageInfo(options: { pkgVersion: string; }): Promise { const { savedObjectsClient, pkgName, pkgVersion } = options; - const [item, savedObject, assets] = await Promise.all([ + const [item, savedObject, latestPackage, assets] = await Promise.all([ Registry.fetchInfo(pkgName, pkgVersion), getInstallationObject({ savedObjectsClient, pkgName }), + Registry.fetchFindLatestPackage(pkgName), Registry.getArchiveInfo(pkgName, pkgVersion), ] as const); // adding `as const` due to regression in TS 3.7.2 @@ -79,6 +80,7 @@ export async function getPackageInfo(options: { // add properties that aren't (or aren't yet) on Registry response const updated = { ...item, + latestVersion: latestPackage.version, title: item.title || nameAsTitle(item.name), assets: Registry.groupPathsByService(assets || []), }; 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 d49e0e661440f3..c67cccd044bf5e 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 @@ -43,6 +43,7 @@ export function createInstallableFrom( ? { ...from, status: InstallationStatus.installed, + installedVersion: savedObject.attributes.version, savedObject, } : { 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 06f3decdbbe6f2..8f51c4d78305c0 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 @@ -106,7 +106,7 @@ export async function installPackage(options: { try { await deleteKibanaSavedObjectsAssets(savedObjectsClient, installedPkg.attributes.installed); } catch (err) { - // some assets may not exist if deleting during a failed update + // log these errors, some assets may not exist if deleted during a failed update } } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts index 498796438c6c8e..befb4722b6504f 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/remove.ts @@ -74,7 +74,21 @@ async function deleteTemplate(callCluster: CallESAsCurrentUser, name: string): P // '*' shouldn't ever appear here, but it still would delete all templates if (name && name !== '*') { try { - await callCluster('indices.deleteTemplate', { name }); + const callClusterParams: { + method: string; + path: string; + ignore: number[]; + } = { + method: 'DELETE', + path: `/_index_template/${name}`, + ignore: [404], + }; + // This uses the catch-all endpoint 'transport.request' because there is no + // convenience endpoint using the new _index_template API yet. + // The existing convenience endpoint `indices.putTemplate` only sends to _template, + // which does not support v2 templates. + // See src/core/server/elasticsearch/api_types.ts for available endpoints. + await callCluster('transport.request', callClusterParams); } catch { throw new Error(`error deleting template ${name}`); } @@ -107,8 +121,12 @@ export async function deleteKibanaSavedObjectsAssets( const deletePromises = installedObjects.map(({ id, type }) => { const assetType = type as AssetType; if (savedObjectTypes.includes(assetType)) { - savedObjectsClient.delete(assetType, id); + return savedObjectsClient.delete(assetType, id); } }); - await Promise.all(deletePromises); + try { + await Promise.all(deletePromises); + } catch (err) { + throw new Error('error deleting saved object asset'); + } } diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index 206ad76703cf56..390e240841611e 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -145,7 +145,7 @@ async function addPackageToConfig( config.namespace ); newDatasource.inputs = await datasourceService.assignPackageStream( - { pkgName: packageToInstall.name, pkgVersion: packageToInstall.version }, + packageInfo, newDatasource.inputs ); diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts index 040b2eb16289ab..59cadf3bd7f74c 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent_config.ts @@ -11,6 +11,9 @@ const AgentConfigBaseSchema = { name: schema.string(), namespace: schema.maybe(schema.string()), description: schema.maybe(schema.string()), + monitoring_enabled: schema.maybe( + schema.arrayOf(schema.oneOf([schema.literal('logs'), schema.literal('metrics')])) + ), }; export const NewAgentConfigSchema = schema.object({ diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 359c06a6a9ebce..48729448b2ea57 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -41,6 +41,10 @@ export const datatableVisualization: Visualization< }, ], + getVisualizationTypeId() { + return 'lnsDatatable'; + }, + getLayerIds(state) { return state.layers.map(l => l.layerId); }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx index 3c61d270b1bcf9..c8d8064e60e38a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.test.tsx @@ -62,7 +62,25 @@ describe('chart_switch', () => { id: 'subvisC2', label: 'C2', }, + { + icon: 'empty', + id: 'subvisC3', + label: 'C3', + }, ], + getSuggestions: jest.fn(options => { + if (options.subVisualizationId === 'subvisC2') { + return []; + } + return [ + { + score: 1, + title: '', + state: `suggestion`, + previewIcon: 'empty', + }, + ]; + }), }, }; } @@ -313,10 +331,11 @@ describe('chart_switch', () => { expect(getMenuItem('subvisB', component).prop('betaBadgeIconType')).toBeUndefined(); }); - it('should not indicate data loss if visualization is not changed', () => { + it('should not show a warning when the subvisualization is the same', () => { const dispatch = jest.fn(); const frame = mockFrame(['a', 'b', 'c']); const visualizations = mockVisualizations(); + visualizations.visC.getVisualizationTypeId.mockReturnValue('subvisC2'); const switchVisualizationType = jest.fn(() => 'therebedragons'); visualizations.visC.switchVisualizationType = switchVisualizationType; @@ -333,10 +352,10 @@ describe('chart_switch', () => { /> ); - expect(getMenuItem('subvisC2', component).prop('betaBadgeIconType')).toBeUndefined(); + expect(getMenuItem('subvisC2', component).prop('betaBadgeIconType')).not.toBeDefined(); }); - it('should remove all layers if there is no suggestion', () => { + it('should get suggestions when switching subvisualization', () => { const dispatch = jest.fn(); const visualizations = mockVisualizations(); visualizations.visB.getSuggestions.mockReturnValueOnce([]); @@ -377,7 +396,7 @@ describe('chart_switch', () => { const dispatch = jest.fn(); const frame = mockFrame(['a', 'b', 'c']); const visualizations = mockVisualizations(); - const switchVisualizationType = jest.fn(() => 'therebedragons'); + const switchVisualizationType = jest.fn(() => 'switched'); visualizations.visC.switchVisualizationType = switchVisualizationType; @@ -393,12 +412,12 @@ describe('chart_switch', () => { /> ); - switchTo('subvisC2', component); - expect(switchVisualizationType).toHaveBeenCalledWith('subvisC2', 'therebegriffins'); + switchTo('subvisC3', component); + expect(switchVisualizationType).toHaveBeenCalledWith('subvisC3', 'suggestion'); expect(dispatch).toHaveBeenCalledWith( expect.objectContaining({ type: 'SWITCH_VISUALIZATION', - initialState: 'therebedragons', + initialState: 'switched', }) ); expect(frame.removeLayers).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx index 1461449f3c1c8a..d73f83e75c0e4e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/chart_switch.tsx @@ -105,7 +105,16 @@ export function ChartSwitch(props: Props) { const switchVisType = props.visualizationMap[visualizationId].switchVisualizationType || ((_type: string, initialState: unknown) => initialState); - if (props.visualizationId === visualizationId) { + const layers = Object.entries(props.framePublicAPI.datasourceLayers); + const containsData = layers.some( + ([_layerId, datasource]) => datasource.getTableSpec().length > 0 + ); + // Always show the active visualization as a valid selection + if ( + props.visualizationId === visualizationId && + props.visualizationState && + newVisualization.getVisualizationTypeId(props.visualizationState) === subVisualizationId + ) { return { visualizationId, subVisualizationId, @@ -116,13 +125,13 @@ export function ChartSwitch(props: Props) { }; } - const layers = Object.entries(props.framePublicAPI.datasourceLayers); - const containsData = layers.some( - ([_layerId, datasource]) => datasource.getTableSpec().length > 0 + const topSuggestion = getTopSuggestion( + props, + visualizationId, + newVisualization, + subVisualizationId ); - const topSuggestion = getTopSuggestion(props, visualizationId, newVisualization); - let dataLoss: VisualizationSelection['dataLoss']; if (!containsData) { @@ -250,7 +259,8 @@ export function ChartSwitch(props: Props) { function getTopSuggestion( props: Props, visualizationId: string, - newVisualization: Visualization + newVisualization: Visualization, + subVisualizationId?: string ): Suggestion | undefined { const suggestions = getSuggestions({ datasourceMap: props.datasourceMap, @@ -258,6 +268,7 @@ function getTopSuggestion( visualizationMap: { [visualizationId]: newVisualization }, activeVisualizationId: props.visualizationId, visualizationState: props.visualizationState, + subVisualizationId, }).filter(suggestion => { // don't use extended versions of current data table on switching between visualizations // to avoid confusing the user. diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts index eabcdfa7a24ab4..949ae1f43448ed 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_helpers.ts @@ -44,6 +44,7 @@ export function getSuggestions({ datasourceStates, visualizationMap, activeVisualizationId, + subVisualizationId, visualizationState, field, }: { @@ -57,6 +58,7 @@ export function getSuggestions({ >; visualizationMap: Record; activeVisualizationId: string | null; + subVisualizationId?: string; visualizationState: unknown; field?: unknown; }): Suggestion[] { @@ -89,7 +91,8 @@ export function getSuggestions({ table, visualizationId, datasourceSuggestion, - currentVisualizationState + currentVisualizationState, + subVisualizationId ); }) ) @@ -108,13 +111,15 @@ function getVisualizationSuggestions( table: TableSuggestion, visualizationId: string, datasourceSuggestion: DatasourceSuggestion & { datasourceId: string }, - currentVisualizationState: unknown + currentVisualizationState: unknown, + subVisualizationId?: string ) { return visualization .getSuggestions({ table, state: currentVisualizationState, keptLayerIds: datasourceSuggestion.keptLayerIds, + subVisualizationId, }) .map(({ state, ...visualizationSuggestion }) => ({ ...visualizationSuggestion, diff --git a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx index 50cd1ad8bd53a4..e684fe8b3b5d62 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/mocks.tsx @@ -28,6 +28,7 @@ export function createMockVisualization(): jest.Mocked { label: 'TEST', }, ], + getVisualizationTypeId: jest.fn(_state => 'empty'), getDescription: jest.fn(_state => ({ label: '' })), switchVisualizationType: jest.fn((_, x) => x), getPersistableState: jest.fn(_state => _state), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.test.ts deleted file mode 100644 index 5f35ef650a08c2..00000000000000 --- a/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.test.ts +++ /dev/null @@ -1,83 +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 { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; -import { getAutoDate } from './auto_date'; - -describe('auto_date', () => { - let autoDate: ReturnType; - - beforeEach(() => { - autoDate = getAutoDate({ data: dataPluginMock.createSetupContract() }); - }); - - it('should do nothing if no time range is provided', () => { - const result = autoDate.fn( - { - type: 'kibana_context', - }, - { - aggConfigs: 'canttouchthis', - }, - // eslint-disable-next-line - {} as any - ); - - expect(result).toEqual('canttouchthis'); - }); - - it('should not change anything if there are no auto date histograms', () => { - const aggConfigs = JSON.stringify([ - { type: 'date_histogram', params: { interval: '35h' } }, - { type: 'count' }, - ]); - const result = autoDate.fn( - { - timeRange: { - from: 'now-10d', - to: 'now', - }, - type: 'kibana_context', - }, - { - aggConfigs, - }, - // eslint-disable-next-line - {} as any - ); - - expect(result).toEqual(aggConfigs); - }); - - it('should change auto date histograms', () => { - const aggConfigs = JSON.stringify([ - { type: 'date_histogram', params: { interval: 'auto' } }, - { type: 'count' }, - ]); - const result = autoDate.fn( - { - timeRange: { - from: 'now-10d', - to: 'now', - }, - type: 'kibana_context', - }, - { - aggConfigs, - }, - // eslint-disable-next-line - {} as any - ); - - const interval = JSON.parse(result).find( - (agg: { type: string }) => agg.type === 'date_histogram' - ).params.interval; - - expect(interval).toBeTruthy(); - expect(typeof interval).toEqual('string'); - expect(interval).not.toEqual('auto'); - }); -}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.ts b/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.ts deleted file mode 100644 index 97a46f4a3e1765..00000000000000 --- a/x-pack/plugins/lens/public/indexpattern_datasource/auto_date.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 { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; -import { - ExpressionFunctionDefinition, - KibanaContext, -} from '../../../../../src/plugins/expressions/public'; - -interface LensAutoDateProps { - aggConfigs: string; -} - -export function getAutoDate(deps: { - data: DataPublicPluginSetup; -}): ExpressionFunctionDefinition< - 'lens_auto_date', - KibanaContext | null, - LensAutoDateProps, - string -> { - function autoIntervalFromContext(ctx?: KibanaContext | null) { - if (!ctx || !ctx.timeRange) { - return; - } - - return deps.data.search.aggs.calculateAutoTimeExpression(ctx.timeRange); - } - - /** - * Convert all 'auto' date histograms into a concrete value (e.g. 2h). - * This allows us to support 'auto' on all date fields, and opens the - * door to future customizations (e.g. adjusting the level of detail, etc). - */ - return { - name: 'lens_auto_date', - aliases: [], - help: '', - inputTypes: ['kibana_context', 'null'], - args: { - aggConfigs: { - types: ['string'], - default: '""', - help: '', - }, - }, - fn(input, args) { - const interval = autoIntervalFromContext(input); - - if (!interval) { - return args.aggConfigs; - } - - const configs = JSON.parse(args.aggConfigs) as Array<{ - type: string; - params: { interval: string }; - }>; - - const updatedConfigs = configs.map(c => { - if (c.type !== 'date_histogram' || !c.params || c.params.interval !== 'auto') { - return c; - } - - return { - ...c, - params: { - ...c.params, - interval, - }, - }; - }); - - return JSON.stringify(updatedConfigs); - }, - }; -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 074c40759f8d82..9df79aa9e09085 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -1380,5 +1380,43 @@ describe('IndexPatternDimensionEditorPanel', () => { }, }); }); + + it('does not set the size of the terms aggregation', () => { + const dragging = { + field: { type: 'string', name: 'mystring', aggregatable: true }, + indexPatternId: 'foo', + }; + const testState = dragDropState(); + onDrop({ + ...defaultProps, + dragDropContext: { + ...dragDropContext, + dragging, + }, + droppedItem: dragging, + state: testState, + columnId: 'col2', + filterOperations: (op: OperationMetadata) => op.isBucketed, + layerId: 'myLayer', + }); + + expect(setState).toBeCalledTimes(1); + expect(setState).toHaveBeenCalledWith({ + ...testState, + layers: { + myLayer: { + ...testState.layers.myLayer, + columnOrder: ['col1', 'col2'], + columns: { + ...testState.layers.myLayer.columns, + col2: expect.objectContaining({ + operationType: 'terms', + params: expect.objectContaining({ size: 3 }), + }), + }, + }, + }, + }); + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index fe14f472341afd..73fd144b9c7f87 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -8,7 +8,6 @@ import { CoreSetup } from 'kibana/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { getIndexPatternDatasource } from './indexpattern'; import { renameColumns } from './rename_columns'; -import { getAutoDate } from './auto_date'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { DataPublicPluginSetup, @@ -31,10 +30,9 @@ export class IndexPatternDatasource { setup( core: CoreSetup, - { data: dataSetup, expressions, editorFrame }: IndexPatternDatasourceSetupPlugins + { expressions, editorFrame }: IndexPatternDatasourceSetupPlugins ) { expressions.registerFunction(renameColumns); - expressions.registerFunction(getAutoDate({ data: dataSetup })); editorFrame.registerDatasource( core.getStartServices().then(([coreStart, { data }]) => diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index e4f3677d0fe88e..06635e663361db 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -10,6 +10,7 @@ import { DatasourcePublicAPI, Operation, Datasource } from '../types'; import { coreMock } from 'src/core/public/mocks'; import { IndexPatternPersistedState, IndexPatternPrivateState } from './types'; import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; +import { Ast } from '@kbn/interpreter/common'; jest.mock('./loader'); jest.mock('../id_generator'); @@ -262,20 +263,7 @@ describe('IndexPattern Data Source', () => { Object { "arguments": Object { "aggConfigs": Array [ - Object { - "chain": Array [ - Object { - "arguments": Object { - "aggConfigs": Array [ - "[{\\"id\\":\\"col1\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}},{\\"id\\":\\"col2\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"timestamp\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"1d\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}}]", - ], - }, - "function": "lens_auto_date", - "type": "function", - }, - ], - "type": "expression", - }, + "[{\\"id\\":\\"col1\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}},{\\"id\\":\\"col2\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"timestamp\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"1d\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}}]", ], "includeFormatHints": Array [ true, @@ -289,6 +277,9 @@ describe('IndexPattern Data Source', () => { "partialRows": Array [ false, ], + "timeFields": Array [ + "timestamp", + ], }, "function": "esaggs", "type": "function", @@ -307,6 +298,89 @@ describe('IndexPattern Data Source', () => { } `); }); + + it('should put all time fields used in date_histograms to the esaggs timeFields parameter', async () => { + const queryPersistedState: IndexPatternPersistedState = { + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + col1: { + label: 'Count of records', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + col2: { + label: 'Date', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { + interval: 'auto', + }, + }, + col3: { + label: 'Date 2', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'another_datefield', + params: { + interval: 'auto', + }, + }, + }, + }, + }, + }; + + const state = stateFromPersistedState(queryPersistedState); + + const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; + expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']); + }); + + it('should not put date fields used outside date_histograms to the esaggs timeFields parameter', async () => { + const queryPersistedState: IndexPatternPersistedState = { + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Count of records', + dataType: 'date', + isBucketed: false, + sourceField: 'timefield', + operationType: 'cardinality', + }, + col2: { + label: 'Date', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { + interval: 'auto', + }, + }, + }, + }, + }, + }; + + const state = stateFromPersistedState(queryPersistedState); + + const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; + expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp']); + expect(ast.chain[0].arguments.timeFields).not.toContain('timefield'); + }); }); describe('#insertLayer', () => { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 2008b326a539ce..02471b935c97c0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -184,6 +184,7 @@ describe('IndexPattern Data Source suggestions', () => { id2: expect.objectContaining({ operationType: 'terms', sourceField: 'source', + params: expect.objectContaining({ size: 5 }), }), id3: expect.objectContaining({ operationType: 'count', @@ -388,6 +389,7 @@ describe('IndexPattern Data Source suggestions', () => { id1: expect.objectContaining({ operationType: 'terms', sourceField: 'source', + params: expect.objectContaining({ size: 5 }), }), id2: expect.objectContaining({ operationType: 'count', @@ -779,7 +781,7 @@ describe('IndexPattern Data Source suggestions', () => { expect(suggestions[0].table.columns[0].operation.isBucketed).toBeFalsy(); }); - it('appends a terms column on string field', () => { + it('appends a terms column with default size on string field', () => { const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'dest', @@ -800,6 +802,7 @@ describe('IndexPattern Data Source suggestions', () => { id1: expect.objectContaining({ operationType: 'terms', sourceField: 'dest', + params: expect.objectContaining({ size: 3 }), }), }, }), 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 2b3e976a77ea75..44963722f8afcb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -17,6 +17,7 @@ import { OperationType, } from './operations'; import { operationDefinitions } from './operations/definitions'; +import { TermsIndexPatternColumn } from './operations/definitions/terms'; import { hasField } from './utils'; import { IndexPattern, @@ -232,6 +233,10 @@ function addFieldAsBucketOperation( [newColumnId]: newColumn, }; + if (buckets.length === 0 && operation === 'terms') { + (newColumn as TermsIndexPatternColumn).params.size = 5; + } + const oldDateHistogramIndex = layer.columnOrder.findIndex( columnId => layer.columns[columnId].operationType === 'date_histogram' ); @@ -327,6 +332,9 @@ function createNewLayerWithBucketAggregation( field, suggestedPriority: undefined, }); + if (operation === 'terms') { + (column as TermsIndexPatternColumn).params.size = 5; + } return { indexPatternId: indexPattern.id, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index 3ab51b5fa3f2b4..1308fa3b7ca603 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -10,6 +10,7 @@ import { IndexPatternColumn } from './indexpattern'; import { operationDefinitionMap } from './operations'; import { IndexPattern, IndexPatternPrivateState } from './types'; import { OriginalColumn } from './rename_columns'; +import { dateHistogramOperation } from './operations/definitions'; function getExpressionForLayer( indexPattern: IndexPattern, @@ -68,6 +69,12 @@ function getExpressionForLayer( return base; }); + const allDateHistogramFields = Object.values(columns) + .map(column => + column.operationType === dateHistogramOperation.type ? column.sourceField : null + ) + .filter((field): field is string => Boolean(field)); + return { type: 'expression', chain: [ @@ -79,20 +86,8 @@ function getExpressionForLayer( metricsAtAllLevels: [false], partialRows: [false], includeFormatHints: [true], - aggConfigs: [ - { - type: 'expression', - chain: [ - { - type: 'function', - function: 'lens_auto_date', - arguments: { - aggConfigs: [JSON.stringify(aggs)], - }, - }, - ], - }, - ], + timeFields: allDateHistogramFields, + aggConfigs: [JSON.stringify(aggs)], }, }, { diff --git a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx index 73b8019a31eaa6..04a1c3865f22d5 100644 --- a/x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/metric_visualization.tsx @@ -53,6 +53,10 @@ export const metricVisualization: Visualization = { }, ], + getVisualizationTypeId() { + return 'lnsMetric'; + }, + clearLayer(state) { return { ...state, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 181f192520d0dd..ed0af8545f0122 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -312,6 +312,10 @@ export interface SuggestionRequest { * The visualization needs to know which table is being suggested */ keptLayerIds: string[]; + /** + * Different suggestions can be generated for each subtype of the visualization + */ + subVisualizationId?: string; } /** @@ -388,6 +392,11 @@ export interface Visualization { * but can register multiple subtypes */ visualizationTypes: VisualizationType[]; + /** + * Return the ID of the current visualization. Used to highlight + * the active subtype of the visualization. + */ + getVisualizationTypeId: (state: T) => string; /** * If the visualization has subtypes, update the subtype in state. */ diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index 5dfae097be8345..3a280fb045b060 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -53,6 +53,7 @@ export class XyVisualization { ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme, timeZone: getTimeZone(core.uiSettings), + histogramBarTarget: core.uiSettings.get('histogram:barTarget'), }) ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index e75e5fe763d6ae..8db00aba0e36d3 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -40,7 +40,7 @@ const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatata id: 'c', name: 'c', formatHint: { id: 'string' }, - meta: { type: 'date-histogram', aggConfigParams: { interval: '10s' } }, + meta: { type: 'date-histogram', aggConfigParams: { interval: 'auto' } }, }, { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, ], @@ -156,6 +156,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -203,6 +204,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -237,15 +239,17 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); + // real auto interval is 30mins = 1800000 expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` Object { "max": 1546491600000, "min": 1546405200000, - "minInterval": 10000, + "minInterval": 1728000, } `); }); @@ -271,6 +275,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -279,7 +284,7 @@ describe('xy_expression', () => { Object { "max": 1546491600000, "min": 1546405200000, - "minInterval": 10000, + "minInterval": 1728000, } `); }); @@ -307,6 +312,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -350,6 +356,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -383,6 +390,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -398,6 +406,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -414,6 +423,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -430,6 +440,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -472,6 +483,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -510,6 +522,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -527,6 +540,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -547,6 +561,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -565,6 +580,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="CEST" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -582,6 +598,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -606,6 +623,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -624,6 +642,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -684,6 +703,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -878,6 +898,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -894,6 +915,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -910,6 +932,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -927,6 +950,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); @@ -943,6 +967,7 @@ describe('xy_expression', () => { args={{ ...args, layers: [{ ...args.layers[0], accessors: ['a'] }] }} formatFactory={getFormatSpy} chartTheme={{}} + histogramBarTarget={50} timeZone="UTC" executeTriggerActions={executeTriggerActions} /> @@ -963,6 +988,7 @@ describe('xy_expression', () => { formatFactory={getFormatSpy} timeZone="UTC" chartTheme={{}} + histogramBarTarget={50} executeTriggerActions={executeTriggerActions} /> ); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index d6b6de479acfb2..85cf5753befd79 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -6,6 +6,7 @@ import React, { useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; +import moment from 'moment'; import { Chart, Settings, @@ -35,8 +36,8 @@ import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { parseInterval } from '../../../../../src/plugins/data/common'; import { getExecuteTriggerActions } from './services'; +import { parseInterval } from '../../../../../src/plugins/data/common'; type InferPropType = T extends React.FunctionComponent ? P : T; type SeriesSpec = InferPropType & @@ -58,6 +59,7 @@ type XYChartRenderProps = XYChartProps & { chartTheme: PartialTheme; formatFactory: FormatFactory; timeZone: string; + histogramBarTarget: number; executeTriggerActions: UiActionsStart['executeTriggerActions']; }; @@ -110,6 +112,7 @@ export const xyChart: ExpressionFunctionDefinition< export const getXyChartRenderer = (dependencies: { formatFactory: Promise; chartTheme: PartialTheme; + histogramBarTarget: number; timeZone: string; }): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', @@ -130,6 +133,7 @@ export const getXyChartRenderer = (dependencies: { formatFactory={formatFactory} chartTheme={dependencies.chartTheme} timeZone={dependencies.timeZone} + histogramBarTarget={dependencies.histogramBarTarget} executeTriggerActions={executeTriggerActions} /> , @@ -169,6 +173,7 @@ export function XYChart({ formatFactory, timeZone, chartTheme, + histogramBarTarget, executeTriggerActions, }: XYChartRenderProps) { const { legend, layers } = args; @@ -212,18 +217,26 @@ export function XYChart({ const xTitle = (xAxisColumn && xAxisColumn.name) || args.xTitle; - // add minInterval only for single row value as it cannot be determined from dataset + function calculateMinInterval() { + // add minInterval only for single row value as it cannot be determined from dataset + if (data.dateRange && layers.every(layer => data.tables[layer.layerId].rows.length <= 1)) { + if (xAxisColumn?.meta?.aggConfigParams?.interval !== 'auto') + return parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds(); - const minInterval = layers.every(layer => data.tables[layer.layerId].rows.length <= 1) - ? parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds() - : undefined; + 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 xDomain = data.dateRange && layers.every(l => l.xScaleType === 'time') ? { min: data.dateRange.fromDate.getTime(), max: data.dateRange.toDate.getTime(), - minInterval, + minInterval: calculateMinInterval(), } : undefined; return ( 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 index beccf0dc46eb45..d176905c65120b 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.test.ts @@ -27,7 +27,7 @@ function exampleState(): State { } describe('xy_visualization', () => { - describe('getDescription', () => { + describe('#getDescription', () => { function mixedState(...types: SeriesType[]) { const state = exampleState(); return { @@ -81,6 +81,45 @@ describe('xy_visualization', () => { }); }); + 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(); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx index c72fa0fec24d77..e91edf9cc01833 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_visualization.tsx @@ -12,7 +12,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { getSuggestions } from './xy_suggestions'; import { LayerContextMenu } from './xy_config_panel'; -import { Visualization, OperationMetadata } from '../types'; +import { Visualization, OperationMetadata, VisualizationType } from '../types'; import { State, PersistableState, SeriesType, visualizationTypes, LayerConfig } from './types'; import { toExpression, toPreviewExpression } from './to_expression'; import chartBarStackedSVG from '../assets/chart_bar_stacked.svg'; @@ -24,6 +24,18 @@ const defaultSeriesType = 'bar_stacked'; const isNumericMetric = (op: OperationMetadata) => !op.isBucketed && op.dataType === 'number'; const isBucketed = (op: OperationMetadata) => op.isBucketed; +function getVisualizationType(state: State): VisualizationType | 'mixed' { + if (!state.layers.length) { + return ( + visualizationTypes.find(t => t.id === state.preferredSeriesType) ?? visualizationTypes[0] + ); + } + const visualizationType = visualizationTypes.find(t => t.id === state.layers[0].seriesType); + const seriesTypes = _.unique(state.layers.map(l => l.seriesType)); + + return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed'; +} + function getDescription(state?: State) { if (!state) { return { @@ -34,32 +46,31 @@ function getDescription(state?: State) { }; } + const visualizationType = getVisualizationType(state); + if (!state.layers.length) { - const visualizationType = visualizationTypes.find(v => v.id === state.preferredSeriesType)!; + const preferredType = visualizationType as VisualizationType; return { - icon: visualizationType.largeIcon || visualizationType.icon, - label: visualizationType.label, + icon: preferredType.largeIcon || preferredType.icon, + label: preferredType.label, }; } - const visualizationType = visualizationTypes.find(t => t.id === state.layers[0].seriesType)!; - const seriesTypes = _.unique(state.layers.map(l => l.seriesType)); - return { icon: - seriesTypes.length === 1 - ? visualizationType.largeIcon || visualizationType.icon - : chartMixedSVG, + visualizationType === 'mixed' + ? chartMixedSVG + : visualizationType.largeIcon || visualizationType.icon, label: - seriesTypes.length === 1 - ? visualizationType.label - : isHorizontalChart(state.layers) - ? i18n.translate('xpack.lens.xyVisualization.mixedBarHorizontalLabel', { - defaultMessage: 'Mixed horizontal bar', - }) - : i18n.translate('xpack.lens.xyVisualization.mixedLabel', { - defaultMessage: 'Mixed XY', - }), + visualizationType === 'mixed' + ? isHorizontalChart(state.layers) + ? i18n.translate('xpack.lens.xyVisualization.mixedBarHorizontalLabel', { + defaultMessage: 'Mixed horizontal bar', + }) + : i18n.translate('xpack.lens.xyVisualization.mixedLabel', { + defaultMessage: 'Mixed XY', + }) + : visualizationType.label, }; } @@ -67,6 +78,10 @@ export const xyVisualization: Visualization = { id: 'lnsXY', visualizationTypes, + getVisualizationTypeId(state) { + const type = getVisualizationType(state); + return type === 'mixed' ? type : type.id; + }, getLayerIds(state) { return state.layers.map(l => l.layerId); diff --git a/x-pack/plugins/lens/server/migrations.test.ts b/x-pack/plugins/lens/server/migrations.test.ts index e80308cc9acdb0..4cc330d40efd7f 100644 --- a/x-pack/plugins/lens/server/migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations.test.ts @@ -158,4 +158,124 @@ describe('Lens migrations', () => { ]); }); }); + + describe('7.8.0 auto timestamp', () => { + const context = {} as SavedObjectMigrationContext; + + const example = { + type: 'lens', + attributes: { + expression: `kibana + | kibana_context query="{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"}" filters="[]" + | lens_merge_tables layerIds="bd09dc71-a7e2-42d0-83bd-85df8291f03c" + tables={esaggs + index="ff959d40-b880-11e8-a6d9-e546fe2bba5f" + metricsAtAllLevels=false + partialRows=false + includeFormatHints=true + aggConfigs={ + lens_auto_date + aggConfigs="[{\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"products.created_on\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"auto\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}},{\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" + } + | lens_rename_columns idMap="{\\"col-0-1d9cc16c-1460-41de-88f8-471932ecbc97\\":{\\"label\\":\\"products.created_on\\",\\"dataType\\":\\"date\\",\\"operationType\\":\\"date_histogram\\",\\"sourceField\\":\\"products.created_on\\",\\"isBucketed\\":true,\\"scale\\":\\"interval\\",\\"params\\":{\\"interval\\":\\"auto\\"},\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\"},\\"col-1-66115819-8481-4917-a6dc-8ffb10dd02df\\":{\\"label\\":\\"Count of records\\",\\"dataType\\":\\"number\\",\\"operationType\\":\\"count\\",\\"suggestedPriority\\":0,\\"isBucketed\\":false,\\"scale\\":\\"ratio\\",\\"sourceField\\":\\"Records\\",\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\"}}" + } + | lens_xy_chart + xTitle="products.created_on" + yTitle="Count of records" + legend={lens_xy_legendConfig isVisible=true position="right"} + layers={lens_xy_layer + layerId="bd09dc71-a7e2-42d0-83bd-85df8291f03c" + hide=false + xAccessor="1d9cc16c-1460-41de-88f8-471932ecbc97" + yScaleType="linear" + xScaleType="time" + isHistogram=true + seriesType="bar_stacked" + accessors="66115819-8481-4917-a6dc-8ffb10dd02df" + columnToLabel="{\\"66115819-8481-4917-a6dc-8ffb10dd02df\\":\\"Count of records\\"}" + } + `, + state: { + datasourceStates: { + indexpattern: { + currentIndexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + layers: { + 'bd09dc71-a7e2-42d0-83bd-85df8291f03c': { + indexPatternId: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', + columns: { + '1d9cc16c-1460-41de-88f8-471932ecbc97': { + label: 'products.created_on', + dataType: 'date', + operationType: 'date_histogram', + sourceField: 'products.created_on', + isBucketed: true, + scale: 'interval', + params: { interval: 'auto' }, + }, + '66115819-8481-4917-a6dc-8ffb10dd02df': { + label: 'Count of records', + dataType: 'number', + operationType: 'count', + suggestedPriority: 0, + isBucketed: false, + scale: 'ratio', + sourceField: 'Records', + }, + }, + columnOrder: [ + '1d9cc16c-1460-41de-88f8-471932ecbc97', + '66115819-8481-4917-a6dc-8ffb10dd02df', + ], + }, + }, + }, + }, + datasourceMetaData: { + filterableIndexPatterns: [ + { id: 'ff959d40-b880-11e8-a6d9-e546fe2bba5f', title: 'kibana_sample_data_ecommerce' }, + ], + }, + visualization: { + legend: { isVisible: true, position: 'right' }, + preferredSeriesType: 'bar_stacked', + layers: [ + { + layerId: 'bd09dc71-a7e2-42d0-83bd-85df8291f03c', + accessors: ['66115819-8481-4917-a6dc-8ffb10dd02df'], + position: 'top', + seriesType: 'bar_stacked', + showGridlines: false, + xAccessor: '1d9cc16c-1460-41de-88f8-471932ecbc97', + }, + ], + }, + query: { query: '', language: 'kuery' }, + filters: [], + }, + title: 'Bar chart', + visualizationType: 'lnsXY', + }, + }; + + it('should remove the lens_auto_date expression', () => { + const result = migrations['7.8.0'](example, context); + expect(result.attributes.expression).toContain(`timeFields=\"products.created_on\"`); + }); + + it('should handle pre-migrated expression', () => { + const input = { + type: 'lens', + attributes: { + ...example.attributes, + expression: `kibana +| kibana_context query="{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"}" filters="[]" +| lens_merge_tables layerIds="bd09dc71-a7e2-42d0-83bd-85df8291f03c" + tables={esaggs index="ff959d40-b880-11e8-a6d9-e546fe2bba5f" metricsAtAllLevels=false partialRows=false includeFormatHints=true aggConfigs="[{\\"id\\":\\"1d9cc16c-1460-41de-88f8-471932ecbc97\\",\\"enabled\\":true,\\"type\\":\\"date_histogram\\",\\"schema\\":\\"segment\\",\\"params\\":{\\"field\\":\\"products.created_on\\",\\"useNormalizedEsInterval\\":true,\\"interval\\":\\"auto\\",\\"drop_partials\\":false,\\"min_doc_count\\":0,\\"extended_bounds\\":{}}},{\\"id\\":\\"66115819-8481-4917-a6dc-8ffb10dd02df\\",\\"enabled\\":true,\\"type\\":\\"count\\",\\"schema\\":\\"metric\\",\\"params\\":{}}]" timeFields=\"products.created_on\"} +| lens_xy_chart xTitle="products.created_on" yTitle="Count of records" legend={lens_xy_legendConfig isVisible=true position="right"} layers={}`, + }, + }; + const result = migrations['7.8.0'](input, context); + expect(result).toEqual(input); + }); + }); }); diff --git a/x-pack/plugins/lens/server/migrations.ts b/x-pack/plugins/lens/server/migrations.ts index 3d238723b7438c..583fba1a4a9992 100644 --- a/x-pack/plugins/lens/server/migrations.ts +++ b/x-pack/plugins/lens/server/migrations.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { cloneDeep } from 'lodash'; +import { cloneDeep, flow } from 'lodash'; +import { fromExpression, toExpression, Ast, ExpressionFunctionAST } from '@kbn/interpreter/common'; import { SavedObjectMigrationFn } from 'src/core/server'; interface XYLayerPre77 { @@ -14,7 +15,126 @@ interface XYLayerPre77 { accessors: string[]; } -export const migrations: Record = { +/** + * Removes the `lens_auto_date` subexpression from a stored expression + * string. For example: aggConfigs={lens_auto_date aggConfigs="JSON string"} + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const removeLensAutoDate: SavedObjectMigrationFn = (doc, context) => { + const expression: string = doc.attributes?.expression; + try { + const ast = fromExpression(expression); + const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => { + if (topNode.function !== 'lens_merge_tables') { + return topNode; + } + return { + ...topNode, + arguments: { + ...topNode.arguments, + tables: (topNode.arguments.tables as Ast[]).map(middleNode => { + return { + type: 'expression', + chain: middleNode.chain.map(node => { + // Check for sub-expression in aggConfigs + if ( + node.function === 'esaggs' && + typeof node.arguments.aggConfigs[0] !== 'string' + ) { + return { + ...node, + arguments: { + ...node.arguments, + aggConfigs: (node.arguments.aggConfigs[0] as Ast).chain[0].arguments + .aggConfigs, + }, + }; + } + return node; + }), + }; + }), + }, + }; + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + expression: toExpression({ ...ast, chain: newChain }), + }, + }; + } catch (e) { + context.log.warning(e.message); + return { ...doc }; + } +}; + +/** + * Adds missing timeField arguments to esaggs in the Lens expression + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const addTimeFieldToEsaggs: SavedObjectMigrationFn = (doc, context) => { + const expression: string = doc.attributes?.expression; + + try { + const ast = fromExpression(expression); + const newChain: ExpressionFunctionAST[] = ast.chain.map(topNode => { + if (topNode.function !== 'lens_merge_tables') { + return topNode; + } + return { + ...topNode, + arguments: { + ...topNode.arguments, + tables: (topNode.arguments.tables as Ast[]).map(middleNode => { + return { + type: 'expression', + chain: middleNode.chain.map(node => { + // Skip if there are any timeField arguments already, because that indicates + // the fix is already applied + if (node.function !== 'esaggs' || node.arguments.timeFields) { + return node; + } + const timeFields: string[] = []; + JSON.parse(node.arguments.aggConfigs[0] as string).forEach( + (agg: { type: string; params: { field: string } }) => { + if (agg.type !== 'date_histogram') { + return; + } + timeFields.push(agg.params.field); + } + ); + return { + ...node, + arguments: { + ...node.arguments, + timeFields, + }, + }; + }), + }; + }), + }, + }; + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + expression: toExpression({ ...ast, chain: newChain }), + }, + }; + } catch (e) { + context.log.warning(e.message); + return { ...doc }; + } +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const migrations: Record> = { '7.7.0': doc => { const newDoc = cloneDeep(doc); if (newDoc.attributes?.visualizationType === 'lnsXY') { @@ -34,4 +154,7 @@ export const migrations: Record = { } return newDoc; }, + // The order of these migrations matter, since the timefield migration relies on the aggConfigs + // sitting directly on the esaggs as an argument and not a nested function (which lens_auto_date was). + '7.8.0': flow(removeLensAutoDate, addTimeFieldToEsaggs), }; diff --git a/x-pack/plugins/lists/common/constants.ts b/x-pack/plugins/lists/common/constants.ts new file mode 100644 index 00000000000000..dbe31fed664131 --- /dev/null +++ b/x-pack/plugins/lists/common/constants.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. + */ + +/** + * Lists routes + */ +export const LIST_URL = `/api/lists`; +export const LIST_INDEX = `${LIST_URL}/index`; +export const LIST_ITEM_URL = `${LIST_URL}/items`; diff --git a/x-pack/legacy/plugins/actions/index.ts b/x-pack/plugins/lists/common/schemas/common/index.ts similarity index 90% rename from x-pack/legacy/plugins/actions/index.ts rename to x-pack/plugins/lists/common/schemas/common/index.ts index 276d1ea3accea6..a05e97ded38eea 100644 --- a/x-pack/legacy/plugins/actions/index.ts +++ b/x-pack/plugins/lists/common/schemas/common/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './server/'; +export * from './schemas'; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts new file mode 100644 index 00000000000000..edc037ed7a0b12 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { NonEmptyString } from '../types/non_empty_string'; + +export const name = t.string; +export type Name = t.TypeOf; +export const nameOrUndefined = t.union([name, t.undefined]); +export type NameOrUndefined = t.TypeOf; + +export const description = t.string; +export type Description = t.TypeOf; +export const descriptionOrUndefined = t.union([description, t.undefined]); +export type DescriptionOrUndefined = t.TypeOf; + +export const list_id = NonEmptyString; +export const list_idOrUndefined = t.union([list_id, t.undefined]); +export type List_idOrUndefined = t.TypeOf; + +export const item = t.string; +export const created_at = t.string; // TODO: Make this into an ISO Date string check +export const updated_at = t.string; // TODO: Make this into an ISO Date string check +export const updated_by = t.string; +export const created_by = t.string; +export const file = t.object; + +export const id = NonEmptyString; +export type Id = t.TypeOf; +export const idOrUndefined = t.union([id, t.undefined]); +export type IdOrUndefined = t.TypeOf; + +export const ip = t.string; +export const ipOrUndefined = t.union([ip, t.undefined]); + +export const keyword = t.string; +export const keywordOrUndefined = t.union([keyword, t.undefined]); + +export const value = t.string; +export const valueOrUndefined = t.union([value, t.undefined]); + +export const tie_breaker_id = t.string; // TODO: Use UUID for this instead of a string for validation +export const _index = t.string; + +export const type = t.keyof({ ip: null, keyword: null }); // TODO: Add the other data types here + +export const typeOrUndefined = t.union([type, t.undefined]); +export type Type = t.TypeOf; + +export const meta = t.object; +export type Meta = t.TypeOf; +export const metaOrUndefined = t.union([meta, t.undefined]); +export type MetaOrUndefined = t.TypeOf; + +export const esDataTypeUnion = t.union([t.type({ ip }), t.type({ keyword })]); +export type EsDataTypeUnion = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts b/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.ts new file mode 100644 index 00000000000000..4a825382c06e49 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/create_es_bulk_type.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. + */ + +import * as t from 'io-ts'; + +import { _index } from '../common/schemas'; + +export const createEsBulkTypeSchema = t.exact( + t.type({ + create: t.exact(t.type({ _index })), + }) +); + +export type CreateEsBulkTypeSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/index.ts b/x-pack/plugins/lists/common/schemas/elastic_query/index.ts new file mode 100644 index 00000000000000..d70dd09849fa6c --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export * from './update_es_list_schema'; +export * from './index_es_list_schema'; +export * from './update_es_list_item_schema'; +export * from './index_es_list_item_schema'; +export * from './create_es_bulk_type'; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.ts new file mode 100644 index 00000000000000..596498b64b7713 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + created_at, + created_by, + esDataTypeUnion, + list_id, + metaOrUndefined, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; + +export const indexEsListItemSchema = t.intersection([ + t.exact( + t.type({ + created_at, + created_by, + list_id, + meta: metaOrUndefined, + tie_breaker_id, + updated_at, + updated_by, + }) + ), + esDataTypeUnion, +]); + +export type IndexEsListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.ts new file mode 100644 index 00000000000000..e0924392628a90 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/index_es_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + created_at, + created_by, + description, + metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, +} from '../common/schemas'; + +export const indexEsListSchema = t.exact( + t.type({ + created_at, + created_by, + description, + meta: metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, + }) +); + +export type IndexEsListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts new file mode 100644 index 00000000000000..e4cf46bc39429c --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_item_schema.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { esDataTypeUnion, metaOrUndefined, updated_at, updated_by } from '../common/schemas'; + +export const updateEsListItemSchema = t.intersection([ + t.exact( + t.type({ + meta: metaOrUndefined, + updated_at, + updated_by, + }) + ), + esDataTypeUnion, +]); + +export type UpdateEsListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.ts new file mode 100644 index 00000000000000..8f23f3744e563d --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_query/update_es_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + descriptionOrUndefined, + metaOrUndefined, + nameOrUndefined, + updated_at, + updated_by, +} from '../common/schemas'; + +export const updateEsListSchema = t.exact( + t.type({ + description: descriptionOrUndefined, + meta: metaOrUndefined, + name: nameOrUndefined, + updated_at, + updated_by, + }) +); + +export type UpdateEsListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_response/index.ts b/x-pack/plugins/lists/common/schemas/elastic_response/index.ts new file mode 100644 index 00000000000000..6fbc6ef2930647 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_response/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 './search_es_list_item_schema'; +export * from './search_es_list_schema'; diff --git a/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_item_schema.ts new file mode 100644 index 00000000000000..902d3e6a9896e4 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + created_at, + created_by, + ipOrUndefined, + keywordOrUndefined, + list_id, + metaOrUndefined, + tie_breaker_id, + updated_at, + updated_by, +} from '../common/schemas'; + +export const searchEsListItemSchema = t.exact( + t.type({ + created_at, + created_by, + ip: ipOrUndefined, + keyword: keywordOrUndefined, + list_id, + meta: metaOrUndefined, + tie_breaker_id, + updated_at, + updated_by, + }) +); + +export type SearchEsListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_schema.ts b/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_schema.ts new file mode 100644 index 00000000000000..00a7c6f321d387 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/elastic_response/search_es_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + created_at, + created_by, + description, + metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, +} from '../common/schemas'; + +export const searchEsListSchema = t.exact( + t.type({ + created_at, + created_by, + description, + meta: metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, + }) +); + +export type SearchEsListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/index.ts b/x-pack/plugins/lists/common/schemas/index.ts new file mode 100644 index 00000000000000..6a60a6df55691a --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/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 './common'; +export * from './request'; +export * from './response'; +export * from './elastic_query'; +export * from './elastic_response'; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.ts new file mode 100644 index 00000000000000..8168e5a9838f20 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { idOrUndefined, list_id, metaOrUndefined, value } from '../common/schemas'; + +export const createListItemSchema = t.exact( + t.type({ + id: idOrUndefined, + list_id, + meta: metaOrUndefined, + value, + }) +); + +export type CreateListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_list_schema.test.ts new file mode 100644 index 00000000000000..ba791a55d17eb8 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_list_schema.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { exactCheck, foldLeftRight, getPaths } from '../../siem_common_deps'; + +import { getListRequest } from './mocks/utils'; +import { createListSchema } from './create_list_schema'; + +describe('create_list_schema', () => { + // TODO: Finish the tests for this + test('it should validate a typical lists request', () => { + const payload = getListRequest(); + const decoded = createListSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual({ + description: 'Description of a list item', + id: 'some-list-id', + name: 'Name of a list item', + type: 'ip', + }); + }); +}); diff --git a/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts new file mode 100644 index 00000000000000..353a4ecdafa0ce --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/create_list_schema.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { description, idOrUndefined, metaOrUndefined, name, type } from '../common/schemas'; + +export const createListSchema = t.exact( + t.type({ + description, + id: idOrUndefined, + meta: metaOrUndefined, + name, + type, + }) +); + +export type CreateListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.ts new file mode 100644 index 00000000000000..f4c1fb5c43eb0b --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; + +export const deleteListItemSchema = t.exact( + t.type({ + id: idOrUndefined, + list_id: list_idOrUndefined, + value: valueOrUndefined, + }) +); + +export type DeleteListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/delete_list_schema.ts new file mode 100644 index 00000000000000..fd6aa5b85f81ae --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/delete_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id } from '../common/schemas'; + +export const deleteListSchema = t.exact( + t.type({ + id, + }) +); + +export type DeleteListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts new file mode 100644 index 00000000000000..14b201bf8089de --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/export_list_item_query_schema.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { list_id } from '../common/schemas'; + +export const exportListItemQuerySchema = t.exact( + t.type({ + list_id, + // TODO: Add file_name here with a default value + }) +); + +export type ExportListItemQuerySchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.ts new file mode 100644 index 00000000000000..b8467d141bdd89 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_query_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { list_idOrUndefined, typeOrUndefined } from '../common/schemas'; + +export const importListItemQuerySchema = t.exact( + t.type({ list_id: list_idOrUndefined, type: typeOrUndefined }) +); + +export type ImportListItemQuerySchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.ts new file mode 100644 index 00000000000000..0cf01db8617f0e --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/import_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import { Readable } from 'stream'; + +import * as t from 'io-ts'; + +import { file } from '../common/schemas'; + +export const importListItemSchema = t.exact( + t.type({ + file, + }) +); + +export interface HapiReadableStream extends Readable { + hapi: { + filename: string; + }; +} + +/** + * Special interface since we are streaming in a file through a reader + */ +export interface ImportListItemSchema { + file: HapiReadableStream; +} diff --git a/x-pack/plugins/lists/common/schemas/request/index.ts b/x-pack/plugins/lists/common/schemas/request/index.ts new file mode 100644 index 00000000000000..d332ab1eb1bab3 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/index.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. + */ + +export * from './create_list_item_schema'; +export * from './create_list_schema'; +export * from './delete_list_item_schema'; +export * from './delete_list_schema'; +export * from './export_list_item_query_schema'; +export * from './import_list_item_schema'; +export * from './patch_list_item_schema'; +export * from './patch_list_schema'; +export * from './read_list_item_schema'; +export * from './read_list_schema'; +export * from './import_list_item_query_schema'; +export * from './update_list_schema'; +export * from './update_list_item_schema'; diff --git a/x-pack/plugins/lists/common/schemas/request/mocks/utils.ts b/x-pack/plugins/lists/common/schemas/request/mocks/utils.ts new file mode 100644 index 00000000000000..e5d189db8490be --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/mocks/utils.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CreateListSchema } from '../create_list_schema'; + +export const getListRequest = (): CreateListSchema => ({ + description: 'Description of a list item', + id: 'some-list-id', + meta: undefined, + name: 'Name of a list item', + type: 'ip', +}); diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.ts new file mode 100644 index 00000000000000..3e8198a5109b31 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, metaOrUndefined, valueOrUndefined } from '../common/schemas'; + +export const patchListItemSchema = t.exact( + t.type({ + id, + meta: metaOrUndefined, + value: valueOrUndefined, + }) +); + +export type PatchListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.ts new file mode 100644 index 00000000000000..efcb81fc8be2ac --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/patch_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { descriptionOrUndefined, id, metaOrUndefined, nameOrUndefined } from '../common/schemas'; + +export const patchListSchema = t.exact( + t.type({ + description: descriptionOrUndefined, + id, + meta: metaOrUndefined, + name: nameOrUndefined, + }) +); + +export type PatchListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.ts new file mode 100644 index 00000000000000..9ea14a2a21ed8b --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { idOrUndefined, list_idOrUndefined, valueOrUndefined } from '../common/schemas'; + +export const readListItemSchema = t.exact( + t.type({ id: idOrUndefined, list_id: list_idOrUndefined, value: valueOrUndefined }) +); + +export type ReadListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/read_list_schema.ts new file mode 100644 index 00000000000000..8803346709c313 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/read_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id } from '../common/schemas'; + +export const readListSchema = t.exact( + t.type({ + id, + }) +); + +export type ReadListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.ts new file mode 100644 index 00000000000000..e1f88bae66e0f8 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_list_item_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { id, metaOrUndefined, value } from '../common/schemas'; + +export const updateListItemSchema = t.exact( + t.type({ + id, + meta: metaOrUndefined, + value, + }) +); + +export type UpdateListItemSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_list_schema.ts new file mode 100644 index 00000000000000..d51ed60c41b56f --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/update_list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { description, id, metaOrUndefined, name } from '../common/schemas'; + +export const updateListSchema = t.exact( + t.type({ + description, + id, + meta: metaOrUndefined, + name, + }) +); + +export type UpdateListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/acknowledge_schema.ts b/x-pack/plugins/lists/common/schemas/response/acknowledge_schema.ts new file mode 100644 index 00000000000000..55aaf587ac06ba --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/acknowledge_schema.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 * as t from 'io-ts'; + +export const acknowledgeSchema = t.type({ acknowledged: t.boolean }); + +export type AcknowledgeSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/index.ts b/x-pack/plugins/lists/common/schemas/response/index.ts new file mode 100644 index 00000000000000..3f11adf58d8d42 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './list_item_schema'; +export * from './list_schema'; +export * from './acknowledge_schema'; +export * from './list_item_index_exist_schema'; diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_index_exist_schema.ts b/x-pack/plugins/lists/common/schemas/response/list_item_index_exist_schema.ts new file mode 100644 index 00000000000000..bf2bf21d2c216e --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/list_item_index_exist_schema.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +export const listItemIndexExistSchema = t.type({ + list_index: t.boolean, + list_item_index: t.boolean, +}); + +export type ListItemIndexExistSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/list_item_schema.ts new file mode 100644 index 00000000000000..6c2f2ed9a70952 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/list_item_schema.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 * as t from 'io-ts'; + +/* eslint-disable @typescript-eslint/camelcase */ + +import { + created_at, + created_by, + id, + list_id, + metaOrUndefined, + tie_breaker_id, + type, + updated_at, + updated_by, + value, +} from '../common/schemas'; + +export const listItemSchema = t.exact( + t.type({ + created_at, + created_by, + id, + list_id, + meta: metaOrUndefined, + tie_breaker_id, + type, + updated_at, + updated_by, + value, + }) +); + +export type ListItemSchema = t.TypeOf; + +export const listItemArraySchema = t.array(listItemSchema); +export type ListItemArraySchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/response/list_schema.ts b/x-pack/plugins/lists/common/schemas/response/list_schema.ts new file mode 100644 index 00000000000000..cad449766ceb4f --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/response/list_schema.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. + */ + +/* eslint-disable @typescript-eslint/camelcase */ + +import * as t from 'io-ts'; + +import { + created_at, + created_by, + description, + id, + metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, +} from '../common/schemas'; + +export const listSchema = t.exact( + t.type({ + created_at, + created_by, + description, + id, + meta: metaOrUndefined, + name, + tie_breaker_id, + type, + updated_at, + updated_by, + }) +); + +export type ListSchema = t.TypeOf; diff --git a/x-pack/plugins/lists/common/schemas/types/non_empty_string.ts b/x-pack/plugins/lists/common/schemas/types/non_empty_string.ts new file mode 100644 index 00000000000000..d1e2094bbcad3a --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/types/non_empty_string.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 * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +export type NonEmptyStringC = t.Type; + +/** + * Types the NonEmptyString as: + * - A string that is not empty + */ +export const NonEmptyString: NonEmptyStringC = new t.Type( + 'NonEmptyString', + t.string.is, + (input, context): Either => { + if (typeof input === 'string' && input.trim() !== '') { + return t.success(input); + } else { + return t.failure(input, context); + } + }, + t.identity +); diff --git a/x-pack/plugins/lists/common/siem_common_deps.ts b/x-pack/plugins/lists/common/siem_common_deps.ts new file mode 100644 index 00000000000000..5e74753a6f0bde --- /dev/null +++ b/x-pack/plugins/lists/common/siem_common_deps.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 { getPaths, foldLeftRight } from '../../siem/server/utils/build_validation/__mocks__/utils'; +export { exactCheck } from '../../siem/server/utils/build_validation/exact_check'; diff --git a/x-pack/plugins/lists/kibana.json b/x-pack/plugins/lists/kibana.json new file mode 100644 index 00000000000000..b7aaac6d3fc760 --- /dev/null +++ b/x-pack/plugins/lists/kibana.json @@ -0,0 +1,10 @@ +{ + "configPath": ["xpack", "lists"], + "id": "lists", + "kibanaVersion": "kibana", + "requiredPlugins": [], + "optionalPlugins": ["spaces", "security"], + "server": true, + "ui": false, + "version": "8.0.0" +} diff --git a/x-pack/plugins/lists/server/config.ts b/x-pack/plugins/lists/server/config.ts new file mode 100644 index 00000000000000..3e7995b2ce8d01 --- /dev/null +++ b/x-pack/plugins/lists/server/config.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TypeOf, schema } from '@kbn/config-schema'; + +export const ConfigSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), + listIndex: schema.string({ defaultValue: '.lists' }), + listItemIndex: schema.string({ defaultValue: '.items' }), +}); + +export type ConfigType = TypeOf; diff --git a/x-pack/plugins/lists/server/create_config.ts b/x-pack/plugins/lists/server/create_config.ts new file mode 100644 index 00000000000000..3158fabda935f1 --- /dev/null +++ b/x-pack/plugins/lists/server/create_config.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 { map } from 'rxjs/operators'; +import { PluginInitializerContext } from 'kibana/server'; +import { Observable } from 'rxjs'; + +import { ConfigType } from './config'; + +export const createConfig$ = ( + context: PluginInitializerContext +): Observable> => { + return context.config.create().pipe(map(config => config)); +}; diff --git a/x-pack/plugins/lists/server/error_with_status_code.ts b/x-pack/plugins/lists/server/error_with_status_code.ts new file mode 100644 index 00000000000000..f9bbbc4abad27e --- /dev/null +++ b/x-pack/plugins/lists/server/error_with_status_code.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export class ErrorWithStatusCode extends Error { + private readonly statusCode: number; + + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + } + + public getStatusCode = (): number => this.statusCode; +} diff --git a/x-pack/plugins/lists/server/get_space_id.test.ts b/x-pack/plugins/lists/server/get_space_id.test.ts new file mode 100644 index 00000000000000..9c1d11b71984d1 --- /dev/null +++ b/x-pack/plugins/lists/server/get_space_id.test.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 { httpServerMock } from 'src/core/server/mocks'; +import { KibanaRequest } from 'src/core/server'; + +import { spacesServiceMock } from '../../spaces/server/spaces_service/spaces_service.mock'; + +import { getSpaceId } from './get_space_id'; + +describe('get_space_id', () => { + let request = KibanaRequest.from(httpServerMock.createRawRequest({})); + beforeEach(() => { + request = KibanaRequest.from(httpServerMock.createRawRequest({})); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns "default" as the space id given a space id of "default"', () => { + const spaces = spacesServiceMock.createSetupContract(); + const space = getSpaceId({ request, spaces }); + expect(space).toEqual('default'); + }); + + test('it returns "another-space" as the space id given a space id of "another-space"', () => { + const spaces = spacesServiceMock.createSetupContract('another-space'); + const space = getSpaceId({ request, spaces }); + expect(space).toEqual('another-space'); + }); + + test('it returns "default" as the space id given a space id of undefined', () => { + const space = getSpaceId({ request, spaces: undefined }); + expect(space).toEqual('default'); + }); + + test('it returns "default" as the space id given a space id of null', () => { + const space = getSpaceId({ request, spaces: null }); + expect(space).toEqual('default'); + }); +}); diff --git a/x-pack/plugins/lists/server/get_space_id.ts b/x-pack/plugins/lists/server/get_space_id.ts new file mode 100644 index 00000000000000..f224e37e044672 --- /dev/null +++ b/x-pack/plugins/lists/server/get_space_id.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. + */ + +import { KibanaRequest } from 'kibana/server'; + +import { SpacesServiceSetup } from '../../spaces/server'; + +export const getSpaceId = ({ + spaces, + request, +}: { + spaces: SpacesServiceSetup | undefined | null; + request: KibanaRequest; +}): string => spaces?.getSpaceId(request) ?? 'default'; diff --git a/x-pack/plugins/lists/server/get_user.test.ts b/x-pack/plugins/lists/server/get_user.test.ts new file mode 100644 index 00000000000000..0992e3c361fcfe --- /dev/null +++ b/x-pack/plugins/lists/server/get_user.test.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 { httpServerMock } from 'src/core/server/mocks'; +import { KibanaRequest } from 'src/core/server'; + +import { securityMock } from '../../security/server/mocks'; +import { SecurityPluginSetup } from '../../security/server'; + +import { getUser } from './get_user'; + +describe('get_user', () => { + let request = KibanaRequest.from(httpServerMock.createRawRequest({})); + beforeEach(() => { + jest.clearAllMocks(); + request = KibanaRequest.from(httpServerMock.createRawRequest({})); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns "bob" as the user given a security request with "bob"', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue({ username: 'bob' }); + const user = getUser({ request, security }); + expect(user).toEqual('bob'); + }); + + test('it returns "alice" as the user given a security request with "alice"', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue({ username: 'alice' }); + const user = getUser({ request, security }); + expect(user).toEqual('alice'); + }); + + test('it returns "elastic" as the user given null as the current user', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue(null); + const user = getUser({ request, security }); + expect(user).toEqual('elastic'); + }); + + test('it returns "elastic" as the user given undefined as the current user', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue(undefined); + const user = getUser({ request, security }); + expect(user).toEqual('elastic'); + }); + + test('it returns "elastic" as the user given undefined as the plugin', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue(undefined); + const user = getUser({ request, security: undefined }); + expect(user).toEqual('elastic'); + }); + + test('it returns "elastic" as the user given null as the plugin', () => { + const security: SecurityPluginSetup = securityMock.createSetup(); + security.authc.getCurrentUser = jest.fn().mockReturnValue(undefined); + const user = getUser({ request, security: null }); + expect(user).toEqual('elastic'); + }); +}); diff --git a/x-pack/plugins/lists/server/get_user.ts b/x-pack/plugins/lists/server/get_user.ts new file mode 100644 index 00000000000000..3b59853d0ab62a --- /dev/null +++ b/x-pack/plugins/lists/server/get_user.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 { KibanaRequest } from 'kibana/server'; + +import { SecurityPluginSetup } from '../../security/server'; + +export interface GetUserOptions { + security: SecurityPluginSetup | null | undefined; + request: KibanaRequest; +} + +export const getUser = ({ security, request }: GetUserOptions): string => { + if (security != null) { + const authenticatedUser = security.authc.getCurrentUser(request); + if (authenticatedUser != null) { + return authenticatedUser.username; + } else { + return 'elastic'; + } + } else { + return 'elastic'; + } +}; diff --git a/x-pack/plugins/lists/server/index.ts b/x-pack/plugins/lists/server/index.ts new file mode 100644 index 00000000000000..c1e577aa601953 --- /dev/null +++ b/x-pack/plugins/lists/server/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from '../../../../src/core/server'; + +import { ConfigSchema } from './config'; +import { ListPlugin } from './plugin'; + +export const config = { schema: ConfigSchema }; +export const plugin = (initializerContext: PluginInitializerContext): ListPlugin => + new ListPlugin(initializerContext); diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts new file mode 100644 index 00000000000000..2498c36967a536 --- /dev/null +++ b/x-pack/plugins/lists/server/plugin.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 { first } from 'rxjs/operators'; +import { Logger, PluginInitializerContext } from 'kibana/server'; +import { CoreSetup } from 'src/core/server'; + +import { SecurityPluginSetup } from '../../security/server'; +import { SpacesServiceSetup } from '../../spaces/server'; + +import { ConfigType } from './config'; +import { initRoutes } from './routes/init_routes'; +import { ListClient } from './services/lists/client'; +import { ContextProvider, ContextProviderReturn, PluginsSetup } from './types'; +import { createConfig$ } from './create_config'; +import { getSpaceId } from './get_space_id'; +import { getUser } from './get_user'; + +export class ListPlugin { + private readonly logger: Logger; + private spaces: SpacesServiceSetup | undefined | null; + private config: ConfigType | undefined | null; + private security: SecurityPluginSetup | undefined | null; + + constructor(private readonly initializerContext: PluginInitializerContext) { + this.logger = this.initializerContext.logger.get(); + } + + public async setup(core: CoreSetup, plugins: PluginsSetup): Promise { + const config = await createConfig$(this.initializerContext) + .pipe(first()) + .toPromise(); + + this.logger.error( + 'You have activated the lists values feature flag which is NOT currently supported for Elastic Security! You should turn this feature flag off immediately by un-setting "xpack.lists.enabled: true" in kibana.yml and restarting Kibana' + ); + this.spaces = plugins.spaces?.spacesService; + this.config = config; + this.security = plugins.security; + + core.http.registerRouteHandlerContext('lists', this.createRouteHandlerContext()); + const router = core.http.createRouter(); + initRoutes(router); + } + + public start(): void { + this.logger.debug('Starting plugin'); + } + + public stop(): void { + this.logger.debug('Stopping plugin'); + } + + private createRouteHandlerContext = (): ContextProvider => { + return async (context, request): ContextProviderReturn => { + const { spaces, config, security } = this; + const { + core: { + elasticsearch: { + dataClient: { callAsCurrentUser }, + }, + }, + } = context; + if (config == null) { + throw new TypeError('Configuration is required for this plugin to operate'); + } else { + const spaceId = getSpaceId({ request, spaces }); + const user = getUser({ request, security }); + return { + getListClient: (): ListClient => + new ListClient({ + callCluster: callAsCurrentUser, + config, + request, + security, + spaceId, + user, + }), + }; + } + }; + }; +} diff --git a/x-pack/plugins/lists/server/routes/create_list_index_route.ts b/x-pack/plugins/lists/server/routes/create_list_index_route.ts new file mode 100644 index 00000000000000..1c893fb757c5d0 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_list_index_route.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { LIST_INDEX } from '../../common/constants'; +import { acknowledgeSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const createListIndexRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: LIST_INDEX, + validate: false, + }, + async (context, _, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const lists = getListClient(context); + const listIndexExists = await lists.getListIndexExists(); + const listItemIndexExists = await lists.getListItemIndexExists(); + + if (listIndexExists && listItemIndexExists) { + return siemResponse.error({ + body: `index: "${lists.getListIndex()}" and "${lists.getListItemIndex()}" already exists`, + statusCode: 409, + }); + } else { + const policyExists = await lists.getListPolicyExists(); + const policyListItemExists = await lists.getListItemPolicyExists(); + + if (!policyExists) { + await lists.setListPolicy(); + } + if (!policyListItemExists) { + await lists.setListItemPolicy(); + } + + const templateExists = await lists.getListTemplateExists(); + const templateListItemsExists = await lists.getListItemTemplateExists(); + + if (!templateExists) { + await lists.setListTemplate(); + } + + if (!templateListItemsExists) { + await lists.setListItemTemplate(); + } + + if (!listIndexExists) { + await lists.createListBootStrapIndex(); + } + if (!listItemIndexExists) { + await lists.createListItemBootStrapIndex(); + } + + const [validated, errors] = validate({ acknowledged: true }, acknowledgeSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/create_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_list_item_route.ts new file mode 100644 index 00000000000000..68622e98cbc526 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_list_item_route.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { createListItemSchema, listItemSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const createListItemRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: LIST_ITEM_URL, + validate: { + body: buildRouteValidation(createListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId, value, meta } = request.body; + const lists = getListClient(context); + const list = await lists.getList({ id: listId }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } else { + const listItem = await lists.getListItemByValue({ listId, type: list.type, value }); + if (listItem.length !== 0) { + return siemResponse.error({ + body: `list_id: "${listId}" already contains the given value: ${value}`, + statusCode: 409, + }); + } else { + const createdListItem = await lists.createListItem({ + id, + listId, + meta, + type: list.type, + value, + }); + const [validated, errors] = validate(createdListItem, listItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/create_list_route.ts b/x-pack/plugins/lists/server/routes/create_list_route.ts new file mode 100644 index 00000000000000..0f3c404c53cfd4 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/create_list_route.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { createListSchema, listSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const createListRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: LIST_URL, + validate: { + body: buildRouteValidation(createListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { name, description, id, type, meta } = request.body; + const lists = getListClient(context); + const listExists = await lists.getListIndexExists(); + if (!listExists) { + return siemResponse.error({ + body: `To create a list, the index must exist first. Index "${lists.getListIndex()}" does not exist`, + statusCode: 400, + }); + } else { + if (id != null) { + const list = await lists.getList({ id }); + if (list != null) { + return siemResponse.error({ + body: `list id: "${id}" already exists`, + statusCode: 409, + }); + } + } + const list = await lists.createList({ description, id, meta, name, type }); + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts new file mode 100644 index 00000000000000..424c3f45aac404 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_INDEX } from '../../common/constants'; +import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { acknowledgeSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +/** + * Deletes all of the indexes, template, ilm policies, and aliases. You can check + * this by looking at each of these settings from ES after a deletion: + * + * GET /_template/.lists-default + * GET /.lists-default-000001/ + * GET /_ilm/policy/.lists-default + * GET /_alias/.lists-default + * + * GET /_template/.items-default + * GET /.items-default-000001/ + * GET /_ilm/policy/.items-default + * GET /_alias/.items-default + * + * And ensuring they're all gone + */ +export const deleteListIndexRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: LIST_INDEX, + validate: false, + }, + async (context, _, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const lists = getListClient(context); + const listIndexExists = await lists.getListIndexExists(); + const listItemIndexExists = await lists.getListItemIndexExists(); + + if (!listIndexExists && !listItemIndexExists) { + return siemResponse.error({ + body: `index: "${lists.getListIndex()}" and "${lists.getListItemIndex()}" does not exist`, + statusCode: 404, + }); + } else { + if (listIndexExists) { + await lists.deleteListIndex(); + } + if (listItemIndexExists) { + await lists.deleteListItemIndex(); + } + + const listsPolicyExists = await lists.getListPolicyExists(); + const listItemPolicyExists = await lists.getListItemPolicyExists(); + + if (listsPolicyExists) { + await lists.deleteListPolicy(); + } + if (listItemPolicyExists) { + await lists.deleteListItemPolicy(); + } + + const listsTemplateExists = await lists.getListTemplateExists(); + const listItemTemplateExists = await lists.getListItemTemplateExists(); + + if (listsTemplateExists) { + await lists.deleteListTemplate(); + } + if (listItemTemplateExists) { + await lists.deleteListItemTemplate(); + } + + const [validated, errors] = validate({ acknowledged: true }, acknowledgeSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts new file mode 100644 index 00000000000000..51b4eb9f02cc26 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteListItemSchema, listItemArraySchema, listItemSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const deleteListItemRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: LIST_ITEM_URL, + validate: { + query: buildRouteValidation(deleteListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId, value } = request.query; + const lists = getListClient(context); + if (id != null) { + const deleted = await lists.deleteListItem({ id }); + if (deleted == null) { + return siemResponse.error({ + body: `list item with id: "${id}" item not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, listItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else if (listId != null && value != null) { + const list = await lists.getList({ id: listId }); + if (list == null) { + return siemResponse.error({ + body: `list_id: "${listId}" does not exist`, + statusCode: 404, + }); + } else { + const deleted = await lists.deleteListItemByValue({ listId, type: list.type, value }); + if (deleted == null || deleted.length === 0) { + return siemResponse.error({ + body: `list_id: "${listId}" with ${value} was not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, listItemArraySchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } else { + return siemResponse.error({ + body: `Either "list_id" or "id" needs to be defined in the request`, + statusCode: 400, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts new file mode 100644 index 00000000000000..e89355b7689c5e --- /dev/null +++ b/x-pack/plugins/lists/server/routes/delete_list_route.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 { IRouter } from 'kibana/server'; + +import { LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { deleteListSchema, listSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const deleteListRoute = (router: IRouter): void => { + router.delete( + { + options: { + tags: ['access:lists'], + }, + path: LIST_URL, + validate: { + query: buildRouteValidation(deleteListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const lists = getListClient(context); + const { id } = request.query; + const deleted = await lists.deleteList({ id }); + if (deleted == null) { + return siemResponse.error({ + body: `list id: "${id}" was not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(deleted, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/export_list_item_route.ts b/x-pack/plugins/lists/server/routes/export_list_item_route.ts new file mode 100644 index 00000000000000..32b99bfc512bf2 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/export_list_item_route.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Stream } from 'stream'; + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { exportListItemQuerySchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const exportListItemRoute = (router: IRouter): void => { + router.post( + { + options: { + tags: ['access:lists'], + }, + path: `${LIST_ITEM_URL}/_export`, + validate: { + query: buildRouteValidation(exportListItemQuerySchema), + // TODO: Do we want to add a body here like export_rules_route and allow a size limit? + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { list_id: listId } = request.query; + const lists = getListClient(context); + const list = await lists.getList({ id: listId }); + if (list == null) { + return siemResponse.error({ + body: `list_id: ${listId} does not exist`, + statusCode: 400, + }); + } else { + // TODO: Allow the API to override the name of the file to export + const fileName = list.name; + + const stream = new Stream.PassThrough(); + lists.exportListItemsToStream({ listId, stream, stringToAppend: '\n' }); + return response.ok({ + body: stream, + headers: { + 'Content-Disposition': `attachment; filename="${fileName}"`, + 'Content-Type': 'text/plain', + }, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/import_list_item_route.ts new file mode 100644 index 00000000000000..a3b6a520a4ecfc --- /dev/null +++ b/x-pack/plugins/lists/server/routes/import_list_item_route.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { + ImportListItemSchema, + importListItemQuerySchema, + importListItemSchema, + listSchema, +} from '../../common/schemas'; + +import { getListClient } from '.'; + +export const importListItemRoute = (router: IRouter): void => { + router.post( + { + options: { + body: { + output: 'stream', + }, + tags: ['access:lists'], + }, + path: `${LIST_ITEM_URL}/_import`, + validate: { + body: buildRouteValidation( + importListItemSchema + ), + query: buildRouteValidation(importListItemQuerySchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { list_id: listId, type } = request.query; + const lists = getListClient(context); + if (listId != null) { + const list = await lists.getList({ id: listId }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 409, + }); + } + await lists.importListItemsToStream({ + listId, + meta: undefined, + stream: request.body.file, + type: list.type, + }); + + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } else if (type != null) { + const { filename } = request.body.file.hapi; + // TODO: Should we prevent the same file from being uploaded multiple times? + const list = await lists.createListIfItDoesNotExist({ + description: `File uploaded from file system of ${filename}`, + id: filename, + meta: undefined, + name: filename, + type, + }); + await lists.importListItemsToStream({ + listId: list.id, + meta: undefined, + stream: request.body.file, + type: list.type, + }); + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } else { + return siemResponse.error({ + body: 'Either type or list_id need to be defined in the query', + statusCode: 400, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts new file mode 100644 index 00000000000000..4951cddc56939f --- /dev/null +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './create_list_index_route'; +export * from './create_list_item_route'; +export * from './create_list_route'; +export * from './delete_list_index_route'; +export * from './delete_list_item_route'; +export * from './delete_list_route'; +export * from './export_list_item_route'; +export * from './import_list_item_route'; +export * from './init_routes'; +export * from './patch_list_item_route'; +export * from './patch_list_route'; +export * from './read_list_index_route'; +export * from './read_list_item_route'; +export * from './read_list_route'; +export * from './utils'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts new file mode 100644 index 00000000000000..924dd086ee7080 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/init_routes.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 { IRouter } from 'kibana/server'; + +import { updateListRoute } from './update_list_route'; +import { updateListItemRoute } from './update_list_item_route'; + +import { + createListIndexRoute, + createListItemRoute, + createListRoute, + deleteListIndexRoute, + deleteListItemRoute, + deleteListRoute, + exportListItemRoute, + importListItemRoute, + patchListItemRoute, + patchListRoute, + readListIndexRoute, + readListItemRoute, + readListRoute, +} from '.'; + +export const initRoutes = (router: IRouter): void => { + // lists + createListRoute(router); + readListRoute(router); + updateListRoute(router); + deleteListRoute(router); + patchListRoute(router); + + // lists items + createListItemRoute(router); + readListItemRoute(router); + updateListItemRoute(router); + deleteListItemRoute(router); + patchListItemRoute(router); + exportListItemRoute(router); + importListItemRoute(router); + + // indexes of lists + createListIndexRoute(router); + readListIndexRoute(router); + deleteListIndexRoute(router); +}; diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts new file mode 100644 index 00000000000000..e18fd0618b133b --- /dev/null +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listItemSchema, patchListItemSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const patchListItemRoute = (router: IRouter): void => { + router.patch( + { + options: { + tags: ['access:lists'], + }, + path: LIST_ITEM_URL, + validate: { + body: buildRouteValidation(patchListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { value, id, meta } = request.body; + const lists = getListClient(context); + const listItem = await lists.updateListItem({ + id, + meta, + value, + }); + if (listItem == null) { + return siemResponse.error({ + body: `list item id: "${id}" not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(listItem, listItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts new file mode 100644 index 00000000000000..9d3fa4db8ccd05 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/patch_list_route.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 { IRouter } from 'kibana/server'; + +import { LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listSchema, patchListSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const patchListRoute = (router: IRouter): void => { + router.patch( + { + options: { + tags: ['access:lists'], + }, + path: LIST_URL, + validate: { + body: buildRouteValidation(patchListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { name, description, id, meta } = request.body; + const lists = getListClient(context); + const list = await lists.updateList({ description, id, meta, name }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${id}" found found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_list_index_route.ts b/x-pack/plugins/lists/server/routes/read_list_index_route.ts new file mode 100644 index 00000000000000..248fc72666d709 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_list_index_route.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 { IRouter } from 'kibana/server'; + +import { LIST_INDEX } from '../../common/constants'; +import { buildSiemResponse, transformError, validate } from '../siem_server_deps'; +import { listItemIndexExistSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const readListIndexRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: LIST_INDEX, + validate: false, + }, + async (context, _, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const lists = getListClient(context); + const listIndexExists = await lists.getListIndexExists(); + const listItemIndexExists = await lists.getListItemIndexExists(); + + if (listIndexExists || listItemIndexExists) { + const [validated, errors] = validate( + { list_index: listIndexExists, lists_item_index: listItemIndexExists }, + listItemIndexExistSchema + ); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } else if (!listIndexExists && listItemIndexExists) { + return siemResponse.error({ + body: `index ${lists.getListIndex()} does not exist`, + statusCode: 404, + }); + } else if (!listItemIndexExists && listIndexExists) { + return siemResponse.error({ + body: `index ${lists.getListItemIndex()} does not exist`, + statusCode: 404, + }); + } else { + return siemResponse.error({ + body: `index ${lists.getListIndex()} and index ${lists.getListItemIndex()} does not exist`, + statusCode: 404, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts new file mode 100644 index 00000000000000..0a60cba786f049 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.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 { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listItemArraySchema, listItemSchema, readListItemSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const readListItemRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: LIST_ITEM_URL, + validate: { + query: buildRouteValidation(readListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId, value } = request.query; + const lists = getListClient(context); + if (id != null) { + const listItem = await lists.getListItem({ id }); + if (listItem == null) { + return siemResponse.error({ + body: `list item id: "${id}" does not exist`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(listItem, listItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } else if (listId != null && value != null) { + const list = await lists.getList({ id: listId }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${listId}" does not exist`, + statusCode: 404, + }); + } else { + const listItem = await lists.getListItemByValue({ + listId, + type: list.type, + value, + }); + if (listItem.length === 0) { + return siemResponse.error({ + body: `list_id: "${listId}" item of ${value} does not exist`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(listItem, listItemArraySchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } + } else { + return siemResponse.error({ + body: `Either "list_id" or "id" needs to be defined in the request`, + statusCode: 400, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/read_list_route.ts b/x-pack/plugins/lists/server/routes/read_list_route.ts new file mode 100644 index 00000000000000..c30eadfca0b650 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/read_list_route.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 { IRouter } from 'kibana/server'; + +import { LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listSchema, readListSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const readListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists'], + }, + path: LIST_URL, + validate: { + query: buildRouteValidation(readListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id } = request.query; + const lists = getListClient(context); + const list = await lists.getList({ id }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${id}" does not exist`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts new file mode 100644 index 00000000000000..494d57b93b8e47 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { LIST_ITEM_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listItemSchema, updateListItemSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const updateListItemRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: LIST_ITEM_URL, + validate: { + body: buildRouteValidation(updateListItemSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { value, id, meta } = request.body; + const lists = getListClient(context); + const listItem = await lists.updateListItem({ + id, + meta, + value, + }); + if (listItem == null) { + return siemResponse.error({ + body: `list item id: "${id}" not found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(listItem, listItemSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts new file mode 100644 index 00000000000000..6ace61e46a7802 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/update_list_route.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 { IRouter } from 'kibana/server'; + +import { LIST_URL } from '../../common/constants'; +import { + buildRouteValidation, + buildSiemResponse, + transformError, + validate, +} from '../siem_server_deps'; +import { listSchema, updateListSchema } from '../../common/schemas'; + +import { getListClient } from '.'; + +export const updateListRoute = (router: IRouter): void => { + router.put( + { + options: { + tags: ['access:lists'], + }, + path: LIST_URL, + validate: { + body: buildRouteValidation(updateListSchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { name, description, id, meta } = request.body; + const lists = getListClient(context); + const list = await lists.updateList({ description, id, meta, name }); + if (list == null) { + return siemResponse.error({ + body: `list id: "${id}" found found`, + statusCode: 404, + }); + } else { + const [validated, errors] = validate(list, listSchema); + if (errors != null) { + return siemResponse.error({ body: errors, statusCode: 500 }); + } else { + return response.ok({ body: validated ?? {} }); + } + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts new file mode 100644 index 00000000000000..a16163ec0fa3a8 --- /dev/null +++ b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RequestHandlerContext } from 'kibana/server'; + +import { ListClient } from '../../services/lists/client'; +import { ErrorWithStatusCode } from '../../error_with_status_code'; + +export const getListClient = (context: RequestHandlerContext): ListClient => { + const lists = context.lists?.getListClient(); + if (lists == null) { + throw new ErrorWithStatusCode('Lists is not found as a plugin', 404); + } else { + return lists; + } +}; diff --git a/x-pack/legacy/plugins/alerting/index.ts b/x-pack/plugins/lists/server/routes/utils/index.ts similarity index 87% rename from x-pack/legacy/plugins/alerting/index.ts rename to x-pack/plugins/lists/server/routes/utils/index.ts index 0d0a698841269f..a601bdfc003c53 100644 --- a/x-pack/legacy/plugins/alerting/index.ts +++ b/x-pack/plugins/lists/server/routes/utils/index.ts @@ -3,5 +3,4 @@ * 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 './server'; +export * from './get_list_client'; diff --git a/x-pack/plugins/lists/server/scripts/check_env_variables.sh b/x-pack/plugins/lists/server/scripts/check_env_variables.sh new file mode 100755 index 00000000000000..fb3bbbe0fad186 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/check_env_variables.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +# Add this to the start of any scripts to detect if env variables are set + +set -e + +if [ -z "${ELASTICSEARCH_USERNAME}" ]; then + echo "Set ELASTICSEARCH_USERNAME in your environment" + exit 1 +fi + +if [ -z "${ELASTICSEARCH_PASSWORD}" ]; then + echo "Set ELASTICSEARCH_PASSWORD in your environment" + exit 1 +fi + +if [ -z "${ELASTICSEARCH_URL}" ]; then + echo "Set ELASTICSEARCH_URL in your environment" + exit 1 +fi + +if [ -z "${KIBANA_URL}" ]; then + echo "Set KIBANA_URL in your environment" + exit 1 +fi + +if [ -z "${TASK_MANAGER_INDEX}" ]; then + echo "Set TASK_MANAGER_INDEX in your environment" + exit 1 +fi + +if [ -z "${KIBANA_INDEX}" ]; then + echo "Set KIBANA_INDEX in your environment" + exit 1 +fi diff --git a/x-pack/plugins/lists/server/scripts/delete_all_lists.sh b/x-pack/plugins/lists/server/scripts/delete_all_lists.sh new file mode 100755 index 00000000000000..5b65bb14414c70 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_all_lists.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_all_lists.sh +# https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html + + +# Delete all the main lists that have children items +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "siem_list" } + } + }' \ + | jq . + +# Delete all the list children items as well +curl -s -k \ + -H "Content-Type: application/json" \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${ELASTICSEARCH_URL}/${KIBANA_INDEX}*/_delete_by_query \ + --data '{ + "query": { + "exists": { "field": "siem_list_item" } + } + }' \ + | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list.sh b/x-pack/plugins/lists/server/scripts/delete_list.sh new file mode 100755 index 00000000000000..9934ce61c71073 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_list.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_list_by_list_id.sh ${list_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list_index.sh b/x-pack/plugins/lists/server/scripts/delete_list_index.sh new file mode 100755 index 00000000000000..85f06ffbd66705 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_list_index.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_signal_index.sh +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/lists/index | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/delete_list_item_by_id.sh new file mode 100755 index 00000000000000..ab14d8c8a80edf --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_list_item_by_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_list_item_by_id.sh?id={id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}${SPACE_URL}/api/lists/items?id=$1 | jq . diff --git a/x-pack/plugins/lists/server/scripts/delete_list_item_by_value.sh b/x-pack/plugins/lists/server/scripts/delete_list_item_by_value.sh new file mode 100755 index 00000000000000..6d3213ccb87934 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/delete_list_item_by_value.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_list_item_by_value.sh?list_id=${some_id}&value=${some_ip} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE "${KIBANA_URL}${SPACE_URL}/api/lists/items?list_id=$1&value=$2" | jq . diff --git a/x-pack/plugins/lists/server/scripts/export_list_items.sh b/x-pack/plugins/lists/server/scripts/export_list_items.sh new file mode 100755 index 00000000000000..ba355854c77cc9 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/export_list_items.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a defaults if no argument is specified +LIST_ID=${1:-ips.txt} + +# Example to export +# ./export_list_items.sh > /tmp/ips.txt + +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST "${KIBANA_URL}${SPACE_URL}/api/lists/items/_export?list_id=${LIST_ID}" diff --git a/x-pack/plugins/lists/server/scripts/export_list_items_to_file.sh b/x-pack/plugins/lists/server/scripts/export_list_items_to_file.sh new file mode 100755 index 00000000000000..5efad01e9a68ec --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/export_list_items_to_file.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a defaults if no argument is specified +FOLDER=${1:-/tmp} + +# Example to export +# ./export_list_items_to_file.sh + +# Change current working directory as exports cause Kibana to restart +pushd ${FOLDER} > /dev/null + +curl -s -k -OJ \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST "${KIBANA_URL}${SPACE_URL}/api/lists/items/_export?list_id=list-ip" + +popd > /dev/null diff --git a/x-pack/plugins/lists/server/scripts/get_list.sh b/x-pack/plugins/lists/server/scripts/get_list.sh new file mode 100755 index 00000000000000..7f0e4e30622668 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_list.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_list.sh {list_id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/lists?id="$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_list_item_by_id.sh b/x-pack/plugins/lists/server/scripts/get_list_item_by_id.sh new file mode 100755 index 00000000000000..31d26e195815fd --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_list_item_by_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_list_item_by_id ${id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items?id=$1" | jq . diff --git a/x-pack/plugins/lists/server/scripts/get_list_item_by_value.sh b/x-pack/plugins/lists/server/scripts/get_list_item_by_value.sh new file mode 100755 index 00000000000000..24ca27b0c949da --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/get_list_item_by_value.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_list_item_by_value.sh ${list_id} ${value} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/lists/items?list_id=$1&value=$2" | jq . diff --git a/x-pack/plugins/lists/server/scripts/hard_reset.sh b/x-pack/plugins/lists/server/scripts/hard_reset.sh new file mode 100755 index 00000000000000..861928866369bc --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/hard_reset.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# re-create the list and list item indexes +./delete_list_index.sh +./post_list_index.sh diff --git a/x-pack/plugins/lists/server/scripts/import_list_items.sh b/x-pack/plugins/lists/server/scripts/import_list_items.sh new file mode 100755 index 00000000000000..a39409cd082677 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/import_list_items.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a defaults if no argument is specified +LIST_ID=${1:-list-ip} +FILE=${2:-./lists/files/ips.txt} + +# ./import_list_items.sh list-ip ./lists/files/ips.txt +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST "${KIBANA_URL}${SPACE_URL}/api/lists/items/_import?list_id=${LIST_ID}" \ + --form file=@${FILE} \ + | jq .; diff --git a/x-pack/plugins/lists/server/scripts/import_list_items_by_filename.sh b/x-pack/plugins/lists/server/scripts/import_list_items_by_filename.sh new file mode 100755 index 00000000000000..4ec55cb4c5f7b2 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/import_list_items_by_filename.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a defaults if no argument is specified +TYPE=${1:-ip} +FILE=${2:-./lists/files/ips.txt} + +# Example to import ips from ./lists/files/ips.txt +# ./import_list_items_by_filename.sh ip ./lists/files/ips.txt + +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST "${KIBANA_URL}${SPACE_URL}/api/lists/items/_import?type=${TYPE}" \ + --form file=@${FILE} \ + | jq .; diff --git a/x-pack/plugins/lists/server/scripts/lists/files/hosts.txt b/x-pack/plugins/lists/server/scripts/lists/files/hosts.txt new file mode 100644 index 00000000000000..aee32e3a4bd925 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/files/hosts.txt @@ -0,0 +1,2 @@ +kibana +rock01 diff --git a/x-pack/plugins/lists/server/scripts/lists/files/ips.txt b/x-pack/plugins/lists/server/scripts/lists/files/ips.txt new file mode 100644 index 00000000000000..cf8ebcacae5a1d --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/files/ips.txt @@ -0,0 +1,9 @@ +127.0.0.1 +127.0.0.2 +127.0.0.3 +127.0.0.4 +127.0.0.5 +127.0.0.6 +127.0.0.7 +127.0.0.8 +127.0.0.9 diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_everything.json b/x-pack/plugins/lists/server/scripts/lists/new/list_everything.json new file mode 100644 index 00000000000000..196b3b149ab829 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_everything.json @@ -0,0 +1,13 @@ +{ + "id": "list-ip-everything", + "name": "Simple list with an ip", + "description": "This list describes bad internet ip", + "type": "ip", + "meta": { + "level_1_meta": { + "level_2_meta": { + "level_3_key": "some_value_ui" + } + } + } +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_ip.json b/x-pack/plugins/lists/server/scripts/lists/new/list_ip.json new file mode 100644 index 00000000000000..3e12ef1754f07d --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_ip.json @@ -0,0 +1,6 @@ +{ + "id": "list-ip", + "name": "Simple list with an ip", + "description": "This list describes bad internet ip", + "type": "ip" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item.json b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item.json new file mode 100644 index 00000000000000..1516fa5057e50e --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item.json @@ -0,0 +1,5 @@ +{ + "id": "hand_inserted_item_id", + "list_id": "list-ip", + "value": "127.0.0.1" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item_everything.json b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item_everything.json new file mode 100644 index 00000000000000..9730c1b7523f19 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_item_everything.json @@ -0,0 +1,12 @@ +{ + "id": "hand_inserted_item_id_everything", + "list_id": "list-ip", + "value": "127.0.0.2", + "meta": { + "level_1_meta": { + "level_2_meta": { + "level_3_key": "some_value_ui" + } + } + } +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_ip_no_id.json b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_no_id.json new file mode 100644 index 00000000000000..4a95a62b67c3e8 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_ip_no_id.json @@ -0,0 +1,5 @@ +{ + "name": "Simple list with an ip", + "description": "This list describes bad internet ip", + "type": "ip" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_keyword.json b/x-pack/plugins/lists/server/scripts/lists/new/list_keyword.json new file mode 100644 index 00000000000000..e8f5fa7e38a06d --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_keyword.json @@ -0,0 +1,6 @@ +{ + "id": "list-keyword", + "name": "Simple list with a keyword", + "description": "This list describes bad host names", + "type": "keyword" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/new/list_keyword_item.json b/x-pack/plugins/lists/server/scripts/lists/new/list_keyword_item.json new file mode 100644 index 00000000000000..b736e7b96ad98e --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/new/list_keyword_item.json @@ -0,0 +1,4 @@ +{ + "list_id": "list-keyword", + "value": "kibana" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/patches/list_ip_item.json b/x-pack/plugins/lists/server/scripts/lists/patches/list_ip_item.json new file mode 100644 index 00000000000000..00c3496e71b358 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/patches/list_ip_item.json @@ -0,0 +1,4 @@ +{ + "id": "hand_inserted_item_id", + "value": "255.255.255.255" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json b/x-pack/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json new file mode 100644 index 00000000000000..1a57ab8b6a3b91 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/patches/simplest_updated_name.json @@ -0,0 +1,4 @@ +{ + "id": "list-ip", + "name": "Changed the name here to something else" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/updates/list_ip_item.json b/x-pack/plugins/lists/server/scripts/lists/updates/list_ip_item.json new file mode 100644 index 00000000000000..00c3496e71b358 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/updates/list_ip_item.json @@ -0,0 +1,4 @@ +{ + "id": "hand_inserted_item_id", + "value": "255.255.255.255" +} diff --git a/x-pack/plugins/lists/server/scripts/lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/lists/updates/simple_update.json new file mode 100644 index 00000000000000..936a070ede52cc --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists/updates/simple_update.json @@ -0,0 +1,5 @@ +{ + "id": "list-ip", + "name": "Changed the name here to something else", + "description": "Some other description here for you" +} diff --git a/x-pack/plugins/lists/server/scripts/lists_index_exists.sh b/x-pack/plugins/lists/server/scripts/lists_index_exists.sh new file mode 100755 index 00000000000000..7dfbd5b1bada56 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/lists_index_exists.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./lists_index_exists.sh +curl -s -k -f \ + -H 'Content-Type: application/json' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + ${KIBANA_URL}${SPACE_URL}/api/lists/index | jq . diff --git a/x-pack/plugins/lists/server/scripts/patch_list.sh b/x-pack/plugins/lists/server/scripts/patch_list.sh new file mode 100755 index 00000000000000..3a517a52dbd21a --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/patch_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/patches/simplest_updated_name.json}) + +# Example: ./patch_list.sh +# Example: ./patch_list.sh ./lists/patches/simplest_updated_name.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PATCH ${KIBANA_URL}${SPACE_URL}/api/lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/patch_list_item.sh b/x-pack/plugins/lists/server/scripts/patch_list_item.sh new file mode 100755 index 00000000000000..406b03dc6499ca --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/patch_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/patches/list_ip_item.json}) + +# Example: ./patch_list.sh +# Example: ./patch_list.sh ./lists/patches/list_ip_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PATCH ${KIBANA_URL}${SPACE_URL}/api/lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/post_list.sh b/x-pack/plugins/lists/server/scripts/post_list.sh new file mode 100755 index 00000000000000..6aaffee0bc4b2e --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/new/list_ip.json}) + +# Example: ./post_list.sh +# Example: ./post_list.sh ./lists/new/list_ip.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/post_list_index.sh b/x-pack/plugins/lists/server/scripts/post_list_index.sh new file mode 100755 index 00000000000000..b7c372d3947e37 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_list_index.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./post_signal_index.sh +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/lists/index | jq . diff --git a/x-pack/plugins/lists/server/scripts/post_list_item.sh b/x-pack/plugins/lists/server/scripts/post_list_item.sh new file mode 100755 index 00000000000000..b55a60420674f3 --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/post_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/new/list_ip_item.json}) + +# Example: ./post_list.sh +# Example: ./post_list.sh ./lists/new/list_ip_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_list.sh b/x-pack/plugins/lists/server/scripts/update_list.sh new file mode 100755 index 00000000000000..4d93544d568a8d --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_list.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/updates/simple_update.json}) + +# Example: ./update_list.sh +# Example: ./update_list.sh ./lists/updates/simple_update.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}${SPACE_URL}/api/lists \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/scripts/update_list_item.sh b/x-pack/plugins/lists/server/scripts/update_list_item.sh new file mode 100755 index 00000000000000..e3153bfd25b19a --- /dev/null +++ b/x-pack/plugins/lists/server/scripts/update_list_item.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +LISTS=(${@:-./lists/updates/list_ip_item.json}) + +# Example: ./patch_list.sh +# Example: ./patch_list.sh ./lists/updates/list_ip_item.json +for LIST in "${LISTS[@]}" +do { + [ -e "$LIST" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PATCH ${KIBANA_URL}${SPACE_URL}/api/lists/items \ + -d @${LIST} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/lists/server/services/items/buffer_lines.test.ts b/x-pack/plugins/lists/server/services/items/buffer_lines.test.ts new file mode 100644 index 00000000000000..48deb3ee86820d --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/buffer_lines.test.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TestReadable } from '../mocks'; + +import { BufferLines } from './buffer_lines'; + +describe('buffer_lines', () => { + test('it can read a single line', done => { + const input = new TestReadable(); + input.push('line one\n'); + input.push(null); + const bufferedLine = new BufferLines({ input }); + let linesToTest: string[] = []; + bufferedLine.on('lines', (lines: string[]) => { + linesToTest = [...linesToTest, ...lines]; + }); + bufferedLine.on('close', () => { + expect(linesToTest).toEqual(['line one']); + done(); + }); + }); + + test('it can read two lines', done => { + const input = new TestReadable(); + input.push('line one\n'); + input.push('line two\n'); + input.push(null); + const bufferedLine = new BufferLines({ input }); + let linesToTest: string[] = []; + bufferedLine.on('lines', (lines: string[]) => { + linesToTest = [...linesToTest, ...lines]; + }); + bufferedLine.on('close', () => { + expect(linesToTest).toEqual(['line one', 'line two']); + done(); + }); + }); + + test('two identical lines are collapsed into just one line without duplicates', done => { + const input = new TestReadable(); + input.push('line one\n'); + input.push('line one\n'); + input.push(null); + const bufferedLine = new BufferLines({ input }); + let linesToTest: string[] = []; + bufferedLine.on('lines', (lines: string[]) => { + linesToTest = [...linesToTest, ...lines]; + }); + bufferedLine.on('close', () => { + expect(linesToTest).toEqual(['line one']); + done(); + }); + }); + + test('it can close out without writing any lines', done => { + const input = new TestReadable(); + input.push(null); + const bufferedLine = new BufferLines({ input }); + let linesToTest: string[] = []; + bufferedLine.on('lines', (lines: string[]) => { + linesToTest = [...linesToTest, ...lines]; + }); + bufferedLine.on('close', () => { + expect(linesToTest).toEqual([]); + done(); + }); + }); + + test('it can read 200 lines', done => { + const input = new TestReadable(); + const bufferedLine = new BufferLines({ input }); + let linesToTest: string[] = []; + const size200: string[] = new Array(200).fill(null).map((_, index) => `${index}\n`); + size200.forEach(element => input.push(element)); + input.push(null); + bufferedLine.on('lines', (lines: string[]) => { + linesToTest = [...linesToTest, ...lines]; + }); + bufferedLine.on('close', () => { + expect(linesToTest.length).toEqual(200); + done(); + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/buffer_lines.ts b/x-pack/plugins/lists/server/services/items/buffer_lines.ts new file mode 100644 index 00000000000000..fd8fe7077fd58d --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/buffer_lines.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import readLine from 'readline'; +import { Readable } from 'stream'; + +const BUFFER_SIZE = 100; + +export class BufferLines extends Readable { + private set = new Set(); + constructor({ input }: { input: NodeJS.ReadableStream }) { + super({ encoding: 'utf-8' }); + const readline = readLine.createInterface({ + input, + }); + + readline.on('line', line => { + this.push(line); + }); + + readline.on('close', () => { + this.push(null); + }); + } + + public _read(): void { + // No operation but this is required to be implemented + } + + public push(line: string | null): boolean { + if (line == null) { + this.emit('lines', Array.from(this.set)); + this.set.clear(); + this.emit('close'); + return true; + } else { + this.set.add(line); + if (this.set.size > BUFFER_SIZE) { + this.emit('lines', Array.from(this.set)); + this.set.clear(); + return true; + } else { + return true; + } + } + } +} diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.test.ts b/x-pack/plugins/lists/server/services/items/create_list_item.test.ts new file mode 100644 index 00000000000000..abbb2701499557 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/create_list_item.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + LIST_ITEM_ID, + LIST_ITEM_INDEX, + getCreateListItemOptionsMock, + getIndexESListItemMock, + getListItemResponseMock, +} from '../mocks'; + +import { createListItem } from './create_list_item'; + +describe('crete_list_item', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list item as expected with the id changed out for the elastic id', async () => { + const options = getCreateListItemOptionsMock(); + const listItem = await createListItem(options); + const expected = getListItemResponseMock(); + expected.id = 'elastic-id-123'; + expect(listItem).toEqual(expected); + }); + + test('It calls "callCluster" with body, index, and listIndex', async () => { + const options = getCreateListItemOptionsMock(); + await createListItem(options); + const body = getIndexESListItemMock(); + const expected = { + body, + id: LIST_ITEM_ID, + index: LIST_ITEM_INDEX, + }; + expect(options.callCluster).toBeCalledWith('index', expected); + }); + + test('It returns an auto-generated id if id is sent in undefined', async () => { + const options = getCreateListItemOptionsMock(); + options.id = undefined; + const list = await createListItem(options); + const expected = getListItemResponseMock(); + expected.id = 'elastic-id-123'; + expect(list).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/create_list_item.ts b/x-pack/plugins/lists/server/services/items/create_list_item.ts new file mode 100644 index 00000000000000..83a118b7951923 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/create_list_item.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; +import { CreateDocumentResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { + IdOrUndefined, + IndexEsListItemSchema, + ListItemSchema, + MetaOrUndefined, + Type, +} from '../../../common/schemas'; +import { transformListItemToElasticQuery } from '../utils'; + +export interface CreateListItemOptions { + id: IdOrUndefined; + listId: string; + type: Type; + value: string; + callCluster: APICaller; + listItemIndex: string; + user: string; + meta: MetaOrUndefined; + dateNow?: string; + tieBreaker?: string; +} + +export const createListItem = async ({ + id, + listId, + type, + value, + callCluster, + listItemIndex, + user, + meta, + dateNow, + tieBreaker, +}: CreateListItemOptions): Promise => { + const createdAt = dateNow ?? new Date().toISOString(); + const tieBreakerId = tieBreaker ?? uuid.v4(); + const baseBody = { + created_at: createdAt, + created_by: user, + list_id: listId, + meta, + tie_breaker_id: tieBreakerId, + updated_at: createdAt, + updated_by: user, + }; + const body: IndexEsListItemSchema = { + ...baseBody, + ...transformListItemToElasticQuery({ type, value }), + }; + + const response: CreateDocumentResponse = await callCluster('index', { + body, + id, + index: listItemIndex, + }); + + return { + id: response._id, + type, + value, + ...baseBody, + }; +}; diff --git a/x-pack/plugins/lists/server/services/items/create_list_items_bulk.test.ts b/x-pack/plugins/lists/server/services/items/create_list_items_bulk.test.ts new file mode 100644 index 00000000000000..94cc57b53b4e24 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/create_list_items_bulk.test.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 { IndexEsListItemSchema } from '../../../common/schemas'; +import { + LIST_ITEM_INDEX, + TIE_BREAKERS, + VALUE_2, + getCreateListItemBulkOptionsMock, + getIndexESListItemMock, +} from '../mocks'; + +import { createListItemsBulk } from './create_list_items_bulk'; + +describe('crete_list_item_bulk', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('It calls "callCluster" with body, index, and the bulk items', async () => { + const options = getCreateListItemBulkOptionsMock(); + await createListItemsBulk(options); + const firstRecord: IndexEsListItemSchema = getIndexESListItemMock(); + const secondRecord: IndexEsListItemSchema = getIndexESListItemMock(VALUE_2); + [firstRecord.tie_breaker_id, secondRecord.tie_breaker_id] = TIE_BREAKERS; + expect(options.callCluster).toBeCalledWith('bulk', { + body: [ + { create: { _index: LIST_ITEM_INDEX } }, + firstRecord, + { create: { _index: LIST_ITEM_INDEX } }, + secondRecord, + ], + index: LIST_ITEM_INDEX, + }); + }); + + test('It should not call the dataClient when the values are empty', async () => { + const options = getCreateListItemBulkOptionsMock(); + options.value = []; + expect(options.callCluster).not.toBeCalled(); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/create_list_items_bulk.ts b/x-pack/plugins/lists/server/services/items/create_list_items_bulk.ts new file mode 100644 index 00000000000000..eac294c5f244a2 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/create_list_items_bulk.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; +import { APICaller } from 'kibana/server'; + +import { transformListItemToElasticQuery } from '../utils'; +import { + CreateEsBulkTypeSchema, + IndexEsListItemSchema, + MetaOrUndefined, + Type, +} from '../../../common/schemas'; + +export interface CreateListItemsBulkOptions { + listId: string; + type: Type; + value: string[]; + callCluster: APICaller; + listItemIndex: string; + user: string; + meta: MetaOrUndefined; + dateNow?: string; + tieBreaker?: string[]; +} + +export const createListItemsBulk = async ({ + listId, + type, + value, + callCluster, + listItemIndex, + user, + meta, + dateNow, + tieBreaker, +}: CreateListItemsBulkOptions): Promise => { + // It causes errors if you try to add items to bulk that do not exist within ES + if (!value.length) { + return; + } + const body = value.reduce>( + (accum, singleValue, index) => { + const createdAt = dateNow ?? new Date().toISOString(); + const tieBreakerId = + tieBreaker != null && tieBreaker[index] != null ? tieBreaker[index] : uuid.v4(); + const elasticBody: IndexEsListItemSchema = { + created_at: createdAt, + created_by: user, + list_id: listId, + meta, + tie_breaker_id: tieBreakerId, + updated_at: createdAt, + updated_by: user, + ...transformListItemToElasticQuery({ type, value: singleValue }), + }; + const createBody: CreateEsBulkTypeSchema = { create: { _index: listItemIndex } }; + return [...accum, createBody, elasticBody]; + }, + [] + ); + + await callCluster('bulk', { + body, + index: listItemIndex, + }); +}; diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item.test.ts b/x-pack/plugins/lists/server/services/items/delete_list_item.test.ts new file mode 100644 index 00000000000000..00fcefb2c379fc --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/delete_list_item.test.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 { + LIST_ITEM_ID, + LIST_ITEM_INDEX, + getDeleteListItemOptionsMock, + getListItemResponseMock, +} from '../mocks'; + +import { getListItem } from './get_list_item'; +import { deleteListItem } from './delete_list_item'; + +jest.mock('./get_list_item', () => ({ + getListItem: jest.fn(), +})); + +describe('delete_list_item', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Delete returns a null if "getListItem" returns a null', async () => { + ((getListItem as unknown) as jest.Mock).mockResolvedValueOnce(null); + const options = getDeleteListItemOptionsMock(); + const deletedListItem = await deleteListItem(options); + expect(deletedListItem).toEqual(null); + }); + + test('Delete returns the same list item if a list item is returned from "getListItem"', async () => { + const listItem = getListItemResponseMock(); + ((getListItem as unknown) as jest.Mock).mockResolvedValueOnce(listItem); + const options = getDeleteListItemOptionsMock(); + const deletedListItem = await deleteListItem(options); + expect(deletedListItem).toEqual(listItem); + }); + + test('Delete calls "delete" if a list item is returned from "getListItem"', async () => { + const listItem = getListItemResponseMock(); + ((getListItem as unknown) as jest.Mock).mockResolvedValueOnce(listItem); + const options = getDeleteListItemOptionsMock(); + await deleteListItem(options); + const deleteQuery = { + id: LIST_ITEM_ID, + index: LIST_ITEM_INDEX, + }; + expect(options.callCluster).toBeCalledWith('delete', deleteQuery); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item.ts b/x-pack/plugins/lists/server/services/items/delete_list_item.ts new file mode 100644 index 00000000000000..9992f43387c89f --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/delete_list_item.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'kibana/server'; + +import { Id, ListItemSchema } from '../../../common/schemas'; + +import { getListItem } from '.'; + +export interface DeleteListItemOptions { + id: Id; + callCluster: APICaller; + listItemIndex: string; +} + +export const deleteListItem = async ({ + id, + callCluster, + listItemIndex, +}: DeleteListItemOptions): Promise => { + const listItem = await getListItem({ callCluster, id, listItemIndex }); + if (listItem == null) { + return null; + } else { + await callCluster('delete', { + id, + index: listItemIndex, + }); + } + return listItem; +}; diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.test.ts b/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.test.ts new file mode 100644 index 00000000000000..c7c80638e4c374 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getDeleteListItemByValueOptionsMock, getListItemResponseMock } from '../mocks'; + +import { getListItemByValues } from './get_list_item_by_values'; +import { deleteListItemByValue } from './delete_list_item_by_value'; + +jest.mock('./get_list_item_by_values', () => ({ + getListItemByValues: jest.fn(), +})); + +describe('delete_list_item_by_value', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Delete returns a an empty array if the list items are also empty', async () => { + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([]); + const options = getDeleteListItemByValueOptionsMock(); + const deletedListItem = await deleteListItemByValue(options); + expect(deletedListItem).toEqual([]); + }); + + test('Delete returns the list item if a list item is returned from "getListByValues"', async () => { + const listItems = [getListItemResponseMock()]; + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce(listItems); + const options = getDeleteListItemByValueOptionsMock(); + const deletedListItem = await deleteListItemByValue(options); + expect(deletedListItem).toEqual(listItems); + }); + + test('Delete calls "deleteByQuery" if a list item is returned from "getListByValues"', async () => { + const listItems = [getListItemResponseMock()]; + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce(listItems); + const options = getDeleteListItemByValueOptionsMock(); + await deleteListItemByValue(options); + const deleteByQuery = { + body: { + query: { + bool: { + filter: [{ term: { list_id: 'some-list-id' } }, { terms: { ip: ['127.0.0.1'] } }], + }, + }, + }, + index: '.items', + }; + expect(options.callCluster).toBeCalledWith('deleteByQuery', deleteByQuery); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.ts b/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.ts new file mode 100644 index 00000000000000..ec29f14a0ff647 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/delete_list_item_by_value.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 { APICaller } from 'kibana/server'; + +import { ListItemArraySchema, Type } from '../../../common/schemas'; +import { getQueryFilterFromTypeValue } from '../utils'; + +import { getListItemByValues } from './get_list_item_by_values'; + +export interface DeleteListItemByValueOptions { + listId: string; + type: Type; + value: string; + callCluster: APICaller; + listItemIndex: string; +} + +export const deleteListItemByValue = async ({ + listId, + value, + type, + callCluster, + listItemIndex, +}: DeleteListItemByValueOptions): Promise => { + const listItems = await getListItemByValues({ + callCluster, + listId, + listItemIndex, + type, + value: [value], + }); + const values = listItems.map(listItem => listItem.value); + const filter = getQueryFilterFromTypeValue({ + listId, + type, + value: values, + }); + await callCluster('deleteByQuery', { + body: { + query: { + bool: { + filter, + }, + }, + }, + index: listItemIndex, + }); + return listItems; +}; diff --git a/x-pack/plugins/lists/server/services/items/get_list_item.test.ts b/x-pack/plugins/lists/server/services/items/get_list_item.test.ts new file mode 100644 index 00000000000000..31a421c2e31bfe --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item.test.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 { + LIST_ID, + LIST_INDEX, + getCallClusterMock, + getListItemResponseMock, + getSearchListItemMock, +} from '../mocks'; + +import { getListItem } from './get_list_item'; + +describe('get_list_item', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list item as expected if the list item is found', async () => { + const data = getSearchListItemMock(); + const callCluster = getCallClusterMock(data); + const list = await getListItem({ callCluster, id: LIST_ID, listItemIndex: LIST_INDEX }); + const expected = getListItemResponseMock(); + expect(list).toEqual(expected); + }); + + test('it returns null if the search is empty', async () => { + const data = getSearchListItemMock(); + data.hits.hits = []; + const callCluster = getCallClusterMock(data); + const list = await getListItem({ callCluster, id: LIST_ID, listItemIndex: LIST_INDEX }); + expect(list).toEqual(null); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/get_list_item.ts b/x-pack/plugins/lists/server/services/items/get_list_item.ts new file mode 100644 index 00000000000000..83b30d336ccd47 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item.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 { SearchResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { Id, ListItemSchema, SearchEsListItemSchema } from '../../../common/schemas'; +import { deriveTypeFromItem, transformElasticToListItem } from '../utils'; + +interface GetListItemOptions { + id: Id; + callCluster: APICaller; + listItemIndex: string; +} + +export const getListItem = async ({ + id, + callCluster, + listItemIndex, +}: GetListItemOptions): Promise => { + const listItemES: SearchResponse = await callCluster('search', { + body: { + query: { + term: { + _id: id, + }, + }, + }, + ignoreUnavailable: true, + index: listItemIndex, + }); + + if (listItemES.hits.hits.length) { + const type = deriveTypeFromItem({ item: listItemES.hits.hits[0]._source }); + const listItems = transformElasticToListItem({ response: listItemES, type }); + return listItems[0]; + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_value.test.ts b/x-pack/plugins/lists/server/services/items/get_list_item_by_value.test.ts new file mode 100644 index 00000000000000..d30b3c795550f1 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_by_value.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getListItemByValueOptionsMocks, getListItemResponseMock } from '../mocks'; + +import { getListItemByValues } from './get_list_item_by_values'; +import { getListItemByValue } from './get_list_item_by_value'; + +jest.mock('./get_list_item_by_values', () => ({ + getListItemByValues: jest.fn(), +})); + +describe('get_list_by_value', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Calls get_list_item_by_values with its input', async () => { + const listItemMock = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([listItemMock]); + const options = getListItemByValueOptionsMocks(); + const listItem = await getListItemByValue(options); + const expected = getListItemResponseMock(); + expect(listItem).toEqual([expected]); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_value.ts b/x-pack/plugins/lists/server/services/items/get_list_item_by_value.ts new file mode 100644 index 00000000000000..49bcf12043d7c9 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_by_value.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'kibana/server'; + +import { ListItemArraySchema, Type } from '../../../common/schemas'; + +import { getListItemByValues } from '.'; + +export interface GetListItemByValueOptions { + listId: string; + callCluster: APICaller; + listItemIndex: string; + type: Type; + value: string; +} + +export const getListItemByValue = async ({ + listId, + callCluster, + listItemIndex, + type, + value, +}: GetListItemByValueOptions): Promise => + getListItemByValues({ + callCluster, + listId, + listItemIndex, + type, + value: [value], + }); diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_values.test.ts b/x-pack/plugins/lists/server/services/items/get_list_item_by_values.test.ts new file mode 100644 index 00000000000000..7f5fff4dc3147a --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_by_values.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + LIST_ID, + LIST_ITEM_INDEX, + TYPE, + VALUE, + VALUE_2, + getCallClusterMock, + getSearchListItemMock, +} from '../mocks'; + +import { getListItemByValues } from './get_list_item_by_values'; + +describe('get_list_item_by_values', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Returns a an empty array if the ES query is also empty', async () => { + const data = getSearchListItemMock(); + data.hits.hits = []; + const callCluster = getCallClusterMock(data); + const listItem = await getListItemByValues({ + callCluster, + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + type: TYPE, + value: [VALUE, VALUE_2], + }); + + expect(listItem).toEqual([]); + }); + + test('Returns transformed list item if the data exists within ES', async () => { + const data = getSearchListItemMock(); + const callCluster = getCallClusterMock(data); + const listItem = await getListItemByValues({ + callCluster, + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + type: TYPE, + value: [VALUE, VALUE_2], + }); + + expect(listItem).toEqual([ + { + created_at: '2020-04-20T15:25:31.830Z', + created_by: 'some user', + id: 'some-list-item-id', + list_id: 'some-list-id', + meta: {}, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'ip', + updated_at: '2020-04-20T15:25:31.830Z', + updated_by: 'some user', + value: '127.0.0.1', + }, + ]); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_by_values.ts b/x-pack/plugins/lists/server/services/items/get_list_item_by_values.ts new file mode 100644 index 00000000000000..29b9b017540270 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_by_values.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 { SearchResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { ListItemArraySchema, SearchEsListItemSchema, Type } from '../../../common/schemas'; +import { getQueryFilterFromTypeValue, transformElasticToListItem } from '../utils'; + +export interface GetListItemByValuesOptions { + listId: string; + callCluster: APICaller; + listItemIndex: string; + type: Type; + value: string[]; +} + +export const getListItemByValues = async ({ + listId, + callCluster, + listItemIndex, + type, + value, +}: GetListItemByValuesOptions): Promise => { + const response: SearchResponse = await callCluster('search', { + body: { + query: { + bool: { + filter: getQueryFilterFromTypeValue({ listId, type, value }), + }, + }, + }, + ignoreUnavailable: true, + index: listItemIndex, + size: value.length, // This has a limit on the number which is 10k + }); + return transformElasticToListItem({ response, type }); +}; diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_index.test.ts b/x-pack/plugins/lists/server/services/items/get_list_item_index.test.ts new file mode 100644 index 00000000000000..ffe2eff9f3ca70 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_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 { getListItemIndex } from './get_list_item_index'; + +describe('get_list_item_index', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Returns the list item index when there is a space', async () => { + const listIndex = getListItemIndex({ + listsItemsIndexName: 'lists-items-index', + spaceId: 'test-space', + }); + expect(listIndex).toEqual('lists-items-index-test-space'); + }); +}); diff --git a/x-pack/legacy/plugins/index_management/index.ts b/x-pack/plugins/lists/server/services/items/get_list_item_index.ts similarity index 50% rename from x-pack/legacy/plugins/index_management/index.ts rename to x-pack/plugins/lists/server/services/items/get_list_item_index.ts index afca15203b9702..4cd93df6d9bf42 100644 --- a/x-pack/legacy/plugins/index_management/index.ts +++ b/x-pack/plugins/lists/server/services/items/get_list_item_index.ts @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -// TODO: Remove this once CCR is migrated to the plugins directory. -export function indexManagement(kibana: any) { - return new kibana.Plugin({ - id: 'index_management', - configPrefix: 'xpack.index_management', - }); +export interface GetListItemIndexOptions { + spaceId: string; + listsItemsIndexName: string; } + +export const getListItemIndex = ({ + spaceId, + listsItemsIndexName, +}: GetListItemIndexOptions): string => `${listsItemsIndexName}-${spaceId}`; diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_template.test.ts b/x-pack/plugins/lists/server/services/items/get_list_item_template.test.ts new file mode 100644 index 00000000000000..9c85fa6ff0256e --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_template.test.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 { getListItemTemplate } from './get_list_item_template'; + +jest.mock('./list_item_mappings.json', () => ({ + listMappings: {}, +})); + +describe('get_list_item_template', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list template with the string filled in', async () => { + const template = getListItemTemplate('some_index'); + expect(template).toEqual({ + index_patterns: ['some_index-*'], + mappings: { listMappings: {} }, + settings: { index: { lifecycle: { name: 'some_index', rollover_alias: 'some_index' } } }, + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/get_list_item_template.ts b/x-pack/plugins/lists/server/services/items/get_list_item_template.ts new file mode 100644 index 00000000000000..95f4a09b406488 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/get_list_item_template.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import listsItemsMappings from './list_item_mappings.json'; + +export const getListItemTemplate = (index: string): Record => { + const template = { + index_patterns: [`${index}-*`], + mappings: listsItemsMappings, + settings: { + index: { + lifecycle: { + name: index, + rollover_alias: index, + }, + }, + }, + }; + return template; +}; diff --git a/x-pack/plugins/lists/server/services/items/index.ts b/x-pack/plugins/lists/server/services/items/index.ts new file mode 100644 index 00000000000000..ee1d83fabca31a --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/index.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. + */ + +export * from './buffer_lines'; +export * from './create_list_item'; +export * from './create_list_items_bulk'; +export * from './delete_list_item_by_value'; +export * from './get_list_item_by_value'; +export * from './get_list_item'; +export * from './get_list_item_by_values'; +export * from './update_list_item'; +export * from './write_lines_to_bulk_list_items'; +export * from './write_list_items_to_stream'; +export * from './get_list_item_template'; +export * from './delete_list_item'; +export * from './get_list_item_index'; diff --git a/x-pack/plugins/lists/server/services/items/list_item_mappings.json b/x-pack/plugins/lists/server/services/items/list_item_mappings.json new file mode 100644 index 00000000000000..ca69c26df52b5a --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/list_item_mappings.json @@ -0,0 +1,33 @@ +{ + "dynamic": "strict", + "properties": { + "tie_breaker_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "keyword": { + "type": "keyword" + }, + "meta": { + "enabled": "false", + "type": "object" + }, + "created_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } +} diff --git a/x-pack/plugins/lists/server/services/items/list_item_policy.json b/x-pack/plugins/lists/server/services/items/list_item_policy.json new file mode 100644 index 00000000000000..a4c84f73e78960 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/list_item_policy.json @@ -0,0 +1,14 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "0ms", + "actions": { + "rollover": { + "max_size": "50gb" + } + } + } + } + } +} diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.test.ts b/x-pack/plugins/lists/server/services/items/update_list_item.test.ts new file mode 100644 index 00000000000000..4ef4110bc0742a --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/update_list_item.test.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 { getListItemResponseMock, getUpdateListItemOptionsMock } from '../mocks'; + +import { updateListItem } from './update_list_item'; +import { getListItem } from './get_list_item'; + +jest.mock('./get_list_item', () => ({ + getListItem: jest.fn(), +})); + +describe('update_list_item', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list item as expected with the id changed out for the elastic id when there is a list item to update', async () => { + const list = getListItemResponseMock(); + ((getListItem as unknown) as jest.Mock).mockResolvedValueOnce(list); + const options = getUpdateListItemOptionsMock(); + const updatedList = await updateListItem(options); + const expected = getListItemResponseMock(); + expected.id = 'elastic-id-123'; + expect(updatedList).toEqual(expected); + }); + + test('it returns null when there is not a list item to update', async () => { + ((getListItem as unknown) as jest.Mock).mockResolvedValueOnce(null); + const options = getUpdateListItemOptionsMock(); + const updatedList = await updateListItem(options); + expect(updatedList).toEqual(null); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/update_list_item.ts b/x-pack/plugins/lists/server/services/items/update_list_item.ts new file mode 100644 index 00000000000000..6a71b2a0caf415 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/update_list_item.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CreateDocumentResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { + Id, + ListItemSchema, + MetaOrUndefined, + UpdateEsListItemSchema, +} from '../../../common/schemas'; +import { transformListItemToElasticQuery } from '../utils'; + +import { getListItem } from './get_list_item'; + +export interface UpdateListItemOptions { + id: Id; + value: string | null | undefined; + callCluster: APICaller; + listItemIndex: string; + user: string; + meta: MetaOrUndefined; + dateNow?: string; +} + +export const updateListItem = async ({ + id, + value, + callCluster, + listItemIndex, + user, + meta, + dateNow, +}: UpdateListItemOptions): Promise => { + const updatedAt = dateNow ?? new Date().toISOString(); + const listItem = await getListItem({ callCluster, id, listItemIndex }); + if (listItem == null) { + return null; + } else { + const doc: UpdateEsListItemSchema = { + meta, + updated_at: updatedAt, + updated_by: user, + ...transformListItemToElasticQuery({ type: listItem.type, value: value ?? listItem.value }), + }; + + const response: CreateDocumentResponse = await callCluster('update', { + body: { + doc, + }, + id: listItem.id, + index: listItemIndex, + }); + return { + created_at: listItem.created_at, + created_by: listItem.created_by, + id: response._id, + list_id: listItem.list_id, + meta: meta ?? listItem.meta, + tie_breaker_id: listItem.tie_breaker_id, + type: listItem.type, + updated_at: updatedAt, + updated_by: listItem.updated_by, + value: value ?? listItem.value, + }; + } +}; diff --git a/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts b/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts new file mode 100644 index 00000000000000..f064543f1ec937 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.test.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + getImportListItemsToStreamOptionsMock, + getListItemResponseMock, + getWriteBufferToItemsOptionsMock, +} from '../mocks'; + +import { + LinesResult, + importListItemsToStream, + writeBufferToItems, +} from './write_lines_to_bulk_list_items'; + +import { getListItemByValues } from '.'; + +jest.mock('./get_list_item_by_values', () => ({ + getListItemByValues: jest.fn(), +})); + +describe('write_lines_to_bulk_list_items', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('importListItemsToStream', () => { + test('It imports a set of items to a write buffer by calling "getListItemByValues" with an empty buffer', async () => { + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([]); + const options = getImportListItemsToStreamOptionsMock(); + const promise = importListItemsToStream(options); + options.stream.push(null); + await promise; + expect(getListItemByValues).toBeCalledWith(expect.objectContaining({ value: [] })); + }); + + test('It imports a set of items to a write buffer by calling "getListItemByValues" with a single value given', async () => { + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([]); + const options = getImportListItemsToStreamOptionsMock(); + const promise = importListItemsToStream(options); + options.stream.push('127.0.0.1\n'); + options.stream.push(null); + await promise; + expect(getListItemByValues).toBeCalledWith(expect.objectContaining({ value: ['127.0.0.1'] })); + }); + + test('It imports a set of items to a write buffer by calling "getListItemByValues" with two values given', async () => { + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([]); + const options = getImportListItemsToStreamOptionsMock(); + const promise = importListItemsToStream(options); + options.stream.push('127.0.0.1\n'); + options.stream.push('127.0.0.2\n'); + options.stream.push(null); + await promise; + expect(getListItemByValues).toBeCalledWith( + expect.objectContaining({ value: ['127.0.0.1', '127.0.0.2'] }) + ); + }); + }); + + describe('writeBufferToItems', () => { + test('It returns no duplicates and no lines processed when given empty items', async () => { + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([]); + const options = getWriteBufferToItemsOptionsMock(); + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 0, + linesProcessed: 0, + }; + expect(linesResult).toEqual(expected); + }); + + test('It returns no lines processed when given items but no buffer', async () => { + const data = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([data]); + const options = getWriteBufferToItemsOptionsMock(); + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 0, + linesProcessed: 0, + }; + expect(linesResult).toEqual(expected); + }); + + test('It returns 1 lines processed when given a buffer item that is not a duplicate', async () => { + const data = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([data]); + const options = getWriteBufferToItemsOptionsMock(); + options.buffer = ['255.255.255.255']; + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 0, + linesProcessed: 1, + }; + expect(linesResult).toEqual(expected); + }); + + test('It filters a duplicate value out and reports it as a duplicate', async () => { + const data = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([data]); + const options = getWriteBufferToItemsOptionsMock(); + options.buffer = [data.value]; + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 1, + linesProcessed: 0, + }; + expect(linesResult).toEqual(expected); + }); + + test('It filters a duplicate value out and reports it as a duplicate and processing a second value as not a duplicate', async () => { + const data = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([data]); + const options = getWriteBufferToItemsOptionsMock(); + options.buffer = ['255.255.255.255', data.value]; + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 1, + linesProcessed: 1, + }; + expect(linesResult).toEqual(expected); + }); + + test('It filters a duplicate value out and reports it as a duplicate and processing two other values', async () => { + const data = getListItemResponseMock(); + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([data]); + const options = getWriteBufferToItemsOptionsMock(); + options.buffer = ['255.255.255.255', '192.168.0.1', data.value]; + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 1, + linesProcessed: 2, + }; + expect(linesResult).toEqual(expected); + }); + + test('It filters two duplicate values out and reports processes a single value', async () => { + const dataItem1 = getListItemResponseMock(); + dataItem1.value = '127.0.0.1'; + const dataItem2 = getListItemResponseMock(); + dataItem2.value = '127.0.0.2'; + ((getListItemByValues as unknown) as jest.Mock).mockResolvedValueOnce([dataItem1, dataItem2]); + const options = getWriteBufferToItemsOptionsMock(); + options.buffer = [dataItem1.value, dataItem2.value, '192.168.0.0.1']; + const linesResult = await writeBufferToItems(options); + const expected: LinesResult = { + duplicatesFound: 2, + linesProcessed: 1, + }; + expect(linesResult).toEqual(expected); + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts b/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.ts new file mode 100644 index 00000000000000..542c2bb12d8e51 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/write_lines_to_bulk_list_items.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 { Readable } from 'stream'; + +import { APICaller } from 'kibana/server'; + +import { MetaOrUndefined, Type } from '../../../common/schemas'; + +import { BufferLines } from './buffer_lines'; +import { getListItemByValues } from './get_list_item_by_values'; +import { createListItemsBulk } from './create_list_items_bulk'; + +export interface ImportListItemsToStreamOptions { + listId: string; + stream: Readable; + callCluster: APICaller; + listItemIndex: string; + type: Type; + user: string; + meta: MetaOrUndefined; +} + +export const importListItemsToStream = ({ + listId, + stream, + callCluster, + listItemIndex, + type, + user, + meta, +}: ImportListItemsToStreamOptions): Promise => { + return new Promise(resolve => { + const readBuffer = new BufferLines({ input: stream }); + readBuffer.on('lines', async (lines: string[]) => { + await writeBufferToItems({ + buffer: lines, + callCluster, + listId, + listItemIndex, + meta, + type, + user, + }); + }); + + readBuffer.on('close', () => { + resolve(); + }); + }); +}; + +export interface WriteBufferToItemsOptions { + listId: string; + callCluster: APICaller; + listItemIndex: string; + buffer: string[]; + type: Type; + user: string; + meta: MetaOrUndefined; +} + +export interface LinesResult { + linesProcessed: number; + duplicatesFound: number; +} + +export const writeBufferToItems = async ({ + listId, + callCluster, + listItemIndex, + buffer, + type, + user, + meta, +}: WriteBufferToItemsOptions): Promise => { + const items = await getListItemByValues({ + callCluster, + listId, + listItemIndex, + type, + value: buffer, + }); + const duplicatesRemoved = buffer.filter( + bufferedValue => !items.some(item => item.value === bufferedValue) + ); + const linesProcessed = duplicatesRemoved.length; + const duplicatesFound = buffer.length - duplicatesRemoved.length; + await createListItemsBulk({ + callCluster, + listId, + listItemIndex, + meta, + type, + user, + value: duplicatesRemoved, + }); + return { duplicatesFound, linesProcessed }; +}; diff --git a/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.test.ts b/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.test.ts new file mode 100644 index 00000000000000..b08e5fa688b4b0 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.test.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 { + LIST_ID, + LIST_ITEM_INDEX, + getCallClusterMock, + getExportListItemsToStreamOptionsMock, + getResponseOptionsMock, + getSearchListItemMock, + getWriteNextResponseOptions, + getWriteResponseHitsToStreamOptionsMock, +} from '../mocks'; + +import { + exportListItemsToStream, + getResponse, + getSearchAfterFromResponse, + writeNextResponse, + writeResponseHitsToStream, +} from '.'; + +describe('write_list_items_to_stream', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('exportListItemsToStream', () => { + test('It exports empty list items to the stream as an empty array', done => { + const options = getExportListItemsToStreamOptionsMock(); + const firstResponse = getSearchListItemMock(); + firstResponse.hits.hits = []; + options.callCluster = getCallClusterMock(firstResponse); + exportListItemsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.on('finish', () => { + expect(chunks).toEqual([]); + done(); + }); + }); + + test('It exports single list item to the stream', done => { + const options = getExportListItemsToStreamOptionsMock(); + exportListItemsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.on('finish', () => { + expect(chunks).toEqual(['127.0.0.1']); + done(); + }); + }); + + test('It exports two list items to the stream', done => { + const options = getExportListItemsToStreamOptionsMock(); + const firstResponse = getSearchListItemMock(); + const secondResponse = getSearchListItemMock(); + firstResponse.hits.hits = [...firstResponse.hits.hits, ...secondResponse.hits.hits]; + options.callCluster = getCallClusterMock(firstResponse); + exportListItemsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.on('finish', () => { + expect(chunks).toEqual(['127.0.0.1', '127.0.0.1']); + done(); + }); + }); + + test('It exports two list items to the stream with two separate calls', done => { + const options = getExportListItemsToStreamOptionsMock(); + + const firstResponse = getSearchListItemMock(); + firstResponse.hits.hits[0].sort = ['some-sort-value']; + + const secondResponse = getSearchListItemMock(); + secondResponse.hits.hits[0]._source.ip = '255.255.255.255'; + + options.callCluster = jest + .fn() + .mockResolvedValueOnce(firstResponse) + .mockResolvedValueOnce(secondResponse); + + exportListItemsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.on('finish', () => { + expect(chunks).toEqual(['127.0.0.1', '255.255.255.255']); + done(); + }); + }); + }); + + describe('writeNextResponse', () => { + test('It returns an empty searchAfter response when there is no sort defined', async () => { + const options = getWriteNextResponseOptions(); + const searchAfter = await writeNextResponse(options); + expect(searchAfter).toEqual(undefined); + }); + + test('It returns a searchAfter response when there is a sort defined', async () => { + const listItem = getSearchListItemMock(); + listItem.hits.hits[0].sort = ['sort-value-1']; + const options = getWriteNextResponseOptions(); + options.callCluster = getCallClusterMock(listItem); + const searchAfter = await writeNextResponse(options); + expect(searchAfter).toEqual(['sort-value-1']); + }); + + test('It returns a searchAfter response of undefined when the response is empty', async () => { + const listItem = getSearchListItemMock(); + listItem.hits.hits = []; + const options = getWriteNextResponseOptions(); + options.callCluster = getCallClusterMock(listItem); + const searchAfter = await writeNextResponse(options); + expect(searchAfter).toEqual(undefined); + }); + }); + + describe('getSearchAfterFromResponse', () => { + test('It returns undefined if the hits array is empty', () => { + const response = getSearchListItemMock(); + response.hits.hits = []; + const searchAfter = getSearchAfterFromResponse({ response }); + expect(searchAfter).toEqual(undefined); + }); + + test('It returns undefined if the hits array does not have a sort', () => { + const response = getSearchListItemMock(); + response.hits.hits[0].sort = undefined; + const searchAfter = getSearchAfterFromResponse({ response }); + expect(searchAfter).toEqual(undefined); + }); + + test('It returns a sort of a single array if that single item exists', () => { + const response = getSearchListItemMock(); + response.hits.hits[0].sort = ['sort-value-1', 'sort-value-2']; + const searchAfter = getSearchAfterFromResponse({ response }); + expect(searchAfter).toEqual(['sort-value-1', 'sort-value-2']); + }); + + test('It returns a sort of the last array element of size 2', () => { + const response = getSearchListItemMock(); + const response2 = getSearchListItemMock(); + response2.hits.hits[0].sort = ['sort-value']; + response.hits.hits = [...response.hits.hits, ...response2.hits.hits]; + const searchAfter = getSearchAfterFromResponse({ response }); + expect(searchAfter).toEqual(['sort-value']); + }); + }); + + describe('getResponse', () => { + test('It returns a simple response with the default size of 100', async () => { + const options = getResponseOptionsMock(); + options.searchAfter = ['string 1', 'string 2']; + await getResponse(options); + const expected = { + body: { + query: { term: { list_id: LIST_ID } }, + search_after: ['string 1', 'string 2'], + sort: [{ tie_breaker_id: 'asc' }], + }, + ignoreUnavailable: true, + index: LIST_ITEM_INDEX, + size: 100, + }; + expect(options.callCluster).toBeCalledWith('search', expected); + }); + + test('It returns a simple response with expected values and size changed', async () => { + const options = getResponseOptionsMock(); + options.searchAfter = ['string 1', 'string 2']; + options.size = 33; + await getResponse(options); + const expected = { + body: { + query: { term: { list_id: LIST_ID } }, + search_after: ['string 1', 'string 2'], + sort: [{ tie_breaker_id: 'asc' }], + }, + ignoreUnavailable: true, + index: LIST_ITEM_INDEX, + size: 33, + }; + expect(options.callCluster).toBeCalledWith('search', expected); + }); + }); + + describe('writeResponseHitsToStream', () => { + test('it will push into the stream the mock response', done => { + const options = getWriteResponseHitsToStreamOptionsMock(); + writeResponseHitsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.end(() => { + expect(chunks).toEqual(['127.0.0.1']); + done(); + }); + }); + + test('it will push into the stream an empty mock response', done => { + const options = getWriteResponseHitsToStreamOptionsMock(); + options.response.hits.hits = []; + writeResponseHitsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.on('finish', () => { + expect(chunks).toEqual([]); + done(); + }); + options.stream.end(); + }); + + test('it will push into the stream 2 mock responses', done => { + const options = getWriteResponseHitsToStreamOptionsMock(); + const secondResponse = getSearchListItemMock(); + options.response.hits.hits = [...options.response.hits.hits, ...secondResponse.hits.hits]; + writeResponseHitsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.end(() => { + expect(chunks).toEqual(['127.0.0.1', '127.0.0.1']); + done(); + }); + }); + + test('it will push an additional string given to it such as a new line character', done => { + const options = getWriteResponseHitsToStreamOptionsMock(); + const secondResponse = getSearchListItemMock(); + options.response.hits.hits = [...options.response.hits.hits, ...secondResponse.hits.hits]; + options.stringToAppend = '\n'; + writeResponseHitsToStream(options); + + let chunks: string[] = []; + options.stream.on('data', (chunk: Buffer) => { + chunks = [...chunks, chunk.toString()]; + }); + + options.stream.end(() => { + expect(chunks).toEqual(['127.0.0.1\n', '127.0.0.1\n']); + done(); + }); + }); + + test('it will throw an exception with a status code if the hit_source is not a data type we expect', () => { + const options = getWriteResponseHitsToStreamOptionsMock(); + options.response.hits.hits[0]._source.ip = undefined; + options.response.hits.hits[0]._source.keyword = undefined; + const expected = `Encountered an error where hit._source was an unexpected type: ${JSON.stringify( + options.response.hits.hits[0]._source + )}`; + expect(() => writeResponseHitsToStream(options)).toThrow(expected); + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.ts b/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.ts new file mode 100644 index 00000000000000..b81e4a4fc73c20 --- /dev/null +++ b/x-pack/plugins/lists/server/services/items/write_list_items_to_stream.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PassThrough } from 'stream'; + +import { SearchResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { SearchEsListItemSchema } from '../../../common/schemas'; +import { ErrorWithStatusCode } from '../../error_with_status_code'; + +/** + * How many results to page through from the network at a time + * using search_after + */ +export const SIZE = 100; + +export interface ExportListItemsToStreamOptions { + listId: string; + callCluster: APICaller; + listItemIndex: string; + stream: PassThrough; + stringToAppend: string | null | undefined; +} + +export const exportListItemsToStream = ({ + listId, + callCluster, + stream, + listItemIndex, + stringToAppend, +}: ExportListItemsToStreamOptions): void => { + // Use a timeout to start the reading process on the next tick. + // and prevent the async await from bubbling up to the caller + setTimeout(async () => { + let searchAfter = await writeNextResponse({ + callCluster, + listId, + listItemIndex, + searchAfter: undefined, + stream, + stringToAppend, + }); + while (searchAfter != null) { + searchAfter = await writeNextResponse({ + callCluster, + listId, + listItemIndex, + searchAfter, + stream, + stringToAppend, + }); + } + stream.end(); + }); +}; + +export interface WriteNextResponseOptions { + listId: string; + callCluster: APICaller; + listItemIndex: string; + stream: PassThrough; + searchAfter: string[] | undefined; + stringToAppend: string | null | undefined; +} + +export const writeNextResponse = async ({ + listId, + callCluster, + stream, + listItemIndex, + searchAfter, + stringToAppend, +}: WriteNextResponseOptions): Promise => { + const response = await getResponse({ + callCluster, + listId, + listItemIndex, + searchAfter, + }); + + if (response.hits.hits.length) { + writeResponseHitsToStream({ response, stream, stringToAppend }); + return getSearchAfterFromResponse({ response }); + } else { + return undefined; + } +}; + +export const getSearchAfterFromResponse = ({ + response, +}: { + response: SearchResponse; +}): string[] | undefined => + response.hits.hits.length > 0 + ? response.hits.hits[response.hits.hits.length - 1].sort + : undefined; + +export interface GetResponseOptions { + callCluster: APICaller; + listId: string; + searchAfter: undefined | string[]; + listItemIndex: string; + size?: number; +} + +export const getResponse = async ({ + callCluster, + searchAfter, + listId, + listItemIndex, + size = SIZE, +}: GetResponseOptions): Promise> => { + return callCluster('search', { + body: { + query: { + term: { + list_id: listId, + }, + }, + search_after: searchAfter, + sort: [{ tie_breaker_id: 'asc' }], + }, + ignoreUnavailable: true, + index: listItemIndex, + size, + }); +}; + +export interface WriteResponseHitsToStreamOptions { + response: SearchResponse; + stream: PassThrough; + stringToAppend: string | null | undefined; +} + +export const writeResponseHitsToStream = ({ + response, + stream, + stringToAppend, +}: WriteResponseHitsToStreamOptions): void => { + const stringToAppendOrEmpty = stringToAppend ?? ''; + + response.hits.hits.forEach(hit => { + if (hit._source.ip != null) { + stream.push(`${hit._source.ip}${stringToAppendOrEmpty}`); + } else if (hit._source.keyword != null) { + stream.push(`${hit._source.keyword}${stringToAppendOrEmpty}`); + } else { + throw new ErrorWithStatusCode( + `Encountered an error where hit._source was an unexpected type: ${JSON.stringify( + hit._source + )}`, + 400 + ); + } + }); +}; diff --git a/x-pack/plugins/lists/server/services/lists/client.ts b/x-pack/plugins/lists/server/services/lists/client.ts new file mode 100644 index 00000000000000..ba22bf72cc18c3 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/client.ts @@ -0,0 +1,413 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { APICaller } from 'kibana/server'; + +import { ListItemArraySchema, ListItemSchema, ListSchema } from '../../../common/schemas'; +import { ConfigType } from '../../config'; +import { + createList, + deleteList, + getList, + getListIndex, + getListTemplate, + updateList, +} from '../../services/lists'; +import { + createListItem, + deleteListItem, + deleteListItemByValue, + exportListItemsToStream, + getListItem, + getListItemByValue, + getListItemByValues, + getListItemIndex, + getListItemTemplate, + importListItemsToStream, + updateListItem, +} from '../../services/items'; +import { + createBootstrapIndex, + deleteAllIndex, + deletePolicy, + deleteTemplate, + getIndexExists, + getPolicyExists, + getTemplateExists, + setPolicy, + setTemplate, +} from '../../siem_server_deps'; +import listsItemsPolicy from '../items/list_item_policy.json'; + +import listPolicy from './list_policy.json'; +import { + ConstructorOptions, + CreateListIfItDoesNotExistOptions, + CreateListItemOptions, + CreateListOptions, + DeleteListItemByValueOptions, + DeleteListItemOptions, + DeleteListOptions, + ExportListItemsToStreamOptions, + GetListItemByValueOptions, + GetListItemOptions, + GetListItemsByValueOptions, + GetListOptions, + ImportListItemsToStreamOptions, + UpdateListItemOptions, + UpdateListOptions, +} from './client_types'; + +export class ListClient { + private readonly spaceId: string; + private readonly user: string; + private readonly config: ConfigType; + private readonly callCluster: APICaller; + + constructor({ spaceId, user, config, callCluster }: ConstructorOptions) { + this.spaceId = spaceId; + this.user = user; + this.config = config; + this.callCluster = callCluster; + } + + public getListIndex = (): string => { + const { + spaceId, + config: { listIndex: listsIndexName }, + } = this; + return getListIndex({ listsIndexName, spaceId }); + }; + + public getListItemIndex = (): string => { + const { + spaceId, + config: { listItemIndex: listsItemsIndexName }, + } = this; + return getListItemIndex({ listsItemsIndexName, spaceId }); + }; + + public getList = async ({ id }: GetListOptions): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return getList({ callCluster, id, listIndex }); + }; + + public createList = async ({ + id, + name, + description, + type, + meta, + }: CreateListOptions): Promise => { + const { callCluster, user } = this; + const listIndex = this.getListIndex(); + return createList({ callCluster, description, id, listIndex, meta, name, type, user }); + }; + + public createListIfItDoesNotExist = async ({ + id, + name, + description, + type, + meta, + }: CreateListIfItDoesNotExistOptions): Promise => { + const list = await this.getList({ id }); + if (list == null) { + return this.createList({ description, id, meta, name, type }); + } else { + return list; + } + }; + + public getListIndexExists = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return getIndexExists(callCluster, listIndex); + }; + + public getListItemIndexExists = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return getIndexExists(callCluster, listItemIndex); + }; + + public createListBootStrapIndex = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return createBootstrapIndex(callCluster, listIndex); + }; + + public createListItemBootStrapIndex = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return createBootstrapIndex(callCluster, listItemIndex); + }; + + public getListPolicyExists = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return getPolicyExists(callCluster, listIndex); + }; + + public getListItemPolicyExists = async (): Promise => { + const { callCluster } = this; + const listsItemIndex = this.getListItemIndex(); + return getPolicyExists(callCluster, listsItemIndex); + }; + + public getListTemplateExists = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return getTemplateExists(callCluster, listIndex); + }; + + public getListItemTemplateExists = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return getTemplateExists(callCluster, listItemIndex); + }; + + public getListTemplate = (): Record => { + const listIndex = this.getListIndex(); + return getListTemplate(listIndex); + }; + + public getListItemTemplate = (): Record => { + const listItemIndex = this.getListItemIndex(); + return getListItemTemplate(listItemIndex); + }; + + public setListTemplate = async (): Promise => { + const { callCluster } = this; + const template = this.getListTemplate(); + const listIndex = this.getListIndex(); + return setTemplate(callCluster, listIndex, template); + }; + + public setListItemTemplate = async (): Promise => { + const { callCluster } = this; + const template = this.getListItemTemplate(); + const listItemIndex = this.getListItemIndex(); + return setTemplate(callCluster, listItemIndex, template); + }; + + public setListPolicy = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return setPolicy(callCluster, listIndex, listPolicy); + }; + + public setListItemPolicy = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return setPolicy(callCluster, listItemIndex, listsItemsPolicy); + }; + + public deleteListIndex = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return deleteAllIndex(callCluster, `${listIndex}-*`); + }; + + public deleteListItemIndex = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return deleteAllIndex(callCluster, `${listItemIndex}-*`); + }; + + public deleteListPolicy = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return deletePolicy(callCluster, listIndex); + }; + + public deleteListItemPolicy = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return deletePolicy(callCluster, listItemIndex); + }; + + public deleteListTemplate = async (): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + return deleteTemplate(callCluster, listIndex); + }; + + public deleteListItemTemplate = async (): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return deleteTemplate(callCluster, listItemIndex); + }; + + public deleteListItem = async ({ id }: DeleteListItemOptions): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return deleteListItem({ callCluster, id, listItemIndex }); + }; + + public deleteListItemByValue = async ({ + listId, + value, + type, + }: DeleteListItemByValueOptions): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return deleteListItemByValue({ + callCluster, + listId, + listItemIndex, + type, + value, + }); + }; + + public deleteList = async ({ id }: DeleteListOptions): Promise => { + const { callCluster } = this; + const listIndex = this.getListIndex(); + const listItemIndex = this.getListItemIndex(); + return deleteList({ + callCluster, + id, + listIndex, + listItemIndex, + }); + }; + + public exportListItemsToStream = ({ + stringToAppend, + listId, + stream, + }: ExportListItemsToStreamOptions): void => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + exportListItemsToStream({ + callCluster, + listId, + listItemIndex, + stream, + stringToAppend, + }); + }; + + public importListItemsToStream = async ({ + type, + listId, + stream, + meta, + }: ImportListItemsToStreamOptions): Promise => { + const { callCluster, user } = this; + const listItemIndex = this.getListItemIndex(); + return importListItemsToStream({ + callCluster, + listId, + listItemIndex, + meta, + stream, + type, + user, + }); + }; + + public getListItemByValue = async ({ + listId, + value, + type, + }: GetListItemByValueOptions): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return getListItemByValue({ + callCluster, + listId, + listItemIndex, + type, + value, + }); + }; + + public createListItem = async ({ + id, + listId, + value, + type, + meta, + }: CreateListItemOptions): Promise => { + const { callCluster, user } = this; + const listItemIndex = this.getListItemIndex(); + return createListItem({ + callCluster, + id, + listId, + listItemIndex, + meta, + type, + user, + value, + }); + }; + + public updateListItem = async ({ + id, + value, + meta, + }: UpdateListItemOptions): Promise => { + const { callCluster, user } = this; + const listItemIndex = this.getListItemIndex(); + return updateListItem({ + callCluster, + id, + listItemIndex, + meta, + user, + value, + }); + }; + + public updateList = async ({ + id, + name, + description, + meta, + }: UpdateListOptions): Promise => { + const { callCluster, user } = this; + const listIndex = this.getListIndex(); + return updateList({ + callCluster, + description, + id, + listIndex, + meta, + name, + user, + }); + }; + + public getListItem = async ({ id }: GetListItemOptions): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return getListItem({ + callCluster, + id, + listItemIndex, + }); + }; + + public getListItemByValues = async ({ + type, + listId, + value, + }: GetListItemsByValueOptions): Promise => { + const { callCluster } = this; + const listItemIndex = this.getListItemIndex(); + return getListItemByValues({ + callCluster, + listId, + listItemIndex, + type, + value, + }); + }; +} diff --git a/x-pack/plugins/lists/server/services/lists/client_types.ts b/x-pack/plugins/lists/server/services/lists/client_types.ts new file mode 100644 index 00000000000000..2cc58c02dbfcff --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/client_types.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 { PassThrough, Readable } from 'stream'; + +import { APICaller, KibanaRequest } from 'kibana/server'; + +import { SecurityPluginSetup } from '../../../../security/server'; +import { + Description, + DescriptionOrUndefined, + Id, + IdOrUndefined, + MetaOrUndefined, + Name, + NameOrUndefined, + Type, +} from '../../../common/schemas'; +import { ConfigType } from '../../config'; + +export interface ConstructorOptions { + callCluster: APICaller; + config: ConfigType; + request: KibanaRequest; + spaceId: string; + user: string; + security: SecurityPluginSetup | undefined | null; +} + +export interface GetListOptions { + id: Id; +} + +export interface DeleteListOptions { + id: Id; +} + +export interface DeleteListItemOptions { + id: Id; +} + +export interface CreateListOptions { + id: IdOrUndefined; + name: Name; + description: Description; + type: Type; + meta: MetaOrUndefined; +} + +export interface CreateListIfItDoesNotExistOptions { + id: Id; + name: Name; + description: Description; + type: Type; + meta: MetaOrUndefined; +} + +export interface DeleteListItemByValueOptions { + listId: string; + value: string; + type: Type; +} + +export interface GetListItemByValueOptions { + listId: string; + value: string; + type: Type; +} + +export interface ExportListItemsToStreamOptions { + stringToAppend: string | null | undefined; + listId: string; + stream: PassThrough; +} + +export interface ImportListItemsToStreamOptions { + listId: string; + type: Type; + stream: Readable; + meta: MetaOrUndefined; +} + +export interface CreateListItemOptions { + id: IdOrUndefined; + listId: string; + type: Type; + value: string; + meta: MetaOrUndefined; +} + +export interface UpdateListItemOptions { + id: Id; + value: string | null | undefined; + meta: MetaOrUndefined; +} + +export interface UpdateListOptions { + id: Id; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; +} + +export interface GetListItemOptions { + id: Id; +} + +export interface GetListItemsByValueOptions { + type: Type; + listId: string; + value: string[]; +} diff --git a/x-pack/plugins/lists/server/services/lists/create_list.test.ts b/x-pack/plugins/lists/server/services/lists/create_list.test.ts new file mode 100644 index 00000000000000..36284a70fb97df --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/create_list.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + LIST_ID, + LIST_INDEX, + getCreateListOptionsMock, + getIndexESListMock, + getListResponseMock, +} from '../mocks'; + +import { createList } from './create_list'; + +describe('crete_list', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list as expected with the id changed out for the elastic id', async () => { + const options = getCreateListOptionsMock(); + const list = await createList(options); + const expected = getListResponseMock(); + expected.id = 'elastic-id-123'; + expect(list).toEqual(expected); + }); + + test('It calls "callCluster" with body, index, and listIndex', async () => { + const options = getCreateListOptionsMock(); + await createList(options); + const body = getIndexESListMock(); + const expected = { + body, + id: LIST_ID, + index: LIST_INDEX, + }; + expect(options.callCluster).toBeCalledWith('index', expected); + }); + + test('It returns an auto-generated id if id is sent in undefined', async () => { + const options = getCreateListOptionsMock(); + options.id = undefined; + const list = await createList(options); + const expected = getListResponseMock(); + expected.id = 'elastic-id-123'; + expect(list).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/create_list.ts b/x-pack/plugins/lists/server/services/lists/create_list.ts new file mode 100644 index 00000000000000..ddbc99c88a877a --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/create_list.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 uuid from 'uuid'; +import { CreateDocumentResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { + Description, + IdOrUndefined, + IndexEsListSchema, + ListSchema, + MetaOrUndefined, + Name, + Type, +} from '../../../common/schemas'; + +export interface CreateListOptions { + id: IdOrUndefined; + type: Type; + name: Name; + description: Description; + callCluster: APICaller; + listIndex: string; + user: string; + meta: MetaOrUndefined; + dateNow?: string; + tieBreaker?: string; +} + +export const createList = async ({ + id, + name, + type, + description, + callCluster, + listIndex, + user, + meta, + dateNow, + tieBreaker, +}: CreateListOptions): Promise => { + const createdAt = dateNow ?? new Date().toISOString(); + const body: IndexEsListSchema = { + created_at: createdAt, + created_by: user, + description, + meta, + name, + tie_breaker_id: tieBreaker ?? uuid.v4(), + type, + updated_at: createdAt, + updated_by: user, + }; + const response: CreateDocumentResponse = await callCluster('index', { + body, + id, + index: listIndex, + }); + return { + id: response._id, + ...body, + }; +}; diff --git a/x-pack/plugins/lists/server/services/lists/delete_list.test.ts b/x-pack/plugins/lists/server/services/lists/delete_list.test.ts new file mode 100644 index 00000000000000..62b5e7c7aec4a3 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/delete_list.test.ts @@ -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 { + LIST_ID, + LIST_INDEX, + LIST_ITEM_INDEX, + getDeleteListOptionsMock, + getListResponseMock, +} from '../mocks'; + +import { getList } from './get_list'; +import { deleteList } from './delete_list'; + +jest.mock('./get_list', () => ({ + getList: jest.fn(), +})); + +describe('delete_list', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Delete returns a null if the list is also null', async () => { + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(null); + const options = getDeleteListOptionsMock(); + const deletedList = await deleteList(options); + expect(deletedList).toEqual(null); + }); + + test('Delete returns the list if a list is returned from getList', async () => { + const list = getListResponseMock(); + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(list); + const options = getDeleteListOptionsMock(); + const deletedList = await deleteList(options); + expect(deletedList).toEqual(list); + }); + + test('Delete calls "deleteByQuery" and "delete" if a list is returned from getList', async () => { + const list = getListResponseMock(); + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(list); + const options = getDeleteListOptionsMock(); + await deleteList(options); + const deleteByQuery = { + body: { query: { term: { list_id: LIST_ID } } }, + index: LIST_ITEM_INDEX, + }; + expect(options.callCluster).toBeCalledWith('deleteByQuery', deleteByQuery); + }); + + test('Delete calls "delete" second if a list is returned from getList', async () => { + const list = getListResponseMock(); + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(list); + const options = getDeleteListOptionsMock(); + await deleteList(options); + const deleteQuery = { + id: LIST_ID, + index: LIST_INDEX, + }; + expect(options.callCluster).toHaveBeenNthCalledWith(2, 'delete', deleteQuery); + }); + + test('Delete does not call data client if the list returns null', async () => { + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(null); + const options = getDeleteListOptionsMock(); + await deleteList(options); + expect(options.callCluster).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/delete_list.ts b/x-pack/plugins/lists/server/services/lists/delete_list.ts new file mode 100644 index 00000000000000..bc66c88b082a31 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/delete_list.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 { APICaller } from 'kibana/server'; + +import { Id, ListSchema } from '../../../common/schemas'; + +import { getList } from './get_list'; + +export interface DeleteListOptions { + id: Id; + callCluster: APICaller; + listIndex: string; + listItemIndex: string; +} + +export const deleteList = async ({ + id, + callCluster, + listIndex, + listItemIndex, +}: DeleteListOptions): Promise => { + const list = await getList({ callCluster, id, listIndex }); + if (list == null) { + return null; + } else { + await callCluster('deleteByQuery', { + body: { + query: { + term: { + list_id: id, + }, + }, + }, + index: listItemIndex, + }); + + await callCluster('delete', { + id, + index: listIndex, + }); + return list; + } +}; diff --git a/x-pack/plugins/lists/server/services/lists/get_list.test.ts b/x-pack/plugins/lists/server/services/lists/get_list.test.ts new file mode 100644 index 00000000000000..c997d5325296a6 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list.test.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 { + LIST_ID, + LIST_INDEX, + getCallClusterMock, + getListResponseMock, + getSearchListMock, +} from '../mocks'; + +import { getList } from './get_list'; + +describe('get_list', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list as expected if the list is found', async () => { + const data = getSearchListMock(); + const callCluster = getCallClusterMock(data); + const list = await getList({ callCluster, id: LIST_ID, listIndex: LIST_INDEX }); + const expected = getListResponseMock(); + expect(list).toEqual(expected); + }); + + test('it returns null if the search is empty', async () => { + const data = getSearchListMock(); + data.hits.hits = []; + const callCluster = getCallClusterMock(data); + const list = await getList({ callCluster, id: LIST_ID, listIndex: LIST_INDEX }); + expect(list).toEqual(null); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/get_list.ts b/x-pack/plugins/lists/server/services/lists/get_list.ts new file mode 100644 index 00000000000000..c04bd504ad8c01 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { Id, ListSchema, SearchEsListSchema } from '../../../common/schemas'; + +interface GetListOptions { + id: Id; + callCluster: APICaller; + listIndex: string; +} + +export const getList = async ({ + id, + callCluster, + listIndex, +}: GetListOptions): Promise => { + const result: SearchResponse = await callCluster('search', { + body: { + query: { + term: { + _id: id, + }, + }, + }, + ignoreUnavailable: true, + index: listIndex, + }); + if (result.hits.hits.length) { + return { + id: result.hits.hits[0]._id, + ...result.hits.hits[0]._source, + }; + } else { + return null; + } +}; diff --git a/x-pack/plugins/lists/server/services/lists/get_list_index.test.ts b/x-pack/plugins/lists/server/services/lists/get_list_index.test.ts new file mode 100644 index 00000000000000..f82928ffeddd2f --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list_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 { getListIndex } from './get_list_index'; + +describe('get_list_index', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('Returns the list index when there is a space', async () => { + const listIndex = getListIndex({ + listsIndexName: 'lists-index', + spaceId: 'test-space', + }); + expect(listIndex).toEqual('lists-index-test-space'); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/get_list_index.ts b/x-pack/plugins/lists/server/services/lists/get_list_index.ts new file mode 100644 index 00000000000000..5086603fa84035 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list_index.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. + */ + +interface GetListIndexOptions { + spaceId: string; + listsIndexName: string; +} + +export const getListIndex = ({ spaceId, listsIndexName }: GetListIndexOptions): string => + `${listsIndexName}-${spaceId}`; diff --git a/x-pack/plugins/lists/server/services/lists/get_list_template.test.ts b/x-pack/plugins/lists/server/services/lists/get_list_template.test.ts new file mode 100644 index 00000000000000..e25eaaafd855e9 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list_template.test.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 { getListTemplate } from './get_list_template'; + +jest.mock('./list_mappings.json', () => ({ + listMappings: {}, +})); + +describe('get_list_template', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list template with the string filled in', async () => { + const template = getListTemplate('some_index'); + expect(template).toEqual({ + index_patterns: ['some_index-*'], + mappings: { listMappings: {} }, + settings: { index: { lifecycle: { name: 'some_index', rollover_alias: 'some_index' } } }, + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/get_list_template.ts b/x-pack/plugins/lists/server/services/lists/get_list_template.ts new file mode 100644 index 00000000000000..9d93a051f2d108 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/get_list_template.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 listMappings from './list_mappings.json'; + +export const getListTemplate = (index: string): Record => ({ + index_patterns: [`${index}-*`], + mappings: listMappings, + settings: { + index: { + lifecycle: { + name: index, + rollover_alias: index, + }, + }, + }, +}); diff --git a/x-pack/legacy/plugins/uptime/common/graphql/resolver_types.ts b/x-pack/plugins/lists/server/services/lists/index.ts similarity index 55% rename from x-pack/legacy/plugins/uptime/common/graphql/resolver_types.ts rename to x-pack/plugins/lists/server/services/lists/index.ts index 22df610d2d516c..f704ef0b05b82f 100644 --- a/x-pack/legacy/plugins/uptime/common/graphql/resolver_types.ts +++ b/x-pack/plugins/lists/server/services/lists/index.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -type UMResolverResult = Promise | T; - -export type UMResolver = ( - parent: Parent, - args: Args, - context: Context -) => UMResolverResult; +export * from './create_list'; +export * from './delete_list'; +export * from './get_list'; +export * from './get_list_template'; +export * from './update_list'; +export * from './get_list_index'; diff --git a/x-pack/plugins/lists/server/services/lists/list_mappings.json b/x-pack/plugins/lists/server/services/lists/list_mappings.json new file mode 100644 index 00000000000000..1136a53da787d2 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/list_mappings.json @@ -0,0 +1,33 @@ +{ + "dynamic": "strict", + "properties": { + "name": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "meta": { + "enabled": "false", + "type": "object" + }, + "created_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } +} diff --git a/x-pack/plugins/lists/server/services/lists/list_policy.json b/x-pack/plugins/lists/server/services/lists/list_policy.json new file mode 100644 index 00000000000000..a4c84f73e78960 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/list_policy.json @@ -0,0 +1,14 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "0ms", + "actions": { + "rollover": { + "max_size": "50gb" + } + } + } + } + } +} diff --git a/x-pack/plugins/lists/server/services/lists/update_list.test.ts b/x-pack/plugins/lists/server/services/lists/update_list.test.ts new file mode 100644 index 00000000000000..09bf0ee69c981f --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/update_list.test.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 { getListResponseMock, getUpdateListOptionsMock } from '../mocks'; + +import { updateList } from './update_list'; +import { getList } from './get_list'; + +jest.mock('./get_list', () => ({ + getList: jest.fn(), +})); + +describe('update_list', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns a list as expected with the id changed out for the elastic id when there is a list to update', async () => { + const list = getListResponseMock(); + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(list); + const options = getUpdateListOptionsMock(); + const updatedList = await updateList(options); + const expected = getListResponseMock(); + expected.id = 'elastic-id-123'; + expect(updatedList).toEqual(expected); + }); + + test('it returns null when there is not a list to update', async () => { + ((getList as unknown) as jest.Mock).mockResolvedValueOnce(null); + const options = getUpdateListOptionsMock(); + const updatedList = await updateList(options); + expect(updatedList).toEqual(null); + }); +}); diff --git a/x-pack/plugins/lists/server/services/lists/update_list.ts b/x-pack/plugins/lists/server/services/lists/update_list.ts new file mode 100644 index 00000000000000..9859adf0624857 --- /dev/null +++ b/x-pack/plugins/lists/server/services/lists/update_list.ts @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CreateDocumentResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { + DescriptionOrUndefined, + Id, + ListSchema, + MetaOrUndefined, + NameOrUndefined, + UpdateEsListSchema, +} from '../../../common/schemas'; + +import { getList } from '.'; + +export interface UpdateListOptions { + id: Id; + callCluster: APICaller; + listIndex: string; + user: string; + name: NameOrUndefined; + description: DescriptionOrUndefined; + meta: MetaOrUndefined; + dateNow?: string; +} + +export const updateList = async ({ + id, + name, + description, + callCluster, + listIndex, + user, + meta, + dateNow, +}: UpdateListOptions): Promise => { + const updatedAt = dateNow ?? new Date().toISOString(); + const list = await getList({ callCluster, id, listIndex }); + if (list == null) { + return null; + } else { + const doc: UpdateEsListSchema = { + description, + meta, + name, + updated_at: updatedAt, + updated_by: user, + }; + const response: CreateDocumentResponse = await callCluster('update', { + body: { doc }, + id, + index: listIndex, + }); + return { + created_at: list.created_at, + created_by: list.created_by, + description: description ?? list.description, + id: response._id, + meta, + name: name ?? list.name, + tie_breaker_id: list.tie_breaker_id, + type: list.type, + updated_at: updatedAt, + updated_by: user, + }; + } +}; diff --git a/x-pack/plugins/lists/server/services/mocks/get_call_cluster_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_call_cluster_mock.ts new file mode 100644 index 00000000000000..180ecbb7973392 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_call_cluster_mock.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 { CreateDocumentResponse } from 'elasticsearch'; +import { APICaller } from 'kibana/server'; + +import { LIST_INDEX } from './lists_services_mock_constants'; +import { getShardMock } from './get_shard_mock'; + +export const getEmptyCreateDocumentResponseMock = (): CreateDocumentResponse => ({ + _id: 'elastic-id-123', + _index: LIST_INDEX, + _shards: getShardMock(), + _type: '', + _version: 1, + created: true, + result: '', +}); + +export const getCallClusterMock = ( + callCluster: unknown = getEmptyCreateDocumentResponseMock() +): APICaller => jest.fn().mockResolvedValue(callCluster); diff --git a/x-pack/plugins/lists/server/services/mocks/get_create_list_item_bulk_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_create_list_item_bulk_options_mock.ts new file mode 100644 index 00000000000000..fcdad66d652518 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_create_list_item_bulk_options_mock.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 { CreateListItemsBulkOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { + DATE_NOW, + LIST_ID, + LIST_ITEM_INDEX, + META, + TIE_BREAKERS, + TYPE, + USER, + VALUE, + VALUE_2, +} from './lists_services_mock_constants'; + +export const getCreateListItemBulkOptionsMock = (): CreateListItemsBulkOptions => ({ + callCluster: getCallClusterMock(), + dateNow: DATE_NOW, + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + meta: META, + tieBreaker: TIE_BREAKERS, + type: TYPE, + user: USER, + value: [VALUE, VALUE_2], +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_create_list_item_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_create_list_item_options_mock.ts new file mode 100644 index 00000000000000..17e3ad2f8de083 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_create_list_item_options_mock.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 { CreateListItemOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { + DATE_NOW, + LIST_ID, + LIST_ITEM_ID, + LIST_ITEM_INDEX, + META, + TIE_BREAKER, + TYPE, + USER, +} from './lists_services_mock_constants'; + +export const getCreateListItemOptionsMock = (): CreateListItemOptions => ({ + callCluster: getCallClusterMock(), + dateNow: DATE_NOW, + id: LIST_ITEM_ID, + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + meta: META, + tieBreaker: TIE_BREAKER, + type: TYPE, + user: USER, + value: '127.0.0.1', +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_create_list_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_create_list_options_mock.ts new file mode 100644 index 00000000000000..0ea6533fc122a8 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_create_list_options_mock.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CreateListOptions } from '../lists'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { + DATE_NOW, + DESCRIPTION, + LIST_ID, + LIST_INDEX, + META, + NAME, + TIE_BREAKER, + TYPE, + USER, +} from './lists_services_mock_constants'; + +export const getCreateListOptionsMock = (): CreateListOptions => ({ + callCluster: getCallClusterMock(), + dateNow: DATE_NOW, + description: DESCRIPTION, + id: LIST_ID, + listIndex: LIST_INDEX, + meta: META, + name: NAME, + tieBreaker: TIE_BREAKER, + type: TYPE, + user: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_by_value_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_by_value_options_mock.ts new file mode 100644 index 00000000000000..f6859e72d71b35 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_by_value_options_mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DeleteListItemByValueOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_ITEM_INDEX, TYPE, VALUE } from './lists_services_mock_constants'; + +export const getDeleteListItemByValueOptionsMock = (): DeleteListItemByValueOptions => ({ + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + type: TYPE, + value: VALUE, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_options_mock.ts new file mode 100644 index 00000000000000..271c185860b074 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_delete_list_item_options_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 { DeleteListItemOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ITEM_ID, LIST_ITEM_INDEX } from './lists_services_mock_constants'; + +export const getDeleteListItemOptionsMock = (): DeleteListItemOptions => ({ + callCluster: getCallClusterMock(), + id: LIST_ITEM_ID, + listItemIndex: LIST_ITEM_INDEX, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_delete_list_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_delete_list_options_mock.ts new file mode 100644 index 00000000000000..8ec92dfa4ef775 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_delete_list_options_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. + */ + +import { DeleteListOptions } from '../lists'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_INDEX, LIST_ITEM_INDEX } from './lists_services_mock_constants'; + +export const getDeleteListOptionsMock = (): DeleteListOptions => ({ + callCluster: getCallClusterMock(), + id: LIST_ID, + listIndex: LIST_INDEX, + listItemIndex: LIST_ITEM_INDEX, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_import_list_items_to_stream_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_import_list_items_to_stream_options_mock.ts new file mode 100644 index 00000000000000..d7541f3e09e6cd --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_import_list_items_to_stream_options_mock.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 { ImportListItemsToStreamOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_ITEM_INDEX, META, TYPE, USER } from './lists_services_mock_constants'; +import { TestReadable } from './test_readable'; + +export const getImportListItemsToStreamOptionsMock = (): ImportListItemsToStreamOptions => ({ + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + meta: META, + stream: new TestReadable(), + type: TYPE, + user: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_index_es_list_item_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_index_es_list_item_mock.ts new file mode 100644 index 00000000000000..574e4afcb36f05 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_index_es_list_item_mock.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 { IndexEsListItemSchema } from '../../../common/schemas'; + +import { DATE_NOW, LIST_ID, META, TIE_BREAKER, USER, VALUE } from './lists_services_mock_constants'; + +export const getIndexESListItemMock = (ip = VALUE): IndexEsListItemSchema => ({ + created_at: DATE_NOW, + created_by: USER, + ip, + list_id: LIST_ID, + meta: META, + tie_breaker_id: TIE_BREAKER, + updated_at: DATE_NOW, + updated_by: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_index_es_list_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_index_es_list_mock.ts new file mode 100644 index 00000000000000..4e4d8d9c572e44 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_index_es_list_mock.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IndexEsListSchema } from '../../../common/schemas'; + +import { + DATE_NOW, + DESCRIPTION, + META, + NAME, + TIE_BREAKER, + TYPE, + USER, +} from './lists_services_mock_constants'; + +export const getIndexESListMock = (): IndexEsListSchema => ({ + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + meta: META, + name: NAME, + tie_breaker_id: TIE_BREAKER, + type: TYPE, + updated_at: DATE_NOW, + updated_by: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_list_item_by_value_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_list_item_by_value_options_mock.ts new file mode 100644 index 00000000000000..96bc22ca7e6f27 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_list_item_by_value_options_mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GetListItemByValueOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_ITEM_INDEX, TYPE, VALUE } from './lists_services_mock_constants'; + +export const getListItemByValueOptionsMocks = (): GetListItemByValueOptions => ({ + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + type: TYPE, + value: VALUE, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_list_item_by_values_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_list_item_by_values_options_mock.ts new file mode 100644 index 00000000000000..f21f97dc8d15f2 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_list_item_by_values_options_mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { GetListItemByValuesOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_ITEM_INDEX, TYPE, VALUE, VALUE_2 } from './lists_services_mock_constants'; + +export const getListItemByValuesOptionsMocks = (): GetListItemByValuesOptions => ({ + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + type: TYPE, + value: [VALUE, VALUE_2], +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_list_item_response_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_list_item_response_mock.ts new file mode 100644 index 00000000000000..1a30282ddaebac --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_list_item_response_mock.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 { ListItemSchema } from '../../../common/schemas'; + +import { DATE_NOW, LIST_ID, LIST_ITEM_ID, USER, VALUE } from './lists_services_mock_constants'; + +export const getListItemResponseMock = (): ListItemSchema => ({ + created_at: DATE_NOW, + created_by: USER, + id: LIST_ITEM_ID, + list_id: LIST_ID, + meta: {}, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'ip', + updated_at: DATE_NOW, + updated_by: USER, + value: VALUE, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_list_response_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_list_response_mock.ts new file mode 100644 index 00000000000000..ea068d774c4edc --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_list_response_mock.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 { ListSchema } from '../../../common/schemas'; + +import { DATE_NOW, DESCRIPTION, LIST_ID, NAME, USER } from './lists_services_mock_constants'; + +export const getListResponseMock = (): ListSchema => ({ + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + id: LIST_ID, + meta: {}, + name: NAME, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'ip', + updated_at: DATE_NOW, + updated_by: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_search_es_list_item_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_search_es_list_item_mock.ts new file mode 100644 index 00000000000000..5e9fd8995c0eb6 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_search_es_list_item_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. + */ + +import { SearchEsListItemSchema } from '../../../common/schemas'; + +import { DATE_NOW, LIST_ID, USER, VALUE } from './lists_services_mock_constants'; + +export const getSearchEsListItemMock = (): SearchEsListItemSchema => ({ + created_at: DATE_NOW, + created_by: USER, + ip: VALUE, + keyword: undefined, + list_id: LIST_ID, + meta: {}, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + updated_at: DATE_NOW, + updated_by: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_search_es_list_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_search_es_list_mock.ts new file mode 100644 index 00000000000000..6a565437617ba8 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_search_es_list_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. + */ + +import { SearchEsListSchema } from '../../../common/schemas'; + +import { DATE_NOW, DESCRIPTION, NAME, USER } from './lists_services_mock_constants'; + +export const getSearchEsListMock = (): SearchEsListSchema => ({ + created_at: DATE_NOW, + created_by: USER, + description: DESCRIPTION, + meta: {}, + name: NAME, + tie_breaker_id: '6a76b69d-80df-4ab2-8c3e-85f466b06a0e', + type: 'ip', + updated_at: DATE_NOW, + updated_by: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_search_list_item_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_search_list_item_mock.ts new file mode 100644 index 00000000000000..9f877c8168cca9 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_search_list_item_mock.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchResponse } from 'elasticsearch'; + +import { SearchEsListItemSchema } from '../../../common/schemas'; + +import { getShardMock } from './get_shard_mock'; +import { LIST_INDEX, LIST_ITEM_ID } from './lists_services_mock_constants'; +import { getSearchEsListItemMock } from './get_search_es_list_item_mock'; + +export const getSearchListItemMock = (): SearchResponse => ({ + _scroll_id: '123', + _shards: getShardMock(), + hits: { + hits: [ + { + _id: LIST_ITEM_ID, + _index: LIST_INDEX, + _score: 0, + _source: getSearchEsListItemMock(), + _type: '', + }, + ], + max_score: 0, + total: 1, + }, + timed_out: false, + took: 10, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_search_list_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_search_list_mock.ts new file mode 100644 index 00000000000000..9728139eab42ad --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_search_list_mock.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchResponse } from 'elasticsearch'; + +import { SearchEsListSchema } from '../../../common/schemas'; + +import { getShardMock } from './get_shard_mock'; +import { LIST_ID, LIST_INDEX } from './lists_services_mock_constants'; +import { getSearchEsListMock } from './get_search_es_list_mock'; + +export const getSearchListMock = (): SearchResponse => ({ + _scroll_id: '123', + _shards: getShardMock(), + hits: { + hits: [ + { + _id: LIST_ID, + _index: LIST_INDEX, + _score: 0, + _source: getSearchEsListMock(), + _type: '', + }, + ], + max_score: 0, + total: 1, + }, + timed_out: false, + took: 10, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_shard_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_shard_mock.ts new file mode 100644 index 00000000000000..4cc6577d5e5312 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_shard_mock.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ShardsResponse } from 'elasticsearch'; + +export const getShardMock = (): ShardsResponse => ({ + failed: 0, + skipped: 0, + successful: 0, + total: 0, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_update_list_item_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_update_list_item_options_mock.ts new file mode 100644 index 00000000000000..0555997941baa0 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_update_list_item_options_mock.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { UpdateListItemOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { + DATE_NOW, + LIST_ITEM_ID, + LIST_ITEM_INDEX, + META, + USER, + VALUE, +} from './lists_services_mock_constants'; + +export const getUpdateListItemOptionsMock = (): UpdateListItemOptions => ({ + callCluster: getCallClusterMock(), + dateNow: DATE_NOW, + id: LIST_ITEM_ID, + listItemIndex: LIST_ITEM_INDEX, + meta: META, + user: USER, + value: VALUE, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_update_list_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_update_list_options_mock.ts new file mode 100644 index 00000000000000..fe6fc37eaf81e6 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_update_list_options_mock.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 { UpdateListOptions } from '../lists'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { + DATE_NOW, + DESCRIPTION, + LIST_ID, + LIST_INDEX, + META, + NAME, + USER, +} from './lists_services_mock_constants'; + +export const getUpdateListOptionsMock = (): UpdateListOptions => ({ + callCluster: getCallClusterMock(), + dateNow: DATE_NOW, + description: DESCRIPTION, + id: LIST_ID, + listIndex: LIST_INDEX, + meta: META, + name: NAME, + user: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_write_buffer_to_items_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_write_buffer_to_items_options_mock.ts new file mode 100644 index 00000000000000..d6b7d70c1aa778 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_write_buffer_to_items_options_mock.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { WriteBufferToItemsOptions } from '../items'; + +import { getCallClusterMock } from './get_call_cluster_mock'; +import { LIST_ID, LIST_ITEM_INDEX, META, TYPE, USER } from './lists_services_mock_constants'; + +export const getWriteBufferToItemsOptionsMock = (): WriteBufferToItemsOptions => ({ + buffer: [], + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + meta: META, + type: TYPE, + user: USER, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/get_write_list_items_to_stream_options_mock.ts b/x-pack/plugins/lists/server/services/mocks/get_write_list_items_to_stream_options_mock.ts new file mode 100644 index 00000000000000..c945818a83e8a8 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/get_write_list_items_to_stream_options_mock.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 { Stream } from 'stream'; + +import { + ExportListItemsToStreamOptions, + GetResponseOptions, + WriteNextResponseOptions, + WriteResponseHitsToStreamOptions, +} from '../items'; + +import { LIST_ID, LIST_ITEM_INDEX } from './lists_services_mock_constants'; +import { getSearchListItemMock } from './get_search_list_item_mock'; +import { getCallClusterMock } from './get_call_cluster_mock'; + +export const getExportListItemsToStreamOptionsMock = (): ExportListItemsToStreamOptions => ({ + callCluster: getCallClusterMock(getSearchListItemMock()), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + stream: new Stream.PassThrough(), + stringToAppend: undefined, +}); + +export const getWriteNextResponseOptions = (): WriteNextResponseOptions => ({ + callCluster: getCallClusterMock(getSearchListItemMock()), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + searchAfter: [], + stream: new Stream.PassThrough(), + stringToAppend: undefined, +}); + +export const getResponseOptionsMock = (): GetResponseOptions => ({ + callCluster: getCallClusterMock(), + listId: LIST_ID, + listItemIndex: LIST_ITEM_INDEX, + searchAfter: [], + size: 100, +}); + +export const getWriteResponseHitsToStreamOptionsMock = (): WriteResponseHitsToStreamOptions => ({ + response: getSearchListItemMock(), + stream: new Stream.PassThrough(), + stringToAppend: undefined, +}); diff --git a/x-pack/plugins/lists/server/services/mocks/index.ts b/x-pack/plugins/lists/server/services/mocks/index.ts new file mode 100644 index 00000000000000..c555ba322fa2bb --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './get_call_cluster_mock'; +export * from './get_delete_list_options_mock'; +export * from './get_create_list_options_mock'; +export * from './get_list_response_mock'; +export * from './get_search_list_mock'; +export * from './get_shard_mock'; +export * from './lists_services_mock_constants'; +export * from './get_update_list_options_mock'; +export * from './get_create_list_item_options_mock'; +export * from './get_list_item_response_mock'; +export * from './get_index_es_list_mock'; +export * from './get_index_es_list_item_mock'; +export * from './get_create_list_item_bulk_options_mock'; +export * from './get_delete_list_item_by_value_options_mock'; +export * from './get_delete_list_item_options_mock'; +export * from './get_list_item_by_values_options_mock'; +export * from './get_search_es_list_mock'; +export * from './get_search_es_list_item_mock'; +export * from './get_list_item_by_value_options_mock'; +export * from './get_update_list_item_options_mock'; +export * from './get_write_buffer_to_items_options_mock'; +export * from './get_import_list_items_to_stream_options_mock'; +export * from './get_write_list_items_to_stream_options_mock'; +export * from './get_search_list_item_mock'; +export * from './test_readable'; diff --git a/x-pack/plugins/lists/server/services/mocks/lists_services_mock_constants.ts b/x-pack/plugins/lists/server/services/mocks/lists_services_mock_constants.ts new file mode 100644 index 00000000000000..d174211f348ea5 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/lists_services_mock_constants.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. + */ +export const DATE_NOW = '2020-04-20T15:25:31.830Z'; +export const USER = 'some user'; +export const LIST_INDEX = '.lists'; +export const LIST_ITEM_INDEX = '.items'; +export const NAME = 'some name'; +export const DESCRIPTION = 'some description'; +export const LIST_ID = 'some-list-id'; +export const LIST_ITEM_ID = 'some-list-item-id'; +export const TIE_BREAKER = '6a76b69d-80df-4ab2-8c3e-85f466b06a0e'; +export const TIE_BREAKERS = [ + '21530991-4051-46ec-bc35-2afa09a1b0b5', + '3c662054-ae37-4aa9-9936-3e8e2ea26775', + '60e49a20-3a23-48b6-8bf9-ed5e3b70f7a0', + '38814080-a40f-4358-992a-3b875f9b7dec', + '29fa61be-aaaf-411c-a78a-7059e3f723f1', + '9c19c959-cb9d-4cd2-99e4-1ea2baf0ef0e', + 'd409308c-f94b-4b3a-8234-bbd7a80c9140', + '87824c99-cd83-45c4-8aa6-4ad95dfea62c', + '7b940c17-9355-479f-b882-f3e575718f79', + '5983ad0c-4ef4-4fa0-8308-80ab9ecc4f74', +]; +export const META = {}; +export const TYPE = 'ip'; +export const VALUE = '127.0.0.1'; +export const VALUE_2 = '255.255.255'; diff --git a/x-pack/plugins/lists/server/services/mocks/test_readable.ts b/x-pack/plugins/lists/server/services/mocks/test_readable.ts new file mode 100644 index 00000000000000..52ad6de4840058 --- /dev/null +++ b/x-pack/plugins/lists/server/services/mocks/test_readable.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 { Readable } from 'stream'; + +export class TestReadable extends Readable { + public _read(): void {} +} diff --git a/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.test.ts b/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.test.ts new file mode 100644 index 00000000000000..3b6f58479a2f22 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.test.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 { getSearchEsListItemMock } from '../mocks'; +import { Type } from '../../../common/schemas'; + +import { deriveTypeFromItem } from './derive_type_from_es_type'; + +describe('derive_type_from_es_type', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns the item ip if it exists', () => { + const item = getSearchEsListItemMock(); + const derivedType = deriveTypeFromItem({ item }); + const expected: Type = 'ip'; + expect(derivedType).toEqual(expected); + }); + + test('it returns the item keyword if it exists', () => { + const item = getSearchEsListItemMock(); + item.ip = undefined; + item.keyword = 'some keyword'; + const derivedType = deriveTypeFromItem({ item }); + const expected: Type = 'keyword'; + expect(derivedType).toEqual(expected); + }); + + test('it throws an error with status code if neither one exists', () => { + const item = getSearchEsListItemMock(); + item.ip = undefined; + item.keyword = undefined; + const expected = `Was expecting a valid type from the Elastic Search List Item such as ip or keyword but did not found one here ${JSON.stringify( + item + )}`; + expect(() => deriveTypeFromItem({ item })).toThrowError(expected); + }); +}); diff --git a/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.ts b/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.ts new file mode 100644 index 00000000000000..7a65e74bf49474 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/derive_type_from_es_type.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 { SearchEsListItemSchema, Type } from '../../../common/schemas'; +import { ErrorWithStatusCode } from '../../error_with_status_code'; + +interface DeriveTypeFromItemOptions { + item: SearchEsListItemSchema; +} + +export const deriveTypeFromItem = ({ item }: DeriveTypeFromItemOptions): Type => { + if (item.ip != null) { + return 'ip'; + } else if (item.keyword != null) { + return 'keyword'; + } else { + throw new ErrorWithStatusCode( + `Was expecting a valid type from the Elastic Search List Item such as ip or keyword but did not found one here ${JSON.stringify( + item + )}`, + 400 + ); + } +}; diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts b/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts new file mode 100644 index 00000000000000..3d48e44e26eaa3 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.test.ts @@ -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 { QueryFilterType, getQueryFilterFromTypeValue } from './get_query_filter_from_type_value'; + +describe('get_query_filter_from_type_value', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it returns an ip if given an ip', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'ip', + value: ['127.0.0.1'], + }); + const expected: QueryFilterType = [ + { term: { list_id: 'list-123' } }, + { terms: { ip: ['127.0.0.1'] } }, + ]; + expect(queryFilter).toEqual(expected); + }); + + test('it returns two ip if given two ip', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'ip', + value: ['127.0.0.1', '127.0.0.2'], + }); + const expected: QueryFilterType = [ + { term: { list_id: 'list-123' } }, + { terms: { ip: ['127.0.0.1', '127.0.0.2'] } }, + ]; + expect(queryFilter).toEqual(expected); + }); + + test('it returns a keyword if given a keyword', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'keyword', + value: ['host-name-1'], + }); + const expected: QueryFilterType = [ + { term: { list_id: 'list-123' } }, + { terms: { keyword: ['host-name-1'] } }, + ]; + expect(queryFilter).toEqual(expected); + }); + + test('it returns two keywords if given two values', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'keyword', + value: ['host-name-1', 'host-name-2'], + }); + const expected: QueryFilterType = [ + { term: { list_id: 'list-123' } }, + { terms: { keyword: ['host-name-1', 'host-name-2'] } }, + ]; + expect(queryFilter).toEqual(expected); + }); + + test('it returns an empty keyword given an empty value', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'keyword', + value: [], + }); + const expected: QueryFilterType = [ + { term: { list_id: 'list-123' } }, + { terms: { keyword: [] } }, + ]; + expect(queryFilter).toEqual(expected); + }); + + test('it returns an empty ip given an empty value', () => { + const queryFilter = getQueryFilterFromTypeValue({ + listId: 'list-123', + type: 'ip', + value: [], + }); + const expected: QueryFilterType = [{ term: { list_id: 'list-123' } }, { terms: { ip: [] } }]; + expect(queryFilter).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts b/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts new file mode 100644 index 00000000000000..3f50efe0c6c56b --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/get_query_filter_from_type_value.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Type } from '../../../common/schemas'; + +export type QueryFilterType = Array< + { term: { list_id: string } } | { terms: { ip: string[] } } | { terms: { keyword: string[] } } +>; + +export const getQueryFilterFromTypeValue = ({ + type, + value, + listId, +}: { + type: Type; + value: string[]; + listId: string; + // We disable the consistent return since we want to use typescript for exhaustive type checks + // eslint-disable-next-line consistent-return +}): QueryFilterType => { + const filter: QueryFilterType = [{ term: { list_id: listId } }]; + switch (type) { + case 'ip': { + return [...filter, ...[{ terms: { ip: value } }]]; + } + case 'keyword': { + return [...filter, ...[{ terms: { keyword: value } }]]; + } + } +}; diff --git a/x-pack/plugins/lists/server/services/utils/index.ts b/x-pack/plugins/lists/server/services/utils/index.ts new file mode 100644 index 00000000000000..8a44b5ab607bf5 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './get_query_filter_from_type_value'; +export * from './transform_elastic_to_list_item'; +export * from './transform_list_item_to_elastic_query'; +export * from './derive_type_from_es_type'; diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts new file mode 100644 index 00000000000000..3b9864be6df538 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ListItemArraySchema } from '../../../common/schemas'; +import { getListItemResponseMock, getSearchListItemMock } from '../mocks'; + +import { transformElasticToListItem } from './transform_elastic_to_list_item'; + +describe('transform_elastic_to_list_item', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it transforms an elastic type to a list item type', () => { + const response = getSearchListItemMock(); + const queryFilter = transformElasticToListItem({ + response, + type: 'ip', + }); + const expected: ListItemArraySchema = [getListItemResponseMock()]; + expect(queryFilter).toEqual(expected); + }); + + test('it transforms an elastic keyword type to a list item type', () => { + const response = getSearchListItemMock(); + response.hits.hits[0]._source.ip = undefined; + response.hits.hits[0]._source.keyword = 'host-name-example'; + const queryFilter = transformElasticToListItem({ + response, + type: 'keyword', + }); + const listItemResponse = getListItemResponseMock(); + listItemResponse.type = 'keyword'; + listItemResponse.value = 'host-name-example'; + const expected: ListItemArraySchema = [listItemResponse]; + expect(queryFilter).toEqual(expected); + }); + + test('it does a throw if it cannot determine the list item type from "ip"', () => { + const response = getSearchListItemMock(); + response.hits.hits[0]._source.ip = undefined; + response.hits.hits[0]._source.keyword = 'host-name-example'; + expect(() => + transformElasticToListItem({ + response, + type: 'ip', + }) + ).toThrow('Was expecting ip to not be null/undefined'); + }); + + test('it does a throw if it cannot determine the list item type from "keyword"', () => { + const response = getSearchListItemMock(); + response.hits.hits[0]._source.ip = '127.0.0.1'; + response.hits.hits[0]._source.keyword = undefined; + expect(() => + transformElasticToListItem({ + response, + type: 'keyword', + }) + ).toThrow('Was expecting keyword to not be null/undefined'); + }); +}); diff --git a/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts new file mode 100644 index 00000000000000..2dc0f4fe7a8216 --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/transform_elastic_to_list_item.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchResponse } from 'elasticsearch'; + +import { ListItemArraySchema, SearchEsListItemSchema, Type } from '../../../common/schemas'; +import { ErrorWithStatusCode } from '../../error_with_status_code'; + +export interface TransformElasticToListItemOptions { + response: SearchResponse; + type: Type; +} + +export const transformElasticToListItem = ({ + response, + type, +}: TransformElasticToListItemOptions): ListItemArraySchema => { + return response.hits.hits.map(hit => { + const { + _id, + _source: { + created_at, + updated_at, + updated_by, + created_by, + list_id, + tie_breaker_id, + ip, + keyword, + meta, + }, + } = hit; + + const baseTypes = { + created_at, + created_by, + id: _id, + list_id, + meta, + tie_breaker_id, + type, + updated_at, + updated_by, + }; + + switch (type) { + case 'ip': { + if (ip == null) { + throw new ErrorWithStatusCode('Was expecting ip to not be null/undefined', 400); + } + return { + ...baseTypes, + value: ip, + }; + } + case 'keyword': { + if (keyword == null) { + throw new ErrorWithStatusCode('Was expecting keyword to not be null/undefined', 400); + } + return { + ...baseTypes, + value: keyword, + }; + } + } + return assertUnreachable(); + }); +}; + +const assertUnreachable = (): never => { + throw new Error('Unknown type in elastic_to_list_items'); +}; diff --git a/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts b/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.ts new file mode 100644 index 00000000000000..217cad30bfdbbd --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.test.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 { EsDataTypeUnion, Type } from '../../../common/schemas'; + +import { transformListItemToElasticQuery } from './transform_list_item_to_elastic_query'; + +describe('transform_elastic_to_elastic_query', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('it transforms a ip type and value to a union', () => { + const elasticQuery = transformListItemToElasticQuery({ + type: 'ip', + value: '127.0.0.1', + }); + const expected: EsDataTypeUnion = { ip: '127.0.0.1' }; + expect(elasticQuery).toEqual(expected); + }); + + test('it transforms a keyword type and value to a union', () => { + const elasticQuery = transformListItemToElasticQuery({ + type: 'keyword', + value: 'host-name', + }); + const expected: EsDataTypeUnion = { keyword: 'host-name' }; + expect(elasticQuery).toEqual(expected); + }); + + test('it throws if the type is not known', () => { + const type: Type = 'made-up' as Type; + expect(() => + transformListItemToElasticQuery({ + type, + value: 'some-value', + }) + ).toThrow('Unknown type: "made-up" in transformListItemToElasticQuery'); + }); +}); diff --git a/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts b/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts new file mode 100644 index 00000000000000..051802cc41b5ba --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/transform_list_item_to_elastic_query.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EsDataTypeUnion, Type } from '../../../common/schemas'; + +export const transformListItemToElasticQuery = ({ + type, + value, +}: { + type: Type; + value: string; +}): EsDataTypeUnion => { + switch (type) { + case 'ip': { + return { + ip: value, + }; + } + case 'keyword': { + return { + keyword: value, + }; + } + } + return assertUnreachable(type); +}; + +const assertUnreachable = (type: string): never => { + throw new Error(`Unknown type: "${type}" in transformListItemToElasticQuery`); +}; diff --git a/x-pack/plugins/lists/server/siem_server_deps.ts b/x-pack/plugins/lists/server/siem_server_deps.ts new file mode 100644 index 00000000000000..e78debc8e43492 --- /dev/null +++ b/x-pack/plugins/lists/server/siem_server_deps.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { + transformError, + buildSiemResponse, +} from '../../siem/server/lib/detection_engine/routes/utils'; +export { deleteTemplate } from '../../siem/server/lib/detection_engine/index/delete_template'; +export { deletePolicy } from '../../siem/server/lib/detection_engine/index/delete_policy'; +export { deleteAllIndex } from '../../siem/server/lib/detection_engine/index/delete_all_index'; +export { setPolicy } from '../../siem/server/lib/detection_engine/index/set_policy'; +export { setTemplate } from '../../siem/server/lib/detection_engine/index/set_template'; +export { getTemplateExists } from '../../siem/server/lib/detection_engine/index/get_template_exists'; +export { getPolicyExists } from '../../siem/server/lib/detection_engine/index/get_policy_exists'; +export { createBootstrapIndex } from '../../siem/server/lib/detection_engine/index/create_bootstrap_index'; +export { getIndexExists } from '../../siem/server/lib/detection_engine/index/get_index_exists'; +export { buildRouteValidation } from '../../siem/server/utils/build_validation/route_validation'; +export { validate } from '../../siem/server/lib/detection_engine/routes/rules/validate'; diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/plugins/lists/server/types.ts new file mode 100644 index 00000000000000..e0e4495d47c341 --- /dev/null +++ b/x-pack/plugins/lists/server/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 { IContextProvider, RequestHandler } from 'kibana/server'; + +import { SecurityPluginSetup } from '../../security/server'; +import { SpacesPluginSetup } from '../../spaces/server'; + +import { ListClient } from './services/lists/client'; + +export type ContextProvider = IContextProvider, 'lists'>; + +export interface PluginsSetup { + security: SecurityPluginSetup | undefined | null; + spaces: SpacesPluginSetup | undefined | null; +} + +export type ContextProviderReturn = Promise<{ getListClient: () => ListClient }>; +declare module 'src/core/server' { + interface RequestHandlerContext { + lists?: { + getListClient: () => ListClient; + }; + } +} diff --git a/x-pack/plugins/maps/public/actions/map_actions.d.ts b/x-pack/plugins/maps/public/actions/map_actions.d.ts index c8db284a5c4f1a..38c56405787eb2 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.d.ts @@ -74,3 +74,11 @@ export function updateMapSetting( settingKey: string, settingValue: string | boolean | number ): AnyAction; + +export function cloneLayer(layerId: string): AnyAction; + +export function fitToLayerExtent(layerId: string): AnyAction; + +export function removeLayer(layerId: string): AnyAction; + +export function toggleLayerVisible(layerId: string): AnyAction; diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap index 27ea52bfed0440..f1cb1a88647539 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap @@ -9,12 +9,10 @@ exports[`TOCEntry is rendered 1`] = `
-
-
-
-
-
{ - dispatch(toggleLayerVisible(layerId)); - }, - fitToBounds: layerId => { - dispatch(fitToLayerExtent(layerId)); - }, - cloneLayer: layerId => { - dispatch(cloneLayer(layerId)); - }, - removeLayer: layerId => { - dispatch(removeLayer(layerId)); - }, hideTOCDetails: layerId => { dispatch(hideTOCDetails(layerId)); }, diff --git a/x-pack/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap similarity index 92% rename from x-pack/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap index af836ceffa4b7e..b8c652909408ac 100644 --- a/x-pack/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`LayerTocActions is rendered 1`] = ` +exports[`TOCEntryActionsPopover is rendered 1`] = ` , "name": "Hide layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "editLayerButton", + "disabled": false, "icon": , "name": "Edit layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "cloneLayerButton", @@ -104,6 +107,7 @@ exports[`LayerTocActions is rendered 1`] = ` />, "name": "Clone layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "removeLayerButton", @@ -113,6 +117,7 @@ exports[`LayerTocActions is rendered 1`] = ` />, "name": "Remove layer", "onClick": [Function], + "toolTipContent": null, }, ], "title": "Layer actions", @@ -123,7 +128,7 @@ exports[`LayerTocActions is rendered 1`] = ` `; -exports[`LayerTocActions should disable fit to data when supportsFitToBounds is false 1`] = ` +exports[`TOCEntryActionsPopover should disable fit to data when supportsFitToBounds is false 1`] = ` , "name": "Hide layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "editLayerButton", + "disabled": false, "icon": , "name": "Edit layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "cloneLayerButton", @@ -227,6 +235,7 @@ exports[`LayerTocActions should disable fit to data when supportsFitToBounds is />, "name": "Clone layer", "onClick": [Function], + "toolTipContent": null, }, Object { "data-test-subj": "removeLayerButton", @@ -236,6 +245,7 @@ exports[`LayerTocActions should disable fit to data when supportsFitToBounds is />, "name": "Remove layer", "onClick": [Function], + "toolTipContent": null, }, ], "title": "Layer actions", @@ -246,7 +256,7 @@ exports[`LayerTocActions should disable fit to data when supportsFitToBounds is `; -exports[`LayerTocActions should not show edit actions in read only mode 1`] = ` +exports[`TOCEntryActionsPopover should not show edit actions in read only mode 1`] = ` , "name": "Hide layer", "onClick": [Function], + "toolTipContent": null, }, ], "title": "Layer actions", diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.ts new file mode 100644 index 00000000000000..1437370557efc4 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/index.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 { AnyAction, Dispatch } from 'redux'; +import { connect } from 'react-redux'; +import { MapStoreState } from '../../../../../../reducers/store'; +import { + fitToLayerExtent, + toggleLayerVisible, + cloneLayer, + removeLayer, +} from '../../../../../../actions/map_actions'; +import { getMapZoom, isUsingSearch } from '../../../../../../selectors/map_selectors'; +import { getIsReadOnly } from '../../../../../../selectors/ui_selectors'; +import { TOCEntryActionsPopover } from './toc_entry_actions_popover'; + +function mapStateToProps(state: MapStoreState) { + return { + isReadOnly: getIsReadOnly(state), + isUsingSearch: isUsingSearch(state), + zoom: getMapZoom(state), + }; +} + +function mapDispatchToProps(dispatch: Dispatch) { + return { + cloneLayer: (layerId: string) => { + dispatch(cloneLayer(layerId)); + }, + fitToBounds: (layerId: string) => { + dispatch(fitToLayerExtent(layerId)); + }, + removeLayer: (layerId: string) => { + dispatch(removeLayer(layerId)); + }, + toggleVisible: (layerId: string) => { + dispatch(toggleLayerVisible(layerId)); + }, + }; +} + +const connectedTOCEntryActionsPopover = connect( + mapStateToProps, + mapDispatchToProps +)(TOCEntryActionsPopover); +export { connectedTOCEntryActionsPopover as TOCEntryActionsPopover }; diff --git a/x-pack/plugins/maps/public/components/layer_toc_actions.test.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx similarity index 52% rename from x-pack/plugins/maps/public/components/layer_toc_actions.test.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx index c3a8f59c4c736a..b873119fd7d139 100644 --- a/x-pack/plugins/maps/public/components/layer_toc_actions.test.js +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx @@ -3,21 +3,45 @@ * 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 React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { AbstractLayer, ILayer } from '../../../../../../layers/layer'; +import { AbstractSource, ISource } from '../../../../../../layers/sources/source'; +import { AbstractStyle, IStyle } from '../../../../../../layers/styles/style'; -import { LayerTocActions } from './layer_toc_actions'; +import { TOCEntryActionsPopover } from './toc_entry_actions_popover'; -let supportsFitToBounds; -const layerMock = { - supportsFitToBounds: () => { +let supportsFitToBounds: boolean; + +class MockSource extends AbstractSource implements ISource {} + +class MockStyle extends AbstractStyle implements IStyle {} + +class LayerMock extends AbstractLayer implements ILayer { + constructor() { + const sourceDescriptor = { + type: 'mySourceType', + }; + const source = new MockSource(sourceDescriptor); + const style = new MockStyle({ type: 'myStyleType' }); + const layerDescriptor = { + id: 'testLayer', + sourceDescriptor, + }; + super({ layerDescriptor, source, style }); + } + + async supportsFitToBounds(): Promise { return supportsFitToBounds; - }, - isVisible: () => { + } + + isVisible() { return true; - }, - getIconAndTooltipContent: (zoom, isUsingSearch) => { + } + + getIconAndTooltipContent(zoom: number, isUsingSearch: boolean) { return { icon: mockIcon, tooltipContent: `simulated tooltip content at zoom: ${zoom}`, @@ -28,24 +52,31 @@ const layerMock = { }, ], }; - }, -}; + } +} const defaultProps = { + cloneLayer: () => {}, displayName: 'layer 1', + editLayer: () => {}, escapedDisplayName: 'layer1', - zoom: 0, - layer: layerMock, + fitToBounds: () => {}, + isEditButtonDisabled: false, + isReadOnly: false, isUsingSearch: true, + layer: new LayerMock(), + removeLayer: () => {}, + toggleVisible: () => {}, + zoom: 0, }; -describe('LayerTocActions', () => { +describe('TOCEntryActionsPopover', () => { beforeEach(() => { supportsFitToBounds = true; }); test('is rendered', async () => { - const component = shallowWithIntl(); + const component = shallowWithIntl(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -56,7 +87,9 @@ describe('LayerTocActions', () => { }); test('should not show edit actions in read only mode', async () => { - const component = shallowWithIntl(); + const component = shallowWithIntl( + + ); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); @@ -68,7 +101,7 @@ describe('LayerTocActions', () => { test('should disable fit to data when supportsFitToBounds is false', async () => { supportsFitToBounds = false; - const component = shallowWithIntl(); + const component = shallowWithIntl(); // Ensure all promises resolve await new Promise(resolve => process.nextTick(resolve)); diff --git a/x-pack/plugins/maps/public/components/layer_toc_actions.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx similarity index 81% rename from x-pack/plugins/maps/public/components/layer_toc_actions.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index d79eda16037cb7..d628cca61de113 100644 --- a/x-pack/plugins/maps/public/components/layer_toc_actions.js +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -8,8 +8,31 @@ import React, { Component, Fragment } from 'react'; import { EuiButtonEmpty, EuiPopover, EuiContextMenu, EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ILayer } from '../../../../../../layers/layer'; + +interface Props { + cloneLayer: (layerId: string) => void; + displayName: string; + editLayer: () => void; + escapedDisplayName: string; + fitToBounds: (layerId: string) => void; + isEditButtonDisabled: boolean; + isReadOnly: boolean; + isUsingSearch: boolean; + layer: ILayer; + removeLayer: (layerId: string) => void; + toggleVisible: (layerId: string) => void; + zoom: number; +} + +interface State { + isPopoverOpen: boolean; + supportsFitToBounds: boolean; +} + +export class TOCEntryActionsPopover extends Component { + private _isMounted: boolean = false; -export class LayerTocActions extends Component { state = { isPopoverOpen: false, supportsFitToBounds: false, @@ -43,6 +66,22 @@ export class LayerTocActions extends Component { })); }; + _cloneLayer() { + this.props.cloneLayer(this.props.layer.getId()); + } + + _fitToBounds() { + this.props.fitToBounds(this.props.layer.getId()); + } + + _removeLayer() { + this.props.fitToBounds(this.props.layer.getId()); + } + + _toggleVisible() { + this.props.toggleVisible(this.props.layer.getId()); + } + _renderPopoverToggleButton() { const { icon, tooltipContent, footnotes } = this.props.layer.getIconAndTooltipContent( this.props.zoom, @@ -108,7 +147,7 @@ export class LayerTocActions extends Component { disabled: !this.state.supportsFitToBounds, onClick: () => { this._closePopover(); - this.props.fitToBounds(); + this._fitToBounds(); }, }, { @@ -121,20 +160,23 @@ export class LayerTocActions extends Component { }), icon: , 'data-test-subj': 'layerVisibilityToggleButton', + toolTipContent: null, onClick: () => { this._closePopover(); - this.props.toggleVisible(); + this._toggleVisible(); }, }, ]; if (!this.props.isReadOnly) { actionItems.push({ + disabled: this.props.isEditButtonDisabled, name: i18n.translate('xpack.maps.layerTocActions.editLayerTitle', { defaultMessage: 'Edit layer', }), icon: , 'data-test-subj': 'editLayerButton', + toolTipContent: null, onClick: () => { this._closePopover(); this.props.editLayer(); @@ -145,10 +187,11 @@ export class LayerTocActions extends Component { defaultMessage: 'Clone layer', }), icon: , + toolTipContent: null, 'data-test-subj': 'cloneLayerButton', onClick: () => { this._closePopover(); - this.props.cloneLayer(); + this._cloneLayer(); }, }); actionItems.push({ @@ -156,10 +199,11 @@ export class LayerTocActions extends Component { defaultMessage: 'Remove layer', }), icon: , + toolTipContent: null, 'data-test-subj': 'removeLayerButton', onClick: () => { this._closePopover(); - this.props.removeLayer(); + this._removeLayer(); }, }); } diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js index fe56523fb25807..c0ce24fef9cd86 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js @@ -8,7 +8,7 @@ import React from 'react'; import classNames from 'classnames'; import { EuiIcon, EuiOverlayMask, EuiButtonIcon, EuiConfirmModal } from '@elastic/eui'; -import { LayerTocActions } from '../../../../../components/layer_toc_actions'; +import { TOCEntryActionsPopover } from './toc_entry_actions_popover'; import { i18n } from '@kbn/i18n'; function escapeLayerName(name) { @@ -124,6 +124,7 @@ export class TOCEntry extends React.Component { return (
- { - fitToBounds(layer.getId()); - }} - zoom={zoom} - toggleVisible={() => { - toggleVisible(layer.getId()); - }} displayName={this.state.displayName} escapedDisplayName={escapeLayerName(this.state.displayName)} - cloneLayer={() => { - cloneLayer(layer.getId()); - }} editLayer={this._openLayerPanelWithCheck} - isReadOnly={isReadOnly} - removeLayer={() => { - removeLayer(layer.getId()); - }} + isEditButtonDisabled={this.props.isEditButtonDisabled} /> {this._renderLayerIcons()} diff --git a/x-pack/plugins/maps/public/layers/layer.tsx b/x-pack/plugins/maps/public/layers/layer.tsx index 13fe447cec3dad..dccf413b489f1b 100644 --- a/x-pack/plugins/maps/public/layers/layer.tsx +++ b/x-pack/plugins/maps/public/layers/layer.tsx @@ -45,7 +45,7 @@ export interface ILayer { supportsFitToBounds(): Promise; getAttributions(): Promise; getLabel(): string; - getCustomIconAndTooltipContent(): IconAndTooltipContent; + getCustomIconAndTooltipContent(): CustomIconAndTooltipContent; getIconAndTooltipContent(zoomLevel: number, isUsingSearch: boolean): IconAndTooltipContent; renderLegendDetails(): ReactElement | null; showAtZoomLevel(zoom: number): boolean; @@ -87,7 +87,11 @@ export type Footnote = { export type IconAndTooltipContent = { icon?: ReactElement | null; tooltipContent?: string | null; - footnotes?: Footnote[] | null; + footnotes: Footnote[]; +}; +export type CustomIconAndTooltipContent = { + icon: ReactElement | null; + tooltipContent?: string | null; areResultsTrimmed?: boolean; }; @@ -212,7 +216,7 @@ export class AbstractLayer implements ILayer { return this._descriptor.label ? this._descriptor.label : ''; } - getCustomIconAndTooltipContent(): IconAndTooltipContent { + getCustomIconAndTooltipContent(): CustomIconAndTooltipContent { return { icon: , }; diff --git a/x-pack/plugins/maps/public/layers/layer_wizard_registry.ts b/x-pack/plugins/maps/public/layers/layer_wizard_registry.ts index 633e8c86d8c94f..7715541b1c52d7 100644 --- a/x-pack/plugins/maps/public/layers/layer_wizard_registry.ts +++ b/x-pack/plugins/maps/public/layers/layer_wizard_registry.ts @@ -20,6 +20,7 @@ export type RenderWizardArguments = { }; export type LayerWizard = { + checkVisibility?: () => boolean; description: string; icon: string; isIndexingSource?: boolean; @@ -34,5 +35,7 @@ export function registerLayerWizard(layerWizard: LayerWizard) { } export function getLayerWizards(): LayerWizard[] { - return [...registry]; + return registry.filter(layerWizard => { + return layerWizard.checkVisibility ? layerWizard.checkVisibility() : true; + }); } diff --git a/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_boundaries_layer_wizard.tsx b/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_boundaries_layer_wizard.tsx index f31e770df2d958..a6e2e7f42657c3 100644 --- a/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_boundaries_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_boundaries_layer_wizard.tsx @@ -12,8 +12,13 @@ import { LayerWizard, RenderWizardArguments } from '../../layer_wizard_registry' import { EMSFileCreateSourceEditor } from './create_source_editor'; // @ts-ignore import { EMSFileSource, sourceTitle } from './ems_file_source'; +// @ts-ignore +import { isEmsEnabled } from '../../../meta'; export const emsBoundariesLayerWizardConfig: LayerWizard = { + checkVisibility: () => { + return isEmsEnabled(); + }, description: i18n.translate('xpack.maps.source.emsFileDescription', { defaultMessage: 'Administrative boundaries from Elastic Maps Service', }), diff --git a/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_base_map_layer_wizard.tsx index ced33a0bcf84a9..fc745edbabee87 100644 --- a/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_base_map_layer_wizard.tsx @@ -12,8 +12,13 @@ import { EMSTMSSource, sourceTitle } from './ems_tms_source'; import { VectorTileLayer } from '../../vector_tile_layer'; // @ts-ignore import { TileServiceSelect } from './tile_service_select'; +// @ts-ignore +import { isEmsEnabled } from '../../../meta'; export const emsBaseMapLayerWizardConfig: LayerWizard = { + checkVisibility: () => { + return isEmsEnabled(); + }, description: i18n.translate('xpack.maps.source.emsTileDescription', { defaultMessage: 'Tile map service from Elastic Maps Service', }), diff --git a/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx index 4321501760fafd..a9adec2bda2c81 100644 --- a/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_layer_wizard.tsx @@ -12,8 +12,14 @@ import { KibanaRegionmapSource, sourceTitle } from './kibana_regionmap_source'; import { VectorLayer } from '../../vector_layer'; // @ts-ignore import { CreateSourceEditor } from './create_source_editor'; +// @ts-ignore +import { getKibanaRegionList } from '../../../meta'; export const kibanaRegionMapLayerWizardConfig: LayerWizard = { + checkVisibility: () => { + const regions = getKibanaRegionList(); + return regions.length; + }, description: i18n.translate('xpack.maps.source.kbnRegionMapDescription', { defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml', }), diff --git a/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx index aeea2d6084f847..141fabeedd3e51 100644 --- a/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_base_map_layer_wizard.tsx @@ -12,8 +12,14 @@ import { CreateSourceEditor } from './create_source_editor'; // @ts-ignore import { KibanaTilemapSource, sourceTitle } from './kibana_tilemap_source'; import { TileLayer } from '../../tile_layer'; +// @ts-ignore +import { getKibanaTileMap } from '../../../meta'; export const kibanaBasemapLayerWizardConfig: LayerWizard = { + checkVisibility: () => { + const tilemap = getKibanaTileMap(); + return !!tilemap.url; + }, description: i18n.translate('xpack.maps.source.kbnTMSDescription', { defaultMessage: 'Tile map service configured in kibana.yml', }), diff --git a/x-pack/plugins/maps/public/meta.js b/x-pack/plugins/maps/public/meta.js index d4612554cf00b9..c3245e8e98db2e 100644 --- a/x-pack/plugins/maps/public/meta.js +++ b/x-pack/plugins/maps/public/meta.js @@ -36,12 +36,15 @@ function fetchFunction(...args) { return fetch(...args); } +export function isEmsEnabled() { + return getInjectedVarFunc()('isEmsEnabled', true); +} + let emsClient = null; let latestLicenseId = null; export function getEMSClient() { if (!emsClient) { - const isEmsEnabled = getInjectedVarFunc()('isEmsEnabled', true); - if (isEmsEnabled) { + if (isEmsEnabled()) { const proxyElasticMapsServiceInMaps = getInjectedVarFunc()( 'proxyElasticMapsServiceInMaps', false @@ -86,7 +89,7 @@ export function getEMSClient() { } export function getGlyphUrl() { - if (!getInjectedVarFunc()('isEmsEnabled', true)) { + if (!isEmsEnabled()) { return ''; } return getInjectedVarFunc()('proxyElasticMapsServiceInMaps', false) diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts index 4d0f652af982a2..bc881d06f62ced 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts @@ -22,4 +22,6 @@ export function getMapSettings(state: MapStoreState): MapSettings; export function hasMapSettingsChanges(state: MapStoreState): boolean; +export function isUsingSearch(state: MapStoreState): boolean; + export function getSpatialFiltersLayer(state: MapStoreState): IVectorLayer; diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index 2a449c95faa5b5..da5fd3ac252096 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -7,6 +7,7 @@ import { KibanaRequest } from 'kibana/server'; export const userMlCapabilities = { + canAccessML: false, // Anomaly Detection canGetJobs: false, canGetDatafeeds: false, @@ -18,6 +19,10 @@ export const userMlCapabilities = { canGetFilters: false, // Data Frame Analytics canGetDataFrameAnalytics: false, + // Annotations + canGetAnnotations: false, + canCreateAnnotation: false, + canDeleteAnnotation: false, }; export const adminMlCapabilities = { @@ -26,9 +31,11 @@ export const adminMlCapabilities = { canDeleteJob: false, canOpenJob: false, canCloseJob: false, + canUpdateJob: false, canForecastJob: false, + canCreateDatafeed: false, + canDeleteDatafeed: false, canStartStopDatafeed: false, - canUpdateJob: false, canUpdateDatafeed: false, canPreviewDatafeed: false, // Calendars @@ -38,8 +45,8 @@ export const adminMlCapabilities = { canCreateFilter: false, canDeleteFilter: false, // Data Frame Analytics - canDeleteDataFrameAnalytics: false, canCreateDataFrameAnalytics: false, + canDeleteDataFrameAnalytics: false, canStartStopDataFrameAnalytics: false, }; @@ -47,7 +54,9 @@ export type UserMlCapabilities = typeof userMlCapabilities; export type AdminMlCapabilities = typeof adminMlCapabilities; export type MlCapabilities = UserMlCapabilities & AdminMlCapabilities; -export const basicLicenseMlCapabilities = ['canFindFileStructure'] as Array; +export const basicLicenseMlCapabilities = ['canAccessML', 'canFindFileStructure'] as Array< + keyof MlCapabilities +>; export function getDefaultCapabilities(): MlCapabilities { return { @@ -56,6 +65,23 @@ export function getDefaultCapabilities(): MlCapabilities { }; } +export function getPluginPrivileges() { + const userMlCapabilitiesKeys = Object.keys(userMlCapabilities); + const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities); + const allMlCapabilities = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys]; + + return { + user: { + ui: userMlCapabilitiesKeys, + api: userMlCapabilitiesKeys.map(k => `ml:${k}`), + }, + admin: { + ui: allMlCapabilities, + api: allMlCapabilities.map(k => `ml:${k}`), + }, + }; +} + export interface MlCapabilitiesResponse { capabilities: MlCapabilities; upgradeInProgress: boolean; diff --git a/x-pack/plugins/ml/common/util/job_utils.d.ts b/x-pack/plugins/ml/common/util/job_utils.d.ts index bfad422e0ab48d..4528fbfbb774d7 100644 --- a/x-pack/plugins/ml/common/util/job_utils.d.ts +++ b/x-pack/plugins/ml/common/util/job_utils.d.ts @@ -52,3 +52,5 @@ export function getLatestDataOrBucketTimestamp( ): number; export function prefixDatafeedId(datafeedId: string, prefix: string): string; + +export function splitIndexPatternNames(indexPatternName: string): string[]; diff --git a/x-pack/plugins/ml/common/util/job_utils.js b/x-pack/plugins/ml/common/util/job_utils.js index de0aa4b8866297..8fe5733ce67bd6 100644 --- a/x-pack/plugins/ml/common/util/job_utils.js +++ b/x-pack/plugins/ml/common/util/job_utils.js @@ -588,3 +588,9 @@ export function processCreatedBy(customSettings) { delete customSettings.created_by; } } + +export function splitIndexPatternNames(indexPatternName) { + return indexPatternName.includes(',') + ? indexPatternName.split(',').map(i => i.trim()) + : [indexPatternName]; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx index 8c65af1d92959f..cc75ddbe08cfb9 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx @@ -18,6 +18,7 @@ import { } from '../../hooks/use_create_analytics_form'; import { State } from '../../hooks/use_create_analytics_form/state'; import { DataFrameAnalyticsListRow } from './common'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; interface PropDefinition { /** @@ -322,6 +323,8 @@ interface CloneActionProps { * to support EuiContext with a valid DOM structure without nested buttons. */ export const CloneAction: FC = ({ createAnalyticsForm, item }) => { + const canCreateDataFrameAnalytics: boolean = checkPermission('canCreateDataFrameAnalytics'); + const buttonText = i18n.translate('xpack.ml.dataframe.analyticsList.cloneJobButtonLabel', { defaultMessage: 'Clone job', }); @@ -338,6 +341,7 @@ export const CloneAction: FC = ({ createAnalyticsForm, item }) iconType="copy" onClick={onClick} aria-label={buttonText} + disabled={canCreateDataFrameAnalytics === false} > {buttonText} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/default_configs.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/default_configs.ts index 306fd82dc87582..9dda8eec206e45 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/default_configs.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/default_configs.ts @@ -7,6 +7,7 @@ import { IndexPatternTitle } from '../../../../../../../common/types/kibana'; import { Field, Aggregation, EVENT_RATE_FIELD_ID } from '../../../../../../../common/types/fields'; import { Job, Datafeed, Detector } from '../../../../../../../common/types/anomaly_detection_jobs'; +import { splitIndexPatternNames } from '../../../../../../../common/util/job_utils'; export function createEmptyJob(): Job { return { @@ -28,7 +29,7 @@ export function createEmptyDatafeed(indexPatternTitle: IndexPatternTitle): Dataf return { datafeed_id: '', job_id: '', - indices: [indexPatternTitle], + indices: splitIndexPatternNames(indexPatternTitle), query: {}, }; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts index 50a84eb3d11cb0..69df2773f9f8d7 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts @@ -50,7 +50,7 @@ function getWizardUrlFromCloningJob(job: CombinedJob) { break; } - const indexPatternId = getIndexPatternIdFromName(job.datafeed_config.indices[0]); + const indexPatternId = getIndexPatternIdFromName(job.datafeed_config.indices.join()); return `jobs/new_job/${page}?index=${indexPatternId}&_g=()`; } diff --git a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx index b2eda12abc578c..c379cd702daee0 100644 --- a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx @@ -82,14 +82,14 @@ export const AnalyticsPanel: FC = ({ jobCreationDisabled }) => { title={

{i18n.translate('xpack.ml.overview.analyticsList.createFirstJobMessage', { - defaultMessage: 'Create your first analytics job', + defaultMessage: 'Create your first data frame analytics job', })}

} body={

{i18n.translate('xpack.ml.overview.analyticsList.emptyPromptText', { - defaultMessage: `Data frame analytics enable you to perform different analyses of your data and annotate it with the results. The analytics job stores the annotated data, as well as a copy of the source data, in a new index.`, + defaultMessage: `Data frame analytics enable you to perform different analyses of your data and annotates it with the results. The job puts the annotated data and a copy of the source data in a new index.`, })}

} diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx index 5f5c3f7c28670a..dac39b1a2071da 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx @@ -172,7 +172,7 @@ export const AnomalyDetectionPanel: FC = ({ jobCreationDisabled }) => {

{i18n.translate('xpack.ml.overview.anomalyDetection.emptyPromptText', { - defaultMessage: `Machine learning makes it easy to detect anomalies in time series data stored in Elasticsearch. Track one metric from a single machine or hundreds of metrics across thousands of machines. Start automatically spotting the anomalies hiding in your data and resolve issues faster.`, + defaultMessage: `Anomaly detection enables you to find unusual behavior in time series data. Start automatically spotting the anomalies hiding in your data and resolve issues faster.`, })}

diff --git a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx index 219c195bab111e..3e4e9cfbd2b663 100644 --- a/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/sidebar.tsx @@ -11,7 +11,6 @@ import { useMlKibana } from '../../contexts/kibana'; const createJobLink = '#/jobs/new_job/step/index_or_search'; const feedbackLink = 'https://www.elastic.co/community/'; -const whatIsMachineLearningLink = 'https://www.elastic.co/what-is/elasticsearch-machine-learning'; interface Props { createAnomalyDetectionJobDisabled: boolean; @@ -60,7 +59,7 @@ export const OverviewSideBar: FC = ({ createAnomalyDetectionJobDisabled }

@@ -79,14 +78,6 @@ export const OverviewSideBar: FC = ({ createAnomalyDetectionJobDisabled } /> ), - whatIsMachineLearning: ( - - - - ), }} />

@@ -96,7 +87,7 @@ export const OverviewSideBar: FC = ({ createAnomalyDetectionJobDisabled }

diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index 3fd228377c57e4..5a062320ca6c5c 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -107,7 +107,7 @@ export function drawLineChartDots(data, lineChartGroup, lineChartValuesLine, rad } // this replicates Kibana's filterAxisLabels() behavior -// which can be found in src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js +// which can be found in src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js // axis labels which overflow the chart's boundaries will be removed export function filterAxisLabels(selection, chartWidth) { if (selection === undefined || selection.selectAll === undefined) { diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index 5093801d2d1846..b6e95ae8373ee1 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -36,7 +36,7 @@ describe('check_capabilities', () => { ); const { capabilities } = await getCapabilities(); const count = Object.keys(capabilities).length; - expect(count).toBe(22); + expect(count).toBe(28); done(); }); }); @@ -49,28 +49,42 @@ describe('check_capabilities', () => { mlLicense, mlIsEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); expect(upgradeInProgress).toBe(false); expect(mlFeatureEnabledInSpace).toBe(true); + expect(isPlatinumOrTrialLicense).toBe(true); + + expect(capabilities.canAccessML).toBe(true); expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canGetAnnotations).toBe(true); + expect(capabilities.canCreateAnnotation).toBe(true); + expect(capabilities.canDeleteAnnotation).toBe(true); + expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); expect(capabilities.canOpenJob).toBe(false); expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canCreateDatafeed).toBe(false); + expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateFilter).toBe(false); expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); @@ -84,28 +98,42 @@ describe('check_capabilities', () => { mlLicense, mlIsEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); expect(upgradeInProgress).toBe(false); expect(mlFeatureEnabledInSpace).toBe(true); + expect(isPlatinumOrTrialLicense).toBe(true); + + expect(capabilities.canAccessML).toBe(true); expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canGetAnnotations).toBe(true); + expect(capabilities.canCreateAnnotation).toBe(true); + expect(capabilities.canDeleteAnnotation).toBe(true); + expect(capabilities.canCreateJob).toBe(true); expect(capabilities.canDeleteJob).toBe(true); expect(capabilities.canOpenJob).toBe(true); expect(capabilities.canCloseJob).toBe(true); expect(capabilities.canForecastJob).toBe(true); - expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canStartStopDatafeed).toBe(true); expect(capabilities.canUpdateJob).toBe(true); + expect(capabilities.canCreateDatafeed).toBe(true); + expect(capabilities.canDeleteDatafeed).toBe(true); expect(capabilities.canUpdateDatafeed).toBe(true); expect(capabilities.canPreviewDatafeed).toBe(true); - expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canCreateCalendar).toBe(true); expect(capabilities.canDeleteCalendar).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateFilter).toBe(true); expect(capabilities.canDeleteFilter).toBe(true); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); expect(capabilities.canCreateDataFrameAnalytics).toBe(true); expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); @@ -119,28 +147,42 @@ describe('check_capabilities', () => { mlLicense, mlIsEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); expect(upgradeInProgress).toBe(true); expect(mlFeatureEnabledInSpace).toBe(true); + expect(isPlatinumOrTrialLicense).toBe(true); + + expect(capabilities.canAccessML).toBe(true); expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canGetAnnotations).toBe(true); + expect(capabilities.canCreateAnnotation).toBe(false); + expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); expect(capabilities.canOpenJob).toBe(false); expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canCreateDatafeed).toBe(false); + expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateFilter).toBe(false); expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); @@ -154,28 +196,42 @@ describe('check_capabilities', () => { mlLicense, mlIsEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); expect(upgradeInProgress).toBe(true); expect(mlFeatureEnabledInSpace).toBe(true); + expect(isPlatinumOrTrialLicense).toBe(true); + + expect(capabilities.canAccessML).toBe(true); expect(capabilities.canGetJobs).toBe(true); + expect(capabilities.canGetDatafeeds).toBe(true); + expect(capabilities.canGetCalendars).toBe(true); + expect(capabilities.canFindFileStructure).toBe(true); + expect(capabilities.canGetFilters).toBe(true); + expect(capabilities.canGetDataFrameAnalytics).toBe(true); + expect(capabilities.canGetAnnotations).toBe(true); + expect(capabilities.canCreateAnnotation).toBe(false); + expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); expect(capabilities.canOpenJob).toBe(false); expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canCreateDatafeed).toBe(false); + expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateFilter).toBe(false); expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); @@ -189,28 +245,42 @@ describe('check_capabilities', () => { mlLicense, mlIsNotEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); expect(upgradeInProgress).toBe(false); expect(mlFeatureEnabledInSpace).toBe(false); + expect(isPlatinumOrTrialLicense).toBe(true); + + expect(capabilities.canAccessML).toBe(false); expect(capabilities.canGetJobs).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(false); + expect(capabilities.canGetCalendars).toBe(false); + expect(capabilities.canFindFileStructure).toBe(false); + expect(capabilities.canGetFilters).toBe(false); + expect(capabilities.canGetDataFrameAnalytics).toBe(false); + expect(capabilities.canGetAnnotations).toBe(false); + expect(capabilities.canCreateAnnotation).toBe(false); + expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); expect(capabilities.canOpenJob).toBe(false); expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canCreateDatafeed).toBe(false); + expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateFilter).toBe(false); expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); @@ -225,28 +295,43 @@ describe('check_capabilities', () => { mlLicenseBasic, mlIsNotEnabled ); - const { capabilities, upgradeInProgress, mlFeatureEnabledInSpace } = await getCapabilities(); + const { + capabilities, + upgradeInProgress, + mlFeatureEnabledInSpace, + isPlatinumOrTrialLicense, + } = await getCapabilities(); + expect(upgradeInProgress).toBe(false); expect(mlFeatureEnabledInSpace).toBe(false); + expect(isPlatinumOrTrialLicense).toBe(false); + + expect(capabilities.canAccessML).toBe(false); expect(capabilities.canGetJobs).toBe(false); + expect(capabilities.canGetDatafeeds).toBe(false); + expect(capabilities.canGetCalendars).toBe(false); + expect(capabilities.canFindFileStructure).toBe(false); + expect(capabilities.canGetFilters).toBe(false); + expect(capabilities.canGetDataFrameAnalytics).toBe(false); + expect(capabilities.canGetAnnotations).toBe(false); + expect(capabilities.canCreateAnnotation).toBe(false); + expect(capabilities.canDeleteAnnotation).toBe(false); + expect(capabilities.canCreateJob).toBe(false); expect(capabilities.canDeleteJob).toBe(false); expect(capabilities.canOpenJob).toBe(false); expect(capabilities.canCloseJob).toBe(false); expect(capabilities.canForecastJob).toBe(false); - expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canStartStopDatafeed).toBe(false); expect(capabilities.canUpdateJob).toBe(false); + expect(capabilities.canCreateDatafeed).toBe(false); + expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); - expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateFilter).toBe(false); expect(capabilities.canDeleteFilter).toBe(false); - expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts index a2ad83c5522de3..d955cf981faca6 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.ts @@ -44,4 +44,6 @@ function disableAdminPrivileges(capabilities: MlCapabilities) { Object.keys(adminMlCapabilities).forEach(k => { capabilities[k as keyof MlCapabilities] = false; }); + capabilities.canCreateAnnotation = false; + capabilities.canDeleteAnnotation = false; } 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 84f81a30f36b81..40b2a524151b39 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 @@ -29,7 +29,11 @@ import { JobSpecificOverride, isGeneralJobOverride, } from '../../../common/types/modules'; -import { getLatestDataOrBucketTimestamp, prefixDatafeedId } from '../../../common/util/job_utils'; +import { + getLatestDataOrBucketTimestamp, + prefixDatafeedId, + splitIndexPatternNames, +} from '../../../common/util/job_utils'; import { mlLog } from '../../client/log'; import { calculateModelMemoryLimitProvider } from '../calculate_model_memory_limit'; import { fieldsServiceProvider } from '../fields_service'; @@ -828,9 +832,7 @@ export class DataRecognizer { updateDatafeedIndices(moduleConfig: Module) { // if the supplied index pattern contains a comma, split into multiple indices and // add each one to the datafeed - const indexPatternNames = this.indexPatternName.includes(',') - ? this.indexPatternName.split(',').map(i => i.trim()) - : [this.indexPatternName]; + const indexPatternNames = splitIndexPatternNames(this.indexPatternName); moduleConfig.datafeeds.forEach(df => { const newIndices: string[] = []; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 64f8eb4b0acd3a..969b74194148b3 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -45,7 +45,7 @@ import { systemRoutes } from './routes/system'; import { MlLicense } from '../common/license'; import { MlServerLicense } from './lib/license'; import { createSharedServices, SharedServices } from './shared_services'; -import { userMlCapabilities, adminMlCapabilities } from '../common/types/capabilities'; +import { getPluginPrivileges } from '../common/types/capabilities'; import { setupCapabilitiesSwitcher } from './lib/capabilities'; import { registerKibanaSettings } from './lib/register_settings'; @@ -75,8 +75,7 @@ export class MlServerPlugin implements Plugin { try { @@ -86,6 +89,9 @@ export function annotationRoutes( validate: { body: indexAnnotationSchema, }, + options: { + tags: ['access:ml:canCreateAnnotation'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -130,6 +136,9 @@ export function annotationRoutes( validate: { params: deleteAnnotationSchema, }, + options: { + tags: ['access:ml:canDeleteAnnotation'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts index ca63d69f403f6b..63cd5498231af5 100644 --- a/x-pack/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/plugins/ml/server/routes/anomaly_detectors.ts @@ -37,6 +37,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/anomaly_detectors', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -65,6 +68,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: jobIdSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -93,6 +99,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/anomaly_detectors/_stats', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -121,6 +130,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: jobIdSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -154,6 +166,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: jobIdSchema, body: schema.object(anomalyDetectionJobSchema), }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -188,6 +203,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: jobIdSchema, body: anomalyDetectionUpdateJobSchema, }, + options: { + tags: ['access:ml:canUpdateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -220,6 +238,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: jobIdSchema, }, + options: { + tags: ['access:ml:canOpenJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -251,6 +272,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: jobIdSchema, }, + options: { + tags: ['access:ml:canCloseJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -286,6 +310,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: jobIdSchema, }, + options: { + tags: ['access:ml:canDeleteJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -319,6 +346,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { body: schema.any(), }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -351,6 +381,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: jobIdSchema, body: forecastAnomalyDetector, }, + options: { + tags: ['access:ml:canForecastJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -389,6 +422,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: jobIdSchema, body: getRecordsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -425,6 +461,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: getBucketParamsSchema, body: getBucketsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -462,6 +501,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { params: jobIdSchema, body: getOverallBucketsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -496,6 +538,9 @@ export function jobRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: getCategoriesSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/calendars.ts b/x-pack/plugins/ml/server/routes/calendars.ts index a17601f74ae93e..9c80651a13999e 100644 --- a/x-pack/plugins/ml/server/routes/calendars.ts +++ b/x-pack/plugins/ml/server/routes/calendars.ts @@ -52,6 +52,9 @@ export function calendars({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/calendars', validate: false, + options: { + tags: ['access:ml:canGetCalendars'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -81,6 +84,9 @@ export function calendars({ router, mlLicense }: RouteInitialization) { validate: { params: calendarIdsSchema, }, + options: { + tags: ['access:ml:canGetCalendars'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { let returnValue; @@ -117,6 +123,9 @@ export function calendars({ router, mlLicense }: RouteInitialization) { validate: { body: calendarSchema, }, + options: { + tags: ['access:ml:canCreateCalendar'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -149,6 +158,9 @@ export function calendars({ router, mlLicense }: RouteInitialization) { params: calendarIdSchema, body: calendarSchema, }, + options: { + tags: ['access:ml:canCreateCalendar'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -180,6 +192,9 @@ export function calendars({ router, mlLicense }: RouteInitialization) { validate: { params: calendarIdSchema, }, + options: { + tags: ['access:ml:canDeleteCalendar'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts index dd9e0ea66aa9dd..32cb2b343f8760 100644 --- a/x-pack/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/plugins/ml/server/routes/data_frame_analytics.ts @@ -33,6 +33,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat { path: '/api/ml/data_frame/analytics', validate: false, + options: { + tags: ['access:ml:canGetDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -61,6 +64,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { params: analyticsIdSchema, }, + options: { + tags: ['access:ml:canGetDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -88,6 +94,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat { path: '/api/ml/data_frame/analytics/_stats', validate: false, + options: { + tags: ['access:ml:canGetDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -118,6 +127,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { params: analyticsIdSchema, }, + options: { + tags: ['access:ml:canGetDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -155,6 +167,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat params: analyticsIdSchema, body: dataAnalyticsJobConfigSchema, }, + options: { + tags: ['access:ml:canCreateDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -190,6 +205,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { body: dataAnalyticsEvaluateSchema, }, + options: { + tags: ['access:ml:canCreateDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -224,6 +242,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { body: dataAnalyticsExplainSchema, }, + options: { + tags: ['access:ml:canCreateDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -257,6 +278,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { params: analyticsIdSchema, }, + options: { + tags: ['access:ml:canDeleteDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -291,6 +315,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { params: analyticsIdSchema, }, + options: { + tags: ['access:ml:canStartStopDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -324,6 +351,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat params: analyticsIdSchema, query: stopsDataFrameAnalyticsJobQuerySchema, }, + options: { + tags: ['access:ml:canStartStopDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -364,6 +394,9 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat validate: { params: analyticsIdSchema, }, + options: { + tags: ['access:ml:canGetDataFrameAnalytics'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/data_visualizer.ts b/x-pack/plugins/ml/server/routes/data_visualizer.ts index 20029fbd8d1a6b..04008a896a1a22 100644 --- a/x-pack/plugins/ml/server/routes/data_visualizer.ts +++ b/x-pack/plugins/ml/server/routes/data_visualizer.ts @@ -88,6 +88,9 @@ export function dataVisualizerRoutes({ router, mlLicense }: RouteInitialization) params: indexPatternTitleSchema, body: dataVisualizerFieldStatsSchema, }, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { @@ -150,6 +153,9 @@ export function dataVisualizerRoutes({ router, mlLicense }: RouteInitialization) params: indexPatternTitleSchema, body: dataVisualizerOverallStatsSchema, }, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/datafeeds.ts b/x-pack/plugins/ml/server/routes/datafeeds.ts index ec667e1d305f52..1fa1d408372da3 100644 --- a/x-pack/plugins/ml/server/routes/datafeeds.ts +++ b/x-pack/plugins/ml/server/routes/datafeeds.ts @@ -28,6 +28,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/datafeeds', validate: false, + options: { + tags: ['access:ml:canGetDatafeeds'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -57,6 +60,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: datafeedIdSchema, }, + options: { + tags: ['access:ml:canGetDatafeeds'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -83,6 +89,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/datafeeds/_stats', validate: false, + options: { + tags: ['access:ml:canGetDatafeeds'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -112,6 +121,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: datafeedIdSchema, }, + options: { + tags: ['access:ml:canGetDatafeeds'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -146,6 +158,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { params: datafeedIdSchema, body: datafeedConfigSchema, }, + options: { + tags: ['access:ml:canCreateDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -181,6 +196,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { params: datafeedIdSchema, body: datafeedConfigSchema, }, + options: { + tags: ['access:ml:canUpdateDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -216,6 +234,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { params: datafeedIdSchema, query: deleteDatafeedQuerySchema, }, + options: { + tags: ['access:ml:canDeleteDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -255,6 +276,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { params: datafeedIdSchema, body: startDatafeedSchema, }, + options: { + tags: ['access:ml:canStartStopDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -291,6 +315,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: datafeedIdSchema, }, + options: { + tags: ['access:ml:canStartStopDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -324,6 +351,9 @@ export function dataFeedRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: datafeedIdSchema, }, + options: { + tags: ['access:ml:canPreviewDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/fields_service.ts b/x-pack/plugins/ml/server/routes/fields_service.ts index 577e8e0161342a..b0f13df294145e 100644 --- a/x-pack/plugins/ml/server/routes/fields_service.ts +++ b/x-pack/plugins/ml/server/routes/fields_service.ts @@ -46,8 +46,10 @@ export function fieldsService({ router, mlLicense }: RouteInitialization) { validate: { body: getCardinalityOfFieldsSchema, }, + options: { + tags: ['access:ml:canAccessML'], + }, }, - mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { const resp = await getCardinalityOfFields(context, request.body); @@ -79,6 +81,9 @@ export function fieldsService({ router, mlLicense }: RouteInitialization) { validate: { body: getTimeFieldRangeSchema, }, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts index 3f3fc3f547b6a0..0f389f9505943b 100644 --- a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts +++ b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts @@ -71,6 +71,7 @@ export function fileDataVisualizerRoutes({ router, mlLicense }: RouteInitializat accepts: ['text/*', 'application/json'], maxBytes: MAX_FILE_SIZE_BYTES, }, + tags: ['access:ml:canFindFileStructure'], }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { @@ -105,6 +106,7 @@ export function fileDataVisualizerRoutes({ router, mlLicense }: RouteInitializat accepts: ['application/json'], maxBytes: MAX_FILE_SIZE_BYTES, }, + tags: ['access:ml:canFindFileStructure'], }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { diff --git a/x-pack/plugins/ml/server/routes/filters.ts b/x-pack/plugins/ml/server/routes/filters.ts index 738c25070358d1..d5287c349a8fca 100644 --- a/x-pack/plugins/ml/server/routes/filters.ts +++ b/x-pack/plugins/ml/server/routes/filters.ts @@ -57,6 +57,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/filters', validate: false, + options: { + tags: ['access:ml:canGetFilters'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -89,6 +92,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: filterIdSchema, }, + options: { + tags: ['access:ml:canGetFilters'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -120,6 +126,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { validate: { body: createFilterSchema, }, + options: { + tags: ['access:ml:canCreateFilter'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -155,6 +164,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { params: filterIdSchema, body: updateFilterSchema, }, + options: { + tags: ['access:ml:canCreateFilter'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -186,6 +198,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { validate: { params: filterIdSchema, }, + options: { + tags: ['access:ml:canDeleteFilter'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -216,6 +231,9 @@ export function filtersRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/filters/_stats', validate: false, + options: { + tags: ['access:ml:canGetFilters'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/indices.ts b/x-pack/plugins/ml/server/routes/indices.ts index e434936beba630..fb3ef7fc41c767 100644 --- a/x-pack/plugins/ml/server/routes/indices.ts +++ b/x-pack/plugins/ml/server/routes/indices.ts @@ -27,6 +27,9 @@ export function indicesRoutes({ router, mlLicense }: RouteInitialization) { validate: { body: indicesSchema, }, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/job_audit_messages.ts b/x-pack/plugins/ml/server/routes/job_audit_messages.ts index 1fe5a7af95d4f0..5acc89e7d13be7 100644 --- a/x-pack/plugins/ml/server/routes/job_audit_messages.ts +++ b/x-pack/plugins/ml/server/routes/job_audit_messages.ts @@ -33,6 +33,9 @@ export function jobAuditMessagesRoutes({ router, mlLicense }: RouteInitializatio params: jobAuditMessagesJobIdSchema, query: jobAuditMessagesQuerySchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -67,6 +70,9 @@ export function jobAuditMessagesRoutes({ router, mlLicense }: RouteInitializatio validate: { query: jobAuditMessagesQuerySchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index 149ca2591fd765..05c44e1da97575 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -4,11 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import Boom from 'boom'; import { schema } from '@kbn/config-schema'; -import { KibanaRequest } from 'kibana/server'; import { wrapError } from '../client/error_wrapper'; -import { RouteInitialization, JobServiceRouteDeps } from '../types'; +import { RouteInitialization } from '../types'; import { categorizationFieldExamplesSchema, chartSchema, @@ -27,19 +25,7 @@ import { categorizationExamplesProvider } from '../models/job_service/new_job'; /** * Routes for job service */ -export function jobServiceRoutes( - { router, mlLicense }: RouteInitialization, - { resolveMlCapabilities }: JobServiceRouteDeps -) { - async function hasPermissionToCreateJobs(request: KibanaRequest) { - const mlCapabilities = await resolveMlCapabilities(request); - if (mlCapabilities === null) { - throw new Error('resolveMlCapabilities is not defined'); - } - - return mlCapabilities.canCreateJob; - } - +export function jobServiceRoutes({ router, mlLicense }: RouteInitialization) { /** * @apiGroup JobService * @@ -55,6 +41,9 @@ export function jobServiceRoutes( validate: { body: forceStartDatafeedSchema, }, + options: { + tags: ['access:ml:canStartStopDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -86,6 +75,9 @@ export function jobServiceRoutes( validate: { body: datafeedIdsSchema, }, + options: { + tags: ['access:ml:canStartStopDatafeed'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -117,6 +109,9 @@ export function jobServiceRoutes( validate: { body: jobIdsSchema, }, + options: { + tags: ['access:ml:canDeleteJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -148,6 +143,9 @@ export function jobServiceRoutes( validate: { body: jobIdsSchema, }, + options: { + tags: ['access:ml:canCloseJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -184,6 +182,9 @@ export function jobServiceRoutes( validate: { body: jobIdsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -215,6 +216,9 @@ export function jobServiceRoutes( validate: { body: schema.object(jobsWithTimerangeSchema), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -245,6 +249,9 @@ export function jobServiceRoutes( validate: { body: jobIdsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -272,6 +279,9 @@ export function jobServiceRoutes( { path: '/api/ml/jobs/groups', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -302,6 +312,9 @@ export function jobServiceRoutes( validate: { body: schema.object(updateGroupsSchema), }, + options: { + tags: ['access:ml:canUpdateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -329,6 +342,9 @@ export function jobServiceRoutes( { path: '/api/ml/jobs/deleting_jobs_tasks', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -359,6 +375,9 @@ export function jobServiceRoutes( validate: { body: jobIdsSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -389,6 +408,9 @@ export function jobServiceRoutes( params: schema.object({ indexPattern: schema.string() }), query: schema.maybe(schema.object({ rollup: schema.maybe(schema.string()) })), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -422,6 +444,9 @@ export function jobServiceRoutes( validate: { body: schema.object(chartSchema), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -474,6 +499,9 @@ export function jobServiceRoutes( validate: { body: schema.object(chartSchema), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -522,6 +550,9 @@ export function jobServiceRoutes( { path: '/api/ml/jobs/all_jobs_and_group_ids', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -552,6 +583,9 @@ export function jobServiceRoutes( validate: { body: schema.object(lookBackProgressSchema), }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -583,17 +617,12 @@ export function jobServiceRoutes( validate: { body: schema.object(categorizationFieldExamplesSchema), }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { - // due to the use of the _analyze endpoint which is called by the kibana user, - // basic job creation privileges are required to use this endpoint - if ((await hasPermissionToCreateJobs(request)) === false) { - throw Boom.forbidden( - 'Insufficient privileges, the machine_learning_admin role is required.' - ); - } - const { validateCategoryExamples } = categorizationExamplesProvider( context.ml!.mlClient.callAsCurrentUser, context.ml!.mlClient.callAsInternalUser @@ -644,6 +673,9 @@ export function jobServiceRoutes( validate: { body: schema.object(topCategoriesSchema), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/job_validation.ts b/x-pack/plugins/ml/server/routes/job_validation.ts index dd2bd9deadf43f..632166d6d5fb8f 100644 --- a/x-pack/plugins/ml/server/routes/job_validation.ts +++ b/x-pack/plugins/ml/server/routes/job_validation.ts @@ -57,6 +57,9 @@ export function jobValidationRoutes({ router, mlLicense }: RouteInitialization, validate: { body: estimateBucketSpanSchema, }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -106,6 +109,9 @@ export function jobValidationRoutes({ router, mlLicense }: RouteInitialization, validate: { body: modelMemoryLimitSchema, }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -135,6 +141,9 @@ export function jobValidationRoutes({ router, mlLicense }: RouteInitialization, validate: { body: validateCardinalitySchema, }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -167,6 +176,9 @@ export function jobValidationRoutes({ router, mlLicense }: RouteInitialization, validate: { body: validateJobSchema, }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/modules.ts b/x-pack/plugins/ml/server/routes/modules.ts index 2891144fc4574a..622ae66ede4264 100644 --- a/x-pack/plugins/ml/server/routes/modules.ts +++ b/x-pack/plugins/ml/server/routes/modules.ts @@ -97,6 +97,9 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { indexPatternTitle: schema.string(), }), }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -127,6 +130,9 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { ...getModuleIdParamSchema(true), }), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -161,6 +167,9 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { params: schema.object(getModuleIdParamSchema()), body: setupModuleBodySchema, }, + options: { + tags: ['access:ml:canCreateJob'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -218,6 +227,9 @@ export function dataRecognizer({ router, mlLicense }: RouteInitialization) { validate: { params: schema.object(getModuleIdParamSchema()), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/notification_settings.ts b/x-pack/plugins/ml/server/routes/notification_settings.ts index 59458b1e486db2..e4a9abb0784beb 100644 --- a/x-pack/plugins/ml/server/routes/notification_settings.ts +++ b/x-pack/plugins/ml/server/routes/notification_settings.ts @@ -22,6 +22,9 @@ export function notificationRoutes({ router, mlLicense }: RouteInitialization) { { path: '/api/ml/notification_settings', validate: false, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/results_service.ts b/x-pack/plugins/ml/server/routes/results_service.ts index 89c267340fe52f..94ca0827ccfa59 100644 --- a/x-pack/plugins/ml/server/routes/results_service.ts +++ b/x-pack/plugins/ml/server/routes/results_service.ts @@ -88,6 +88,9 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization) validate: { body: anomaliesTableDataSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -117,6 +120,9 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization) validate: { body: categoryDefinitionSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -146,6 +152,9 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization) validate: { body: maxAnomalyScoreSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -175,6 +184,9 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization) validate: { body: categoryExamplesSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { @@ -204,6 +216,9 @@ export function resultsServiceRoutes({ router, mlLicense }: RouteInitialization) validate: { body: partitionFieldValuesSchema, }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/routes/system.ts b/x-pack/plugins/ml/server/routes/system.ts index d5fe45728c56c7..7ae7dd8eef0653 100644 --- a/x-pack/plugins/ml/server/routes/system.ts +++ b/x-pack/plugins/ml/server/routes/system.ts @@ -54,6 +54,9 @@ export function systemRoutes( validate: { body: schema.maybe(schema.any()), }, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { @@ -110,6 +113,9 @@ export function systemRoutes( { path: '/api/ml/ml_capabilities', validate: false, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { @@ -150,7 +156,11 @@ export function systemRoutes( { path: '/api/ml/ml_node_count', validate: false, + options: { + tags: ['access:ml:canGetJobs'], + }, }, + mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { // check for basic license first for consistency with other @@ -201,6 +211,9 @@ export function systemRoutes( { path: '/api/ml/info', validate: false, + options: { + tags: ['access:ml:canAccessML'], + }, }, mlLicense.basicLicenseAPIGuard(async (context, request, response) => { try { @@ -229,6 +242,9 @@ export function systemRoutes( validate: { body: schema.maybe(schema.any()), }, + options: { + tags: ['access:ml:canGetJobs'], + }, }, mlLicense.fullLicenseAPIGuard(async (context, request, response) => { try { diff --git a/x-pack/plugins/ml/server/types.ts b/x-pack/plugins/ml/server/types.ts index d4cd61a7fa4f71..678e81d3526ac8 100644 --- a/x-pack/plugins/ml/server/types.ts +++ b/x-pack/plugins/ml/server/types.ts @@ -30,10 +30,6 @@ export interface SystemRouteDeps { resolveMlCapabilities: ResolveMlCapabilities; } -export interface JobServiceRouteDeps { - resolveMlCapabilities: ResolveMlCapabilities; -} - export interface PluginsSetup { cloud: CloudSetup; features: FeaturesPluginSetup; diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts index edd6142455dfb6..eeed7b4d5acf61 100644 --- a/x-pack/plugins/monitoring/common/constants.ts +++ b/x-pack/plugins/monitoring/common/constants.ts @@ -245,7 +245,7 @@ export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION, ALERT_TYPE_CLUSTER_ST /** * Matches the id for the built-in in email action type - * See x-pack/legacy/plugins/actions/server/builtin_action_types/email.ts + * See x-pack/plugins/actions/server/builtin_action_types/email.ts */ export const ALERT_ACTION_TYPE_EMAIL = '.email'; diff --git a/x-pack/legacy/plugins/remote_clusters/public/index.scss b/x-pack/plugins/remote_clusters/public/application/_hacks.scss similarity index 82% rename from x-pack/legacy/plugins/remote_clusters/public/index.scss rename to x-pack/plugins/remote_clusters/public/application/_hacks.scss index 4ae11323642d8a..b7d81885e716d0 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/index.scss +++ b/x-pack/plugins/remote_clusters/public/application/_hacks.scss @@ -1,7 +1,4 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - -// Remote clusters plugin styles +// Remote clusters plugin hacks // Prefix all styles with "remoteClusters" to avoid conflicts. // Examples diff --git a/x-pack/plugins/remote_clusters/public/application/index.js b/x-pack/plugins/remote_clusters/public/application/index.js index f2d788c7413428..cf6e855ba58dfa 100644 --- a/x-pack/plugins/remote_clusters/public/application/index.js +++ b/x-pack/plugins/remote_clusters/public/application/index.js @@ -13,6 +13,8 @@ import { App } from './app'; import { remoteClustersStore } from './store'; import { AppContextProvider } from './app_context'; +import './_hacks.scss'; + export const renderApp = (elem, I18nContext, appDependencies) => { render( diff --git a/x-pack/plugins/security/common/licensing/license_features.ts b/x-pack/plugins/security/common/licensing/license_features.ts index 5184ab0e962bd2..571d2630b2b177 100644 --- a/x-pack/plugins/security/common/licensing/license_features.ts +++ b/x-pack/plugins/security/common/licensing/license_features.ts @@ -33,6 +33,11 @@ export interface SecurityLicenseFeatures { */ readonly showRoleMappingsManagement: boolean; + /** + * Indicates whether we allow users to access agreement UI and acknowledge it. + */ + readonly allowAccessAgreement: boolean; + /** * Indicates whether we allow users to define document level security in roles. */ diff --git a/x-pack/plugins/security/common/licensing/license_service.test.ts b/x-pack/plugins/security/common/licensing/license_service.test.ts index 5bdfa7d4886aac..9dec665614635f 100644 --- a/x-pack/plugins/security/common/licensing/license_service.test.ts +++ b/x-pack/plugins/security/common/licensing/license_service.test.ts @@ -18,6 +18,7 @@ describe('license features', function() { allowLogin: false, showLinks: false, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, layout: 'error-es-unavailable', @@ -37,6 +38,7 @@ describe('license features', function() { allowLogin: false, showLinks: false, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, layout: 'error-xpack-unavailable', @@ -60,6 +62,7 @@ describe('license features', function() { expect(subscriptionHandler.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { + "allowAccessAgreement": false, "allowLogin": false, "allowRbac": false, "allowRoleDocumentLevelSecurity": false, @@ -78,6 +81,7 @@ describe('license features', function() { expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(` Array [ Object { + "allowAccessAgreement": true, "allowLogin": true, "allowRbac": true, "allowRoleDocumentLevelSecurity": true, @@ -94,7 +98,7 @@ describe('license features', function() { } }); - it('should show login page and other security elements, allow RBAC but forbid role mappings, DLS, and sub-feature privileges if license is basic.', () => { + it('should show login page and other security elements, allow RBAC but forbid paid features if license is basic.', () => { const mockRawLicense = licensingMock.createLicense({ features: { security: { isEnabled: true, isAvailable: true } }, }); @@ -109,6 +113,7 @@ describe('license features', function() { allowLogin: true, showLinks: true, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, allowRbac: true, @@ -131,6 +136,7 @@ describe('license features', function() { allowLogin: false, showLinks: false, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, allowRbac: false, @@ -138,7 +144,7 @@ describe('license features', function() { }); }); - it('should allow role mappings and sub-feature privileges, but not DLS/FLS if license = gold', () => { + it('should allow role mappings, access agreement and sub-feature privileges, but not DLS/FLS if license = gold', () => { const mockRawLicense = licensingMock.createLicense({ license: { mode: 'gold', type: 'gold' }, features: { security: { isEnabled: true, isAvailable: true } }, @@ -152,6 +158,7 @@ describe('license features', function() { allowLogin: true, showLinks: true, showRoleMappingsManagement: true, + allowAccessAgreement: true, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, allowRbac: true, @@ -159,7 +166,7 @@ describe('license features', function() { }); }); - it('should allow to login, allow RBAC, role mappings, sub-feature privileges, and DLS if license >= platinum', () => { + it('should allow to login, allow RBAC, role mappings, access agreement, sub-feature privileges, and DLS if license >= platinum', () => { const mockRawLicense = licensingMock.createLicense({ license: { mode: 'platinum', type: 'platinum' }, features: { security: { isEnabled: true, isAvailable: true } }, @@ -173,6 +180,7 @@ describe('license features', function() { allowLogin: true, showLinks: true, showRoleMappingsManagement: true, + allowAccessAgreement: true, allowRoleDocumentLevelSecurity: true, allowRoleFieldLevelSecurity: true, allowRbac: true, diff --git a/x-pack/plugins/security/common/licensing/license_service.ts b/x-pack/plugins/security/common/licensing/license_service.ts index 34bc44b88e40d9..7815798d6a9f3d 100644 --- a/x-pack/plugins/security/common/licensing/license_service.ts +++ b/x-pack/plugins/security/common/licensing/license_service.ts @@ -71,6 +71,7 @@ export class SecurityLicenseService { allowLogin: false, showLinks: false, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, allowRbac: false, @@ -88,6 +89,7 @@ export class SecurityLicenseService { allowLogin: false, showLinks: false, showRoleMappingsManagement: false, + allowAccessAgreement: false, allowRoleDocumentLevelSecurity: false, allowRoleFieldLevelSecurity: false, allowRbac: false, @@ -102,6 +104,7 @@ export class SecurityLicenseService { allowLogin: true, showLinks: true, showRoleMappingsManagement: isLicenseGoldOrBetter, + allowAccessAgreement: isLicenseGoldOrBetter, allowSubFeaturePrivileges: isLicenseGoldOrBetter, // Only platinum and trial licenses are compliant with field- and document-level security. allowRoleDocumentLevelSecurity: isLicensePlatinumOrBetter, diff --git a/x-pack/plugins/security/common/login_state.ts b/x-pack/plugins/security/common/login_state.ts index 4342e82d2f90b1..fd2b1cb8d1cf7e 100644 --- a/x-pack/plugins/security/common/login_state.ts +++ b/x-pack/plugins/security/common/login_state.ts @@ -6,15 +6,24 @@ import { LoginLayout } from './licensing'; +export interface LoginSelectorProvider { + type: string; + name: string; + usesLoginForm: boolean; + description?: string; + hint?: string; + icon?: string; +} + export interface LoginSelector { enabled: boolean; - providers: Array<{ type: string; name: string; description?: string }>; + providers: LoginSelectorProvider[]; } export interface LoginState { layout: LoginLayout; allowLogin: boolean; - showLoginForm: boolean; requiresSecureConnection: boolean; + loginHelp?: string; selector: LoginSelector; } diff --git a/x-pack/plugins/security/public/types.ts b/x-pack/plugins/security/common/types.ts similarity index 65% rename from x-pack/plugins/security/public/types.ts rename to x-pack/plugins/security/common/types.ts index e9c4b6e281cf3e..c668c6ccf71d16 100644 --- a/x-pack/plugins/security/public/types.ts +++ b/x-pack/plugins/security/common/types.ts @@ -4,9 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +/** + * Type and name tuple to identify provider used to authenticate user. + */ +export interface AuthenticationProvider { + type: string; + name: string; +} + export interface SessionInfo { now: number; idleTimeoutExpiration: number | null; lifespanExpiration: number | null; - provider: string; + provider: AuthenticationProvider; } diff --git a/x-pack/plugins/security/public/account_management/account_management_app.ts b/x-pack/plugins/security/public/account_management/account_management_app.ts index cd3ef34858b19c..41567a04fe0302 100644 --- a/x-pack/plugins/security/public/account_management/account_management_app.ts +++ b/x-pack/plugins/security/public/account_management/account_management_app.ts @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import { StartServicesAccessor, ApplicationSetup, AppMountParameters } from 'src/core/public'; import { AuthenticationServiceSetup } from '../authentication'; -import { UserAPIClient } from '../management'; interface CreateDeps { application: ApplicationSetup; @@ -28,9 +27,14 @@ export const accountManagementApp = Object.freeze({ navLinkStatus: 3, appRoute: '/security/account', async mount({ element }: AppMountParameters) { - const [[coreStart], { renderAccountManagementPage }] = await Promise.all([ + const [ + [coreStart], + { renderAccountManagementPage }, + { UserAPIClient }, + ] = await Promise.all([ getStartServices(), import('./account_management_page'), + import('../management'), ]); coreStart.chrome.setBreadcrumbs([{ text: title }]); diff --git a/x-pack/plugins/security/public/authentication/_index.scss b/x-pack/plugins/security/public/authentication/_index.scss deleted file mode 100644 index 0a423c00f0218a..00000000000000 --- a/x-pack/plugins/security/public/authentication/_index.scss +++ /dev/null @@ -1,5 +0,0 @@ -// Component styles -@import './components/index'; - -// Login styles -@import './login/index'; diff --git a/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap new file mode 100644 index 00000000000000..2227cbe8a495cf --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/__snapshots__/access_agreement_page.test.tsx.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AccessAgreementPage renders as expected when state is available 1`] = ` + +

+

+ This is + + link + +

+
+ +`; diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts new file mode 100644 index 00000000000000..add2db6a3c170d --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('./access_agreement_page'); + +import { AppMount, ScopedHistory } from 'src/core/public'; +import { accessAgreementApp } from './access_agreement_app'; + +import { coreMock, scopedHistoryMock } from '../../../../../../src/core/public/mocks'; + +describe('accessAgreementApp', () => { + it('properly registers application', () => { + const coreSetupMock = coreMock.createSetup(); + + accessAgreementApp.create({ + application: coreSetupMock.application, + getStartServices: coreSetupMock.getStartServices, + }); + + expect(coreSetupMock.application.register).toHaveBeenCalledTimes(1); + + const [[appRegistration]] = coreSetupMock.application.register.mock.calls; + expect(appRegistration).toEqual({ + id: 'security_access_agreement', + chromeless: true, + appRoute: '/security/access_agreement', + title: 'Access Agreement', + mount: expect.any(Function), + }); + }); + + it('properly renders application', async () => { + const coreSetupMock = coreMock.createSetup(); + const coreStartMock = coreMock.createStart(); + coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); + const containerMock = document.createElement('div'); + + accessAgreementApp.create({ + application: coreSetupMock.application, + getStartServices: coreSetupMock.getStartServices, + }); + + const [[{ mount }]] = coreSetupMock.application.register.mock.calls; + await (mount as AppMount)({ + element: containerMock, + appBasePath: '', + onAppLeave: jest.fn(), + history: (scopedHistoryMock.create() as unknown) as ScopedHistory, + }); + + const mockRenderApp = jest.requireMock('./access_agreement_page').renderAccessAgreementPage; + expect(mockRenderApp).toHaveBeenCalledTimes(1); + expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, { + http: coreStartMock.http, + notifications: coreStartMock.notifications, + fatalErrors: coreStartMock.fatalErrors, + }); + }); +}); diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.ts new file mode 100644 index 00000000000000..156a76542a28fb --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_app.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 { i18n } from '@kbn/i18n'; +import { StartServicesAccessor, ApplicationSetup, AppMountParameters } from 'src/core/public'; + +interface CreateDeps { + application: ApplicationSetup; + getStartServices: StartServicesAccessor; +} + +export const accessAgreementApp = Object.freeze({ + id: 'security_access_agreement', + create({ application, getStartServices }: CreateDeps) { + application.register({ + id: this.id, + title: i18n.translate('xpack.security.accessAgreementAppTitle', { + defaultMessage: 'Access Agreement', + }), + chromeless: true, + appRoute: '/security/access_agreement', + async mount({ element }: AppMountParameters) { + const [[coreStart], { renderAccessAgreementPage }] = await Promise.all([ + getStartServices(), + import('./access_agreement_page'), + ]); + return renderAccessAgreementPage(coreStart.i18n, element, { + http: coreStart.http, + notifications: coreStart.notifications, + fatalErrors: coreStart.fatalErrors, + }); + }, + }); + }, +}); diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.scss b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.scss new file mode 100644 index 00000000000000..08e7be248619f0 --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.scss @@ -0,0 +1,21 @@ +.secAccessAgreementPage .secAuthenticationStatePage__content { + max-width: 600px; +} + +.secAccessAgreementPage__textWrapper { + overflow-y: hidden; +} + +.secAccessAgreementPage__text { + @include euiYScrollWithShadows; + max-height: 400px; + padding: $euiSize $euiSizeL 0; +} + +.secAccessAgreementPage__footer { + padding: $euiSize $euiSizeL $euiSizeL; +} + +.secAccessAgreementPage__footerInner { + text-align: left; +} diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.test.tsx b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.test.tsx new file mode 100644 index 00000000000000..89b7489d45ebb2 --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.test.tsx @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 ReactMarkdown from 'react-markdown'; +import { EuiLoadingContent } from '@elastic/eui'; +import { act } from '@testing-library/react'; +import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; +import { findTestSubject } from 'test_utils/find_test_subject'; +import { coreMock } from '../../../../../../src/core/public/mocks'; +import { AccessAgreementPage } from './access_agreement_page'; + +describe('AccessAgreementPage', () => { + beforeAll(() => { + Object.defineProperty(window, 'location', { + value: { href: 'http://some-host/bar', protocol: 'http' }, + writable: true, + }); + }); + + afterAll(() => { + delete (window as any).location; + }); + + it('renders as expected when state is available', async () => { + const coreStartMock = coreMock.createStart(); + coreStartMock.http.get.mockResolvedValue({ accessAgreement: 'This is [link](../link)' }); + + const wrapper = mountWithIntl( + + ); + + expect(wrapper.exists(EuiLoadingContent)).toBe(true); + expect(wrapper.exists(ReactMarkdown)).toBe(false); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find(ReactMarkdown)).toMatchSnapshot(); + expect(wrapper.exists(EuiLoadingContent)).toBe(false); + + expect(coreStartMock.http.get).toHaveBeenCalledTimes(1); + expect(coreStartMock.http.get).toHaveBeenCalledWith( + '/internal/security/access_agreement/state' + ); + expect(coreStartMock.fatalErrors.add).not.toHaveBeenCalled(); + }); + + it('fails when state is not available', async () => { + const coreStartMock = coreMock.createStart(); + const error = Symbol(); + coreStartMock.http.get.mockRejectedValue(error); + + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(coreStartMock.http.get).toHaveBeenCalledTimes(1); + expect(coreStartMock.http.get).toHaveBeenCalledWith( + '/internal/security/access_agreement/state' + ); + expect(coreStartMock.fatalErrors.add).toHaveBeenCalledTimes(1); + expect(coreStartMock.fatalErrors.add).toHaveBeenCalledWith(error); + }); + + it('properly redirects after successful acknowledgement', async () => { + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + coreStartMock.http.get.mockResolvedValue({ accessAgreement: 'This is [link](../link)' }); + coreStartMock.http.post.mockResolvedValue(undefined); + + window.location.href = `https://some-host/security/access_agreement?next=${encodeURIComponent( + '/some-base-path/app/kibana#/home?_g=()' + )}`; + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + findTestSubject(wrapper, 'accessAgreementAcknowledge').simulate('click'); + + await act(async () => { + await nextTick(); + }); + + expect(coreStartMock.http.post).toHaveBeenCalledTimes(1); + expect(coreStartMock.http.post).toHaveBeenCalledWith( + '/internal/security/access_agreement/acknowledge' + ); + + expect(window.location.href).toBe('/some-base-path/app/kibana#/home?_g=()'); + expect(coreStartMock.notifications.toasts.addError).not.toHaveBeenCalled(); + }); + + it('shows error toast if acknowledgement fails', async () => { + const currentURL = `https://some-host/login?next=${encodeURIComponent( + '/some-base-path/app/kibana#/home?_g=()' + )}`; + + const failureReason = new Error('Oh no!'); + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + coreStartMock.http.get.mockResolvedValue({ accessAgreement: 'This is [link](../link)' }); + coreStartMock.http.post.mockRejectedValue(failureReason); + + window.location.href = currentURL; + const wrapper = mountWithIntl( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + findTestSubject(wrapper, 'accessAgreementAcknowledge').simulate('click'); + + await act(async () => { + await nextTick(); + }); + + expect(coreStartMock.http.post).toHaveBeenCalledTimes(1); + expect(coreStartMock.http.post).toHaveBeenCalledWith( + '/internal/security/access_agreement/acknowledge' + ); + + expect(window.location.href).toBe(currentURL); + expect(coreStartMock.notifications.toasts.addError).toHaveBeenCalledWith(failureReason, { + title: 'Could not acknowledge access agreement.', + }); + }); +}); diff --git a/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx new file mode 100644 index 00000000000000..a34dcb18d2b9cc --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/access_agreement_page.tsx @@ -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 './access_agreement_page.scss'; + +import React, { FormEvent, MouseEvent, useCallback, useEffect, useState } from 'react'; +import ReactDOM from 'react-dom'; +import ReactMarkdown from 'react-markdown'; +import { + EuiButton, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingContent, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CoreStart, FatalErrorsStart, HttpStart, NotificationsStart } from 'src/core/public'; + +import { parseNext } from '../../../common/parse_next'; +import { AuthenticationStatePage } from '../components'; + +interface Props { + http: HttpStart; + notifications: NotificationsStart; + fatalErrors: FatalErrorsStart; +} + +export function AccessAgreementPage({ http, fatalErrors, notifications }: Props) { + const [isLoading, setIsLoading] = useState(false); + + const [accessAgreement, setAccessAgreement] = useState(null); + useEffect(() => { + http + .get<{ accessAgreement: string }>('/internal/security/access_agreement/state') + .then(response => setAccessAgreement(response.accessAgreement)) + .catch(err => fatalErrors.add(err)); + }, [http, fatalErrors]); + + const onAcknowledge = useCallback( + async (e: MouseEvent | FormEvent) => { + e.preventDefault(); + + try { + setIsLoading(true); + await http.post('/internal/security/access_agreement/acknowledge'); + window.location.href = parseNext(window.location.href, http.basePath.serverBasePath); + } catch (err) { + notifications.toasts.addError(err, { + title: i18n.translate('xpack.security.accessAgreement.acknowledgeErrorMessage', { + defaultMessage: 'Could not acknowledge access agreement.', + }), + }); + + setIsLoading(false); + } + }, + [http, notifications] + ); + + const content = accessAgreement ? ( +
+ + + +
+ + {accessAgreement} + +
+
+ +
+ + + +
+
+
+
+
+ ) : ( + + + + ); + + return ( + + } + > + {content} + + + ); +} + +export function renderAccessAgreementPage( + i18nStart: CoreStart['i18n'], + element: Element, + props: Props +) { + ReactDOM.render( + + + , + element + ); + + return () => ReactDOM.unmountComponentAtNode(element); +} diff --git a/x-pack/plugins/security/public/authentication/access_agreement/index.ts b/x-pack/plugins/security/public/authentication/access_agreement/index.ts new file mode 100644 index 00000000000000..8f7661a89a269d --- /dev/null +++ b/x-pack/plugins/security/public/authentication/access_agreement/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 { accessAgreementApp } from './access_agreement_app'; diff --git a/x-pack/plugins/security/public/authentication/authentication_service.ts b/x-pack/plugins/security/public/authentication/authentication_service.ts index 2e73b8cd044826..6657f5c0a900cf 100644 --- a/x-pack/plugins/security/public/authentication/authentication_service.ts +++ b/x-pack/plugins/security/public/authentication/authentication_service.ts @@ -8,6 +8,7 @@ import { ApplicationSetup, StartServicesAccessor, HttpSetup } from 'src/core/pub import { AuthenticatedUser } from '../../common/model'; import { ConfigType } from '../config'; import { PluginStartDependencies } from '../plugin'; +import { accessAgreementApp } from './access_agreement'; import { loginApp } from './login'; import { logoutApp } from './logout'; import { loggedOutApp } from './logged_out'; @@ -46,6 +47,7 @@ export class AuthenticationService { ((await http.get('/internal/security/api_key/_enabled')) as { apiKeysEnabled: boolean }) .apiKeysEnabled; + accessAgreementApp.create({ application, getStartServices }); loginApp.create({ application, config, getStartServices, http }); logoutApp.create({ application, http }); loggedOutApp.create({ application, getStartServices, http }); diff --git a/x-pack/plugins/security/public/authentication/components/_index.scss b/x-pack/plugins/security/public/authentication/components/_index.scss deleted file mode 100644 index dfa258d523c5a4..00000000000000 --- a/x-pack/plugins/security/public/authentication/components/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './authentication_state_page/index'; diff --git a/x-pack/plugins/security/public/authentication/components/authentication_state_page/__snapshots__/authentication_state_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/components/authentication_state_page/__snapshots__/authentication_state_page.test.tsx.snap index 3590fa460a4010..585dc368da7077 100644 --- a/x-pack/plugins/security/public/authentication/components/authentication_state_page/__snapshots__/authentication_state_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/components/authentication_state_page/__snapshots__/authentication_state_page.test.tsx.snap @@ -2,7 +2,7 @@ exports[`AuthenticationStatePage renders 1`] = `
{ ) ).toMatchSnapshot(); }); + + it('renders with custom CSS class', () => { + expect( + shallowWithIntl( + + hello world + + ).exists('.secAuthenticationStatePage.customClassName') + ).toBe(true); + }); }); diff --git a/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx b/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx index aa306611299783..35be650d127fb7 100644 --- a/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx +++ b/x-pack/plugins/security/public/authentication/components/authentication_state_page/authentication_state_page.tsx @@ -4,20 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ +import './authentication_state_page.scss'; + import { EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; interface Props { + className?: string; title: React.ReactNode; } export const AuthenticationStatePage: React.FC = props => ( -
+
- +

{props.title}

diff --git a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap index ecbdfedac1dd38..bbc6bfa1faddc7 100644 --- a/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/__snapshots__/login_page.test.tsx.snap @@ -121,10 +121,15 @@ exports[`LoginPage enabled form state renders as expected 1`] = ` selector={ Object { "enabled": false, - "providers": Array [], + "providers": Array [ + Object { + "name": "basic1", + "type": "basic", + "usesLoginForm": true, + }, + ], } } - showLoginForm={true} /> `; @@ -155,10 +160,15 @@ exports[`LoginPage enabled form state renders as expected when info message is s selector={ Object { "enabled": false, - "providers": Array [], + "providers": Array [ + Object { + "name": "basic1", + "type": "basic", + "usesLoginForm": true, + }, + ], } } - showLoginForm={true} /> `; @@ -189,10 +199,55 @@ exports[`LoginPage enabled form state renders as expected when loginAssistanceMe selector={ Object { "enabled": false, - "providers": Array [], + "providers": Array [ + Object { + "name": "basic1", + "type": "basic", + "usesLoginForm": true, + }, + ], + } + } +/> +`; + +exports[`LoginPage enabled form state renders as expected when loginHelp is set 1`] = ` + `; @@ -279,10 +334,15 @@ exports[`LoginPage page renders as expected 1`] = ` selector={ Object { "enabled": false, - "providers": Array [], + "providers": Array [ + Object { + "name": "basic1", + "type": "basic", + "usesLoginForm": true, + }, + ], } } - showLoginForm={true} /> diff --git a/x-pack/plugins/security/public/authentication/login/_index.scss b/x-pack/plugins/security/public/authentication/login/_index.scss deleted file mode 100644 index 4dd2c0cabfb5e8..00000000000000 --- a/x-pack/plugins/security/public/authentication/login/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './login_page'; diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap b/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap index 7b8283b7bec0e6..072a025aa06a07 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/__snapshots__/login_form.test.tsx.snap @@ -1,170 +1,91 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`LoginForm login selector renders as expected with login form 1`] = ` - - - Login w/SAML - - - - Login w/PKI - - - login help and back: Login Help 1`] = ` + +
- ―――   - -   ――― - - - -
- - } - labelType="label" + - - - - } - labelType="label" - > - - - - - - -
- + some help + +

+
+
`; -exports[`LoginForm login selector renders as expected without login form for providers with and without description 1`] = ` - - +
- Login w/SAML - - - + + some help + +

+
+ +`; + +exports[`LoginForm properly switches to login help: Login Help 1`] = ` + +
- - - - +

+ + some help + +

+
+
`; exports[`LoginForm renders as expected 1`] = ` - +
@@ -227,21 +148,32 @@ exports[`LoginForm renders as expected 1`] = ` value="" /> - + - - + + + + + +
diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss new file mode 100644 index 00000000000000..6784052ef4337e --- /dev/null +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.scss @@ -0,0 +1,55 @@ +.secLoginCard { + display: block; + box-shadow: none; + padding: $euiSize; + text-align: left; + width: 100%; + + &:hover { + .secLoginCard__title { + text-decoration: underline; + } + } + + &:disabled { + pointer-events: none; + } + + &:not(.secLoginCard-isLoading):disabled { + .secLoginCard__title, + .secLoginCard__hint { + color: $euiColorMediumShade; + } + } + + &:focus { + border-color: transparent; + border-radius: $euiBorderRadius; + @include euiFocusRing; + + .secLoginCard__title { + text-decoration: underline; + } + + // Make the focus ring clean and without borders + + .secLoginCard { + border-color: transparent; + } + } + + + .secLoginCard { + border-top: $euiBorderThin; + } +} + +.secLoginCard__hint { + @include euiFontSizeXS; + color: $euiColorDarkShade; + margin-top: $euiSizeXS; +} + +.secLoginAssistanceMessage { + // This tightens up the layout if message is present + margin-top: -($euiSizeXXL + $euiSizeS); + padding: 0 $euiSize; +} diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.test.tsx b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.test.tsx index c17c10a2c51484..4e172cdde0eeda 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.test.tsx +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.test.tsx @@ -5,12 +5,39 @@ */ import React from 'react'; +import ReactMarkdown from 'react-markdown'; import { act } from '@testing-library/react'; -import { EuiButton, EuiCallOut } from '@elastic/eui'; +import { EuiButton, EuiCallOut, EuiIcon } from '@elastic/eui'; import { mountWithIntl, nextTick, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { LoginForm } from './login_form'; +import { findTestSubject } from 'test_utils/find_test_subject'; +import { LoginForm, PageMode } from './login_form'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { ReactWrapper } from 'enzyme'; + +function expectPageMode(wrapper: ReactWrapper, mode: PageMode) { + const assertions: Array<[string, boolean]> = + mode === PageMode.Form + ? [ + ['loginForm', true], + ['loginSelector', false], + ['loginHelp', false], + ] + : mode === PageMode.Selector + ? [ + ['loginForm', false], + ['loginSelector', true], + ['loginHelp', false], + ] + : [ + ['loginForm', false], + ['loginSelector', false], + ['loginHelp', true], + ]; + for (const [selector, exists] of assertions) { + expect(findTestSubject(wrapper, selector).exists()).toBe(exists); + } +} describe('LoginForm', () => { beforeAll(() => { @@ -32,8 +59,10 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} - selector={{ enabled: false, providers: [] }} + selector={{ + enabled: false, + providers: [{ type: 'basic', name: 'basic', usesLoginForm: true }], + }} /> ) ).toMatchSnapshot(); @@ -41,20 +70,44 @@ describe('LoginForm', () => { it('renders an info message when provided.', () => { const coreStartMock = coreMock.createStart(); - const wrapper = shallowWithIntl( + const wrapper = mountWithIntl( ); + expectPageMode(wrapper, PageMode.Form); + expect(wrapper.find(EuiCallOut).props().title).toEqual('Hey this is an info message'); }); + it('renders `Need help?` link if login help text is provided.', () => { + const coreStartMock = coreMock.createStart(); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Form); + + expect(findTestSubject(wrapper, 'loginHelpLink').text()).toEqual('Need help?'); + }); + it('renders an invalid credentials message', async () => { const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); coreStartMock.http.post.mockRejectedValue({ response: { status: 401 } }); @@ -64,11 +117,15 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} - selector={{ enabled: false, providers: [] }} + selector={{ + enabled: false, + providers: [{ type: 'basic', name: 'basic', usesLoginForm: true }], + }} /> ); + expectPageMode(wrapper, PageMode.Form); + wrapper.find('input[name="username"]').simulate('change', { target: { value: 'username' } }); wrapper.find('input[name="password"]').simulate('change', { target: { value: 'password' } }); wrapper.find(EuiButton).simulate('click'); @@ -92,11 +149,15 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} - selector={{ enabled: false, providers: [] }} + selector={{ + enabled: false, + providers: [{ type: 'basic', name: 'basic', usesLoginForm: true }], + }} /> ); + expectPageMode(wrapper, PageMode.Form); + wrapper.find('input[name="username"]').simulate('change', { target: { value: 'username' } }); wrapper.find('input[name="password"]').simulate('change', { target: { value: 'password' } }); wrapper.find(EuiButton).simulate('click'); @@ -121,11 +182,15 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} - selector={{ enabled: false, providers: [] }} + selector={{ + enabled: false, + providers: [{ type: 'basic', name: 'basic', usesLoginForm: true }], + }} /> ); + expectPageMode(wrapper, PageMode.Form); + wrapper.find('input[name="username"]').simulate('change', { target: { value: 'username1' } }); wrapper.find('input[name="password"]').simulate('change', { target: { value: 'password1' } }); wrapper.find(EuiButton).simulate('click'); @@ -144,47 +209,125 @@ describe('LoginForm', () => { expect(wrapper.find(EuiCallOut).exists()).toBe(false); }); + it('properly switches to login help', async () => { + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Form); + expect(findTestSubject(wrapper, 'loginBackToSelector').exists()).toBe(false); + + // Going to login help. + findTestSubject(wrapper, 'loginHelpLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.LoginHelp); + + expect(findTestSubject(wrapper, 'loginHelp').find(ReactMarkdown)).toMatchSnapshot('Login Help'); + + // Going back to login form. + findTestSubject(wrapper, 'loginBackToLoginLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Form); + expect(findTestSubject(wrapper, 'loginBackToSelector').exists()).toBe(false); + }); + describe('login selector', () => { - it('renders as expected with login form', async () => { + it('renders as expected with providers that use login form', async () => { const coreStartMock = coreMock.createStart(); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Selector); + expect( - shallowWithIntl( - - ) - ).toMatchSnapshot(); + wrapper.find('.secLoginCard').map(card => { + const hint = card.find('.secLoginCard__hint'); + return { + title: card.find('p.secLoginCard__title').text(), + hint: hint.exists() ? hint.text() : '', + icon: card.find(EuiIcon).props().type, + }; + }) + ).toEqual([ + { title: 'Log in with basic/basic', hint: 'Basic hint', icon: 'logoElastic' }, + { title: 'Log in w/SAML', hint: '', icon: 'empty' }, + { title: 'Log in w/PKI', hint: 'PKI hint', icon: 'empty' }, + ]); }); - it('renders as expected without login form for providers with and without description', async () => { + it('renders as expected without providers that use login form', async () => { const coreStartMock = coreMock.createStart(); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Selector); + expect( - shallowWithIntl( - - ) - ).toMatchSnapshot(); + wrapper.find('.secLoginCard').map(card => { + const hint = card.find('.secLoginCard__hint'); + return { + title: card.find('p.secLoginCard__title').text(), + hint: hint.exists() ? hint.text() : '', + icon: card.find(EuiIcon).props().type, + }; + }) + ).toEqual([ + { title: 'Login w/SAML', hint: 'SAML hint', icon: 'empty' }, + { title: 'Log in with pki/pki1', hint: '', icon: 'some-icon' }, + ]); }); it('properly redirects after successful login', async () => { @@ -203,17 +346,19 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} selector={{ enabled: true, providers: [ - { type: 'saml', name: 'saml1', description: 'Login w/SAML' }, - { type: 'pki', name: 'pki1', description: 'Login w/PKI' }, + { type: 'basic', name: 'basic', usesLoginForm: true }, + { type: 'saml', name: 'saml1', description: 'Login w/SAML', usesLoginForm: false }, + { type: 'pki', name: 'pki1', description: 'Login w/PKI', usesLoginForm: false }, ], }} /> ); + expectPageMode(wrapper, PageMode.Selector); + wrapper.findWhere(node => node.key() === 'saml1').simulate('click'); await act(async () => { @@ -246,11 +391,18 @@ describe('LoginForm', () => { http={coreStartMock.http} notifications={coreStartMock.notifications} loginAssistanceMessage="" - showLoginForm={true} - selector={{ enabled: true, providers: [{ type: 'saml', name: 'saml1' }] }} + selector={{ + enabled: true, + providers: [ + { type: 'basic', name: 'basic', usesLoginForm: true }, + { type: 'saml', name: 'saml1', usesLoginForm: false }, + ], + }} /> ); + expectPageMode(wrapper, PageMode.Selector); + wrapper.findWhere(node => node.key() === 'saml1').simulate('click'); await act(async () => { @@ -268,5 +420,123 @@ describe('LoginForm', () => { title: 'Could not perform login.', }); }); + + it('properly switches to login form', async () => { + const currentURL = `https://some-host/login?next=${encodeURIComponent( + '/some-base-path/app/kibana#/home?_g=()' + )}`; + + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + window.location.href = currentURL; + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Selector); + + wrapper.findWhere(node => node.key() === 'basic').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Form); + + expect(coreStartMock.http.post).not.toHaveBeenCalled(); + expect(coreStartMock.notifications.toasts.addError).not.toHaveBeenCalled(); + expect(window.location.href).toBe(currentURL); + }); + + it('properly switches to login help', async () => { + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Selector); + + findTestSubject(wrapper, 'loginHelpLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.LoginHelp); + + expect(findTestSubject(wrapper, 'loginHelp').find(ReactMarkdown)).toMatchSnapshot( + 'Login Help' + ); + + // Going back to login selector. + findTestSubject(wrapper, 'loginBackToLoginLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Selector); + + expect(coreStartMock.http.post).not.toHaveBeenCalled(); + expect(coreStartMock.notifications.toasts.addError).not.toHaveBeenCalled(); + }); + + it('properly switches to login form -> login help and back', async () => { + const coreStartMock = coreMock.createStart({ basePath: '/some-base-path' }); + const wrapper = mountWithIntl( + + ); + + expectPageMode(wrapper, PageMode.Selector); + + // Going to login form. + wrapper.findWhere(node => node.key() === 'basic').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Form); + + // Going to login help. + findTestSubject(wrapper, 'loginHelpLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.LoginHelp); + + expect(findTestSubject(wrapper, 'loginHelp').find(ReactMarkdown)).toMatchSnapshot( + 'Login Help' + ); + + // Going back to login form. + findTestSubject(wrapper, 'loginBackToLoginLink').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Form); + + // Going back to login selector. + findTestSubject(wrapper, 'loginBackToSelector').simulate('click'); + wrapper.update(); + expectPageMode(wrapper, PageMode.Selector); + + expect(coreStartMock.http.post).not.toHaveBeenCalled(); + expect(coreStartMock.notifications.toasts.addError).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx index 01f5c40a69aebd..460c6550085a45 100644 --- a/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx +++ b/x-pack/plugins/security/public/authentication/login/components/login_form/login_form.tsx @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import './login_form.scss'; + import React, { ChangeEvent, Component, FormEvent, Fragment, MouseEvent } from 'react'; import ReactMarkdown from 'react-markdown'; import { EuiButton, + EuiIcon, EuiCallOut, EuiFieldPassword, EuiFieldText, @@ -15,21 +18,28 @@ import { EuiPanel, EuiSpacer, EuiText, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiLoadingSpinner, + EuiLink, + EuiHorizontalRule, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { HttpStart, IHttpFetchError, NotificationsStart } from 'src/core/public'; -import { LoginValidator, LoginValidationResult } from './validate_login'; import { parseNext } from '../../../../../common/parse_next'; import { LoginSelector } from '../../../../../common/login_state'; +import { LoginValidator } from './validate_login'; interface Props { http: HttpStart; notifications: NotificationsStart; selector: LoginSelector; - showLoginForm: boolean; infoMessage?: string; loginAssistanceMessage: string; + loginHelp?: string; } interface State { @@ -42,7 +52,8 @@ interface State { message: | { type: MessageType.None } | { type: MessageType.Danger | MessageType.Info; content: string }; - formError: LoginValidationResult | null; + mode: PageMode; + previousMode: PageMode; } enum LoadingStateType { @@ -57,12 +68,21 @@ enum MessageType { Danger, } +export enum PageMode { + Selector, + Form, + LoginHelp, +} + export class LoginForm extends Component { private readonly validator: LoginValidator; constructor(props: Props) { super(props); this.validator = new LoginValidator({ shouldValidate: false }); + + const mode = this.showLoginSelector() ? PageMode.Selector : PageMode.Form; + this.state = { loadingState: { type: LoadingStateType.None }, username: '', @@ -70,7 +90,8 @@ export class LoginForm extends Component { message: this.props.infoMessage ? { type: MessageType.Info, content: this.props.infoMessage } : { type: MessageType.None }, - formError: null, + mode, + previousMode: mode, }; } @@ -79,19 +100,91 @@ export class LoginForm extends Component { {this.renderLoginAssistanceMessage()} {this.renderMessage()} - {this.renderSelector()} - {this.renderLoginForm()} + {this.renderContent()} + {this.renderPageModeSwitchLink()} ); } - private renderLoginForm = () => { - if (!this.props.showLoginForm) { + private renderLoginAssistanceMessage = () => { + if (!this.props.loginAssistanceMessage) { return null; } return ( - +
+ + + {this.props.loginAssistanceMessage} + +
+ ); + }; + + private renderMessage = () => { + const { message } = this.state; + if (message.type === MessageType.Danger) { + return ( + + + + + ); + } + + if (message.type === MessageType.Info) { + return ( + + + + + ); + } + + return null; + }; + + public renderContent() { + switch (this.state.mode) { + case PageMode.Form: + return this.renderLoginForm(); + case PageMode.Selector: + return this.renderSelector(); + case PageMode.LoginHelp: + return this.renderLoginHelp(); + } + } + + private renderLoginForm = () => { + const loginSelectorLink = this.showLoginSelector() ? ( + + this.onPageModeChange(PageMode.Selector)} + > + + + + ) : null; + + return ( +
{ /> - - - + + + + + + + + + {loginSelectorLink} +
); }; - private renderLoginAssistanceMessage = () => { - if (!this.props.loginAssistanceMessage) { - return null; - } + private renderSelector = () => { + return ( + + {this.props.selector.providers.map(provider => ( + + ))} + + ); + }; + private renderLoginHelp = () => { return ( - - - {this.props.loginAssistanceMessage} + + + {this.props.loginHelp || ''} - +
); }; - private renderMessage = () => { - const { message } = this.state; - if (message.type === MessageType.Danger) { + private renderPageModeSwitchLink = () => { + if (this.state.mode === PageMode.LoginHelp) { return ( - - + + + this.onPageModeChange(this.state.previousMode)} + > + + + ); } - if (message.type === MessageType.Info) { + if (this.props.loginHelp) { return ( - - + + + this.onPageModeChange(PageMode.LoginHelp)} + > + + + ); } @@ -205,60 +359,16 @@ export class LoginForm extends Component { return null; }; - private renderSelector = () => { - const showLoginSelector = - this.props.selector.enabled && this.props.selector.providers.length > 0; - if (!showLoginSelector) { - return null; - } - - const loginSelectorAndLoginFormSeparator = showLoginSelector && this.props.showLoginForm && ( - <> - - ―――   - -   ――― - - - - ); - - return ( - <> - {this.props.selector.providers.map((provider, index) => ( - - this.loginWithSelector(provider.type, provider.name)} - > - {provider.description ?? ( - - )} - - - - ))} - {loginSelectorAndLoginFormSeparator} - - ); - }; - private setUsernameInputRef(ref: HTMLInputElement) { if (ref) { ref.focus(); } } + private onPageModeChange = (mode: PageMode) => { + this.setState({ message: { type: MessageType.None }, mode, previousMode: this.state.mode }); + }; + private onUsernameChange = (e: ChangeEvent) => { this.setState({ username: e.target.value, @@ -279,12 +389,10 @@ export class LoginForm extends Component { this.validator.enableValidation(); const { username, password } = this.state; - const result = this.validator.validateForLogin(username, password); - if (result.isInvalid) { - this.setState({ formError: result }); - return; - } else { - this.setState({ formError: null }); + if (this.validator.validateForLogin(username, password).isInvalid) { + // Since validation is enabled now, we should ask React to re-render form and display + // validation error messages if any. + return this.forceUpdate(); } this.setState({ @@ -351,4 +459,11 @@ export class LoginForm extends Component { loadingState.type !== LoadingStateType.Selector || loadingState.providerName === providerName ); } + + private showLoginSelector() { + return ( + this.props.selector.enabled && + this.props.selector.providers.some(provider => !provider.usesLoginForm) + ); + } } diff --git a/x-pack/plugins/security/public/authentication/login/_login_page.scss b/x-pack/plugins/security/public/authentication/login/login_page.scss similarity index 100% rename from x-pack/plugins/security/public/authentication/login/_login_page.scss rename to x-pack/plugins/security/public/authentication/login/login_page.scss diff --git a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx index c4be57d8d7db7d..ab107e46dfff61 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.test.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.test.tsx @@ -18,8 +18,10 @@ const createLoginState = (options?: Partial) => { allowLogin: true, layout: 'form', requiresSecureConnection: false, - showLoginForm: true, - selector: { enabled: false, providers: [] }, + selector: { + enabled: false, + providers: [{ type: 'basic', name: 'basic1', usesLoginForm: true }], + }, ...options, } as LoginState; }; @@ -163,7 +165,9 @@ describe('LoginPage', () => { it('renders as expected when login is not enabled', async () => { const coreStartMock = coreMock.createStart(); - httpMock.get.mockResolvedValue(createLoginState({ showLoginForm: false })); + httpMock.get.mockResolvedValue( + createLoginState({ selector: { enabled: false, providers: [] } }) + ); const wrapper = shallow( { expect(wrapper.find(LoginForm)).toMatchSnapshot(); }); + + it('renders as expected when loginHelp is set', async () => { + const coreStartMock = coreMock.createStart(); + httpMock.get.mockResolvedValue(createLoginState({ loginHelp: '**some-help**' })); + + const wrapper = shallow( + + ); + + await act(async () => { + await nextTick(); + wrapper.update(); + resetHttpMock(); // so the calls don't show in the BasicLoginForm snapshot + }); + + expect(wrapper.find(LoginForm)).toMatchSnapshot(); + }); }); describe('API calls', () => { diff --git a/x-pack/plugins/security/public/authentication/login/login_page.tsx b/x-pack/plugins/security/public/authentication/login/login_page.tsx index 70f8f76ee0a9c0..d24a301ed24ec7 100644 --- a/x-pack/plugins/security/public/authentication/login/login_page.tsx +++ b/x-pack/plugins/security/public/authentication/login/login_page.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './login_page.scss'; + import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import classNames from 'classnames'; @@ -120,10 +122,9 @@ export class LoginPage extends Component { requiresSecureConnection, isSecureConnection, selector, - showLoginForm, + loginHelp, }: LoginState & { isSecureConnection: boolean }) => { - const isLoginExplicitlyDisabled = - !showLoginForm && (!selector.enabled || selector.providers.length === 0); + const isLoginExplicitlyDisabled = selector.providers.length === 0; if (isLoginExplicitlyDisabled) { return ( { ); }; diff --git a/x-pack/plugins/security/public/authentication/overwritten_session/__snapshots__/overwritten_session_page.test.tsx.snap b/x-pack/plugins/security/public/authentication/overwritten_session/__snapshots__/overwritten_session_page.test.tsx.snap index 2ff760891fa4e2..02b1a7d0d3fa03 100644 --- a/x-pack/plugins/security/public/authentication/overwritten_session/__snapshots__/overwritten_session_page.test.tsx.snap +++ b/x-pack/plugins/security/public/authentication/overwritten_session/__snapshots__/overwritten_session_page.test.tsx.snap @@ -11,7 +11,7 @@ exports[`OverwrittenSessionPage renders as expected 1`] = ` } >
diff --git a/x-pack/plugins/security/public/index.scss b/x-pack/plugins/security/public/index.scss deleted file mode 100644 index 999639ba22eb78..00000000000000 --- a/x-pack/plugins/security/public/index.scss +++ /dev/null @@ -1,7 +0,0 @@ -$secFormWidth: 460px; - -// Authentication styles -@import './authentication/index'; - -// Management styles -@import './management/index'; diff --git a/x-pack/plugins/security/public/index.ts b/x-pack/plugins/security/public/index.ts index 458f7ab801fdf9..8016c942240601 100644 --- a/x-pack/plugins/security/public/index.ts +++ b/x-pack/plugins/security/public/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import './index.scss'; import { PluginInitializer, PluginInitializerContext } from 'src/core/public'; import { SecurityPlugin, @@ -15,7 +14,6 @@ import { } from './plugin'; export { SecurityPluginSetup, SecurityPluginStart }; -export { SessionInfo } from './types'; export { AuthenticatedUser } from '../common/model'; export { SecurityLicense, SecurityLicenseFeatures } from '../common/licensing'; diff --git a/x-pack/plugins/security/public/management/_index.scss b/x-pack/plugins/security/public/management/_index.scss deleted file mode 100644 index 5d419b53230799..00000000000000 --- a/x-pack/plugins/security/public/management/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './roles/index'; -@import './users/index'; -@import './role_mappings/index'; diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx index 272fc9cfc2fe6a..b9ec5b35b3f9df 100644 --- a/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx +++ b/x-pack/plugins/security/public/management/api_keys/api_keys_management_app.tsx @@ -10,8 +10,6 @@ import { i18n } from '@kbn/i18n'; import { StartServicesAccessor } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { PluginStartDependencies } from '../../plugin'; -import { APIKeysGridPage } from './api_keys_grid'; -import { APIKeysAPIClient } from './api_keys_api_client'; import { DocumentationLinksService } from './documentation_links'; interface CreateParams { @@ -28,7 +26,6 @@ export const apiKeysManagementApp = Object.freeze({ defaultMessage: 'API Keys', }), async mount({ basePath, element, setBreadcrumbs }) { - const [{ docLinks, http, notifications, i18n: i18nStart }] = await getStartServices(); setBreadcrumbs([ { text: i18n.translate('xpack.security.apiKeys.breadcrumb', { @@ -38,6 +35,16 @@ export const apiKeysManagementApp = Object.freeze({ }, ]); + const [ + [{ docLinks, http, notifications, i18n: i18nStart }], + { APIKeysGridPage }, + { APIKeysAPIClient }, + ] = await Promise.all([ + getStartServices(), + import('./api_keys_grid'), + import('./api_keys_api_client'), + ]); + render( ; @@ -31,7 +27,6 @@ export const roleMappingsManagementApp = Object.freeze({ defaultMessage: 'Role Mappings', }), async mount({ basePath, element, setBreadcrumbs }) { - const [{ docLinks, http, notifications, i18n: i18nStart }] = await getStartServices(); const roleMappingsBreadcrumbs = [ { text: i18n.translate('xpack.security.roleMapping.breadcrumb', { @@ -41,6 +36,20 @@ export const roleMappingsManagementApp = Object.freeze({ }, ]; + const [ + [{ docLinks, http, notifications, i18n: i18nStart }], + { RoleMappingsGridPage }, + { EditRoleMappingPage }, + { RoleMappingsAPIClient }, + { RolesAPIClient }, + ] = await Promise.all([ + getStartServices(), + import('./role_mappings_grid'), + import('./edit_role_mapping'), + import('./role_mappings_api_client'), + import('../roles'), + ]); + const roleMappingsAPIClient = new RoleMappingsAPIClient(http); const dockLinksService = new DocumentationLinksService(docLinks); const RoleMappingsGridPageWithBreadcrumbs = () => { diff --git a/x-pack/plugins/security/public/management/roles/_index.scss b/x-pack/plugins/security/public/management/roles/_index.scss deleted file mode 100644 index 5256c79f01f10f..00000000000000 --- a/x-pack/plugins/security/public/management/roles/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './edit_role/index'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/_index.scss deleted file mode 100644 index 0153b1734ceba0..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './collapsible_panel/index'; -@import './spaces_popover_list/index'; -@import './privileges/index'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss deleted file mode 100644 index c0f4f8ab9a8701..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './collapsible_panel'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_collapsible_panel.scss b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.scss similarity index 100% rename from x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/_collapsible_panel.scss rename to x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.scss diff --git a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx index 01af7cb4509f64..eb1417600e19b5 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/collapsible_panel/collapsible_panel.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './collapsible_panel.scss'; + import { EuiFlexGroup, EuiFlexItem, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss deleted file mode 100644 index a1a9d038065e61..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './privilege_feature_icon'; -@import './kibana/index'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss deleted file mode 100644 index 19547c0e1953e0..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './feature_table/index'; -@import './space_aware_privilege_section/index'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss deleted file mode 100644 index 6a96553742819f..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './change_all_privileges'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_change_all_privileges.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.scss similarity index 100% rename from x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/_change_all_privileges.scss rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.scss diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx index 2083778e539980..5d7b13acf79da6 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx @@ -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 './change_all_privileges.scss'; + import { EuiContextMenuItem, EuiContextMenuPanel, EuiLink, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss similarity index 100% rename from x-pack/plugins/security/public/management/roles/edit_role/privileges/_privilege_feature_icon.scss rename to x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx index 9e4a3a8a99b565..77445952f3d692 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './feature_table_cell.scss'; + import React from 'react'; import { EuiText, EuiIconTip, EuiIcon, IconType } from '@elastic/eui'; import { SecuredFeature } from '../../../../model'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_index.scss deleted file mode 100644 index 3f40f21e102a1d..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './privilege_matrix'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss deleted file mode 100644 index 8f47727fdf8d62..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/_privilege_matrix.scss +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 1. Allow table to scroll both directions - */ - -.secPrivilegeMatrix__modal, -.secPrivilegeMatrix__modal .euiModal__flex { - overflow: hidden; /* 1 */ -} - -.secPrivilegeMatrix__row--isBasePrivilege, -.secPrivilegeMatrix__cell--isGlobalPrivilege, -.secPrivilegeTable__row--isGlobalSpace { - background-color: $euiColorLightestShade; -} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.scss new file mode 100644 index 00000000000000..8e2a3b0512afbf --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.scss @@ -0,0 +1,3 @@ +.secPrivilegeTable__row--isGlobalSpace { + background-color: $euiColorLightestShade; +} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx index ccb5398a11b236..30a275876fdc79 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx @@ -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 './privilege_space_table.scss'; + import { EuiBadge, EuiBadgeProps, diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss deleted file mode 100644 index b40a32cb8df96b..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './spaces_popover_list'; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_spaces_popover_list.scss b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.scss similarity index 100% rename from x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/_spaces_popover_list.scss rename to x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.scss diff --git a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx index 92e42ec811afce..63ee311f3155ed 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/spaces_popover_list/spaces_popover_list.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './spaces_popover_list.scss'; + import { EuiButtonEmpty, EuiContextMenuItem, diff --git a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx index e1a10fdc2b8c33..9aaa3b47f3b193 100644 --- a/x-pack/plugins/security/public/management/roles/roles_management_app.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_management_app.tsx @@ -12,13 +12,7 @@ import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { SecurityLicense } from '../../../common/licensing'; import { PluginStartDependencies } from '../../plugin'; -import { UserAPIClient } from '../users'; -import { RolesAPIClient } from './roles_api_client'; -import { RolesGridPage } from './roles_grid'; -import { EditRolePage } from './edit_role'; import { DocumentationLinksService } from './documentation_links'; -import { IndicesAPIClient } from './indices_api_client'; -import { PrivilegesAPIClient } from './privileges_api_client'; interface CreateParams { fatalErrors: FatalErrorsSetup; @@ -34,11 +28,6 @@ export const rolesManagementApp = Object.freeze({ order: 20, title: i18n.translate('xpack.security.management.rolesTitle', { defaultMessage: 'Roles' }), async mount({ basePath, element, setBreadcrumbs }) { - const [ - { application, docLinks, http, i18n: i18nStart, injectedMetadata, notifications }, - { data, features }, - ] = await getStartServices(); - const rolesBreadcrumbs = [ { text: i18n.translate('xpack.security.roles.breadcrumb', { defaultMessage: 'Roles' }), @@ -46,6 +35,27 @@ export const rolesManagementApp = Object.freeze({ }, ]; + const [ + [ + { application, docLinks, http, i18n: i18nStart, injectedMetadata, notifications }, + { data, features }, + ], + { RolesGridPage }, + { EditRolePage }, + { RolesAPIClient }, + { IndicesAPIClient }, + { PrivilegesAPIClient }, + { UserAPIClient }, + ] = await Promise.all([ + getStartServices(), + import('./roles_grid'), + import('./edit_role'), + import('./roles_api_client'), + import('./indices_api_client'), + import('./privileges_api_client'), + import('../users'), + ]); + const rolesAPIClient = new RolesAPIClient(http); const RolesGridPageWithBreadcrumbs = () => { setBreadcrumbs(rolesBreadcrumbs); diff --git a/x-pack/plugins/security/public/management/users/_index.scss b/x-pack/plugins/security/public/management/users/_index.scss deleted file mode 100644 index 35df0c1b965837..00000000000000 --- a/x-pack/plugins/security/public/management/users/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './edit_user/index'; diff --git a/x-pack/plugins/security/public/management/users/edit_user/_index.scss b/x-pack/plugins/security/public/management/users/edit_user/_index.scss deleted file mode 100644 index 734ba7882ba72c..00000000000000 --- a/x-pack/plugins/security/public/management/users/edit_user/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './edit_user_page'; diff --git a/x-pack/plugins/security/public/management/users/edit_user/_edit_user_page.scss b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss similarity index 76% rename from x-pack/plugins/security/public/management/users/edit_user/_edit_user_page.scss rename to x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss index 7b24b74aceba0c..727fac4782752d 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/_edit_user_page.scss +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss @@ -1,5 +1,5 @@ .secUsersEditPage__content { - max-width: $secFormWidth; + max-width: 460px; margin-left: auto; margin-right: auto; flex-grow: 0; diff --git a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx index 6417ce81b647d5..1c8130029bb501 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/edit_user_page.tsx @@ -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 './edit_user_page.scss'; + import { get } from 'lodash'; import React, { Component, Fragment, ChangeEvent } from 'react'; import { diff --git a/x-pack/plugins/security/public/management/users/users_management_app.tsx b/x-pack/plugins/security/public/management/users/users_management_app.tsx index 82a2b8d2a98ada..9d337c1508ad48 100644 --- a/x-pack/plugins/security/public/management/users/users_management_app.tsx +++ b/x-pack/plugins/security/public/management/users/users_management_app.tsx @@ -12,10 +12,6 @@ import { StartServicesAccessor } from 'src/core/public'; import { RegisterManagementAppArgs } from '../../../../../../src/plugins/management/public'; import { AuthenticationServiceSetup } from '../../authentication'; import { PluginStartDependencies } from '../../plugin'; -import { RolesAPIClient } from '../roles'; -import { UserAPIClient } from './user_api_client'; -import { UsersGridPage } from './users_grid'; -import { EditUserPage } from './edit_user'; interface CreateParams { authc: AuthenticationServiceSetup; @@ -30,7 +26,6 @@ export const usersManagementApp = Object.freeze({ order: 10, title: i18n.translate('xpack.security.management.usersTitle', { defaultMessage: 'Users' }), async mount({ basePath, element, setBreadcrumbs }) { - const [{ http, notifications, i18n: i18nStart }] = await getStartServices(); const usersBreadcrumbs = [ { text: i18n.translate('xpack.security.users.breadcrumb', { defaultMessage: 'Users' }), @@ -38,6 +33,20 @@ export const usersManagementApp = Object.freeze({ }, ]; + const [ + [{ http, notifications, i18n: i18nStart }], + { UsersGridPage }, + { EditUserPage }, + { UserAPIClient }, + { RolesAPIClient }, + ] = await Promise.all([ + getStartServices(), + import('./users_grid'), + import('./edit_user'), + import('./user_api_client'), + import('../roles'), + ]); + const userAPIClient = new UserAPIClient(http); const rolesAPIClient = new RolesAPIClient(http); const UsersGridPageWithBreadcrumbs = () => { diff --git a/x-pack/plugins/security/public/session/session_timeout.test.tsx b/x-pack/plugins/security/public/session/session_timeout.test.tsx index eca3e7d6727df8..11aadcff377ef7 100644 --- a/x-pack/plugins/security/public/session/session_timeout.test.tsx +++ b/x-pack/plugins/security/public/session/session_timeout.test.tsx @@ -74,6 +74,7 @@ describe('Session Timeout', () => { now, idleTimeoutExpiration: now + 2 * 60 * 1000, lifespanExpiration: null, + provider: { type: 'basic', name: 'basic1' }, }; let notifications: ReturnType['notifications']; let http: ReturnType['http']; @@ -192,6 +193,7 @@ describe('Session Timeout', () => { now, idleTimeoutExpiration: null, lifespanExpiration: now + 2 * 60 * 1000, + provider: { type: 'basic', name: 'basic1' }, }; http.fetch.mockResolvedValue(sessionInfo); await sessionTimeout.start(); @@ -225,6 +227,7 @@ describe('Session Timeout', () => { now, idleTimeoutExpiration: null, lifespanExpiration: now + 2 * 60 * 1000, + provider: { type: 'basic', name: 'basic1' }, }; http.fetch.mockResolvedValue(sessionInfo); await sessionTimeout.start(); @@ -251,6 +254,7 @@ describe('Session Timeout', () => { now: now + elapsed, idleTimeoutExpiration: now + elapsed + 2 * 60 * 1000, lifespanExpiration: null, + provider: { type: 'basic', name: 'basic1' }, }); await sessionTimeout.extend('/foo'); expect(http.fetch).toHaveBeenCalledTimes(3); @@ -303,6 +307,7 @@ describe('Session Timeout', () => { now, idleTimeoutExpiration: now + 64 * 1000, lifespanExpiration: null, + provider: { type: 'basic', name: 'basic1' }, }); await sessionTimeout.start(); expect(http.fetch).toHaveBeenCalled(); @@ -336,6 +341,7 @@ describe('Session Timeout', () => { now: now + elapsed, idleTimeoutExpiration: now + elapsed + 2 * 60 * 1000, lifespanExpiration: null, + provider: { type: 'basic', name: 'basic1' }, }; http.fetch.mockResolvedValue(sessionInfo); await sessionTimeout.extend('/foo'); @@ -358,6 +364,7 @@ describe('Session Timeout', () => { now, idleTimeoutExpiration: now + 4 * 1000, lifespanExpiration: null, + provider: { type: 'basic', name: 'basic1' }, }); await sessionTimeout.start(); diff --git a/x-pack/plugins/security/public/session/session_timeout.tsx b/x-pack/plugins/security/public/session/session_timeout.tsx index bd6dbad7dbf149..b06d8fffd4b629 100644 --- a/x-pack/plugins/security/public/session/session_timeout.tsx +++ b/x-pack/plugins/security/public/session/session_timeout.tsx @@ -6,10 +6,10 @@ import { NotificationsSetup, Toast, HttpSetup, ToastInput } from 'src/core/public'; import { BroadcastChannel } from 'broadcast-channel'; +import { SessionInfo } from '../../common/types'; import { createToast as createIdleTimeoutToast } from './session_idle_timeout_warning'; import { createToast as createLifespanToast } from './session_lifespan_warning'; import { ISessionExpired } from './session_expired'; -import { SessionInfo } from '../types'; /** * Client session timeout is decreased by this number so that Kibana server @@ -127,7 +127,7 @@ export class SessionTimeout implements ISessionTimeout { this.sessionInfo = sessionInfo; // save the provider name in session storage, we will need it when we log out const key = `${this.tenant}/session_provider`; - sessionStorage.setItem(key, sessionInfo.provider); + sessionStorage.setItem(key, sessionInfo.provider.name); const { timeout, isLifespanTimeout } = this.getTimeout(); if (timeout == null) { diff --git a/x-pack/plugins/security/server/audit/audit_logger.test.ts b/x-pack/plugins/security/server/audit/audit_logger.test.ts index f7ee210a21a741..4dfd69a2ccb1ff 100644 --- a/x-pack/plugins/security/server/audit/audit_logger.test.ts +++ b/x-pack/plugins/security/server/audit/audit_logger.test.ts @@ -62,7 +62,7 @@ describe(`#savedObjectsAuthorizationFailure`, () => { }); describe(`#savedObjectsAuthorizationSuccess`, () => { - test('logs via auditLogger when xpack.security.audit.enabled is true', () => { + test('logs via auditLogger', () => { const auditLogger = createMockAuditLogger(); const securityAuditLogger = new SecurityAuditLogger(() => auditLogger); const username = 'foo-user'; @@ -92,3 +92,21 @@ describe(`#savedObjectsAuthorizationSuccess`, () => { ); }); }); + +describe(`#accessAgreementAcknowledged`, () => { + test('logs via auditLogger', () => { + const auditLogger = createMockAuditLogger(); + const securityAuditLogger = new SecurityAuditLogger(() => auditLogger); + const username = 'foo-user'; + const provider = { type: 'saml', name: 'saml1' }; + + securityAuditLogger.accessAgreementAcknowledged(username, provider); + + expect(auditLogger.log).toHaveBeenCalledTimes(1); + expect(auditLogger.log).toHaveBeenCalledWith( + 'access_agreement_acknowledged', + 'foo-user acknowledged access agreement (saml/saml1).', + { username, provider } + ); + }); +}); diff --git a/x-pack/plugins/security/server/audit/audit_logger.ts b/x-pack/plugins/security/server/audit/audit_logger.ts index 40b525b5d21888..d7243ecbe13f87 100644 --- a/x-pack/plugins/security/server/audit/audit_logger.ts +++ b/x-pack/plugins/security/server/audit/audit_logger.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AuthenticationProvider } from '../../common/types'; import { LegacyAPI } from '../plugin'; export class SecurityAuditLogger { @@ -57,4 +58,12 @@ export class SecurityAuditLogger { } ); } + + accessAgreementAcknowledged(username: string, provider: AuthenticationProvider) { + this.getAuditLogger().log( + 'access_agreement_acknowledged', + `${username} acknowledged access agreement (${provider.type}/${provider.name}).`, + { username, provider } + ); + } } diff --git a/x-pack/plugins/security/server/audit/index.mock.ts b/x-pack/plugins/security/server/audit/index.mock.ts index c14b98ed4781eb..888aa3361faf04 100644 --- a/x-pack/plugins/security/server/audit/index.mock.ts +++ b/x-pack/plugins/security/server/audit/index.mock.ts @@ -11,6 +11,7 @@ export const securityAuditLoggerMock = { return ({ savedObjectsAuthorizationFailure: jest.fn(), savedObjectsAuthorizationSuccess: jest.fn(), + accessAgreementAcknowledged: jest.fn(), } as unknown) as jest.Mocked; }, }; diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index a595b63faaf9b3..49b7b40659cfc1 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -20,7 +20,10 @@ import { elasticsearchServiceMock, sessionStorageMock, } from '../../../../../src/core/server/mocks'; +import { licenseMock } from '../../common/licensing/index.mock'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; +import { securityAuditLoggerMock } from '../audit/index.mock'; +import { SecurityLicenseFeatures } from '../../common/licensing'; import { ConfigSchema, createConfig } from '../config'; import { AuthenticationResult } from './authentication_result'; import { Authenticator, AuthenticatorOptions, ProviderSession } from './authenticator'; @@ -39,8 +42,11 @@ function getMockOptions({ selector?: AuthenticatorOptions['config']['authc']['selector']; } = {}) { return { + auditLogger: securityAuditLoggerMock.create(), + getCurrentUser: jest.fn(), clusterClient: elasticsearchServiceMock.createClusterClient(), basePath: httpServiceMock.createSetupContract().basePath, + license: licenseMock.create(), loggers: loggingServiceMock.create(), config: createConfig( ConfigSchema.validate({ session, authc: { selector, providers, http } }), @@ -1108,6 +1114,141 @@ describe('Authenticator', () => { expect(mockBasicAuthenticationProvider.authenticate).not.toHaveBeenCalled(); }); }); + + describe('with Access Agreement', () => { + const mockUser = mockAuthenticatedUser(); + beforeEach(() => { + mockOptions = getMockOptions({ + providers: { + basic: { basic1: { order: 0, accessAgreement: { message: 'some notice' } } }, + }, + }); + mockOptions.sessionStorageFactory.asScoped.mockReturnValue(mockSessionStorage); + mockOptions.license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, + } as SecurityLicenseFeatures); + + mockBasicAuthenticationProvider.authenticate.mockResolvedValue( + AuthenticationResult.succeeded(mockUser) + ); + + authenticator = new Authenticator(mockOptions); + }); + + it('does not redirect to Access Agreement if there is no active session', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue(null); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('does not redirect AJAX requests to Access Agreement', async () => { + const request = httpServerMock.createKibanaRequest({ headers: { 'kbn-xsrf': 'xsrf' } }); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('does not redirect to Access Agreement if request cannot be handled', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + mockBasicAuthenticationProvider.authenticate.mockResolvedValue( + AuthenticationResult.notHandled() + ); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.notHandled() + ); + }); + + it('does not redirect to Access Agreement if authentication fails', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + const failureReason = new Error('something went wrong'); + mockBasicAuthenticationProvider.authenticate.mockResolvedValue( + AuthenticationResult.failed(failureReason) + ); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.failed(failureReason) + ); + }); + + it('does not redirect to Access Agreement if redirect is required to complete authentication', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + mockBasicAuthenticationProvider.authenticate.mockResolvedValue( + AuthenticationResult.redirectTo('/some-url') + ); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.redirectTo('/some-url') + ); + }); + + it('does not redirect to Access Agreement if user has already acknowledged it', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue({ + ...mockSessVal, + accessAgreementAcknowledged: true, + }); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('does not redirect to Access Agreement its own requests', async () => { + const request = httpServerMock.createKibanaRequest({ path: '/security/access_agreement' }); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('does not redirect to Access Agreement if it is not configured', async () => { + mockOptions = getMockOptions({ providers: { basic: { basic1: { order: 0 } } } }); + mockOptions.sessionStorageFactory.asScoped.mockReturnValue(mockSessionStorage); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + authenticator = new Authenticator(mockOptions); + + const request = httpServerMock.createKibanaRequest(); + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('does not redirect to Access Agreement if license doesnt allow it.', async () => { + const request = httpServerMock.createKibanaRequest(); + mockSessionStorage.get.mockResolvedValue(mockSessVal); + mockOptions.license.getFeatures.mockReturnValue({ + allowAccessAgreement: false, + } as SecurityLicenseFeatures); + + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.succeeded(mockUser) + ); + }); + + it('redirects to Access Agreement when needed.', async () => { + mockSessionStorage.get.mockResolvedValue(mockSessVal); + + const request = httpServerMock.createKibanaRequest(); + await expect(authenticator.authenticate(request)).resolves.toEqual( + AuthenticationResult.redirectTo( + '/mock-server-basepath/security/access_agreement?next=%2Fmock-server-basepath%2Fpath' + ) + ); + }); + }); }); describe('`logout` method', () => { @@ -1228,13 +1369,13 @@ describe('Authenticator', () => { now: currentDate, idleTimeoutExpiration: currentDate + 60000, lifespanExpiration: currentDate + 120000, - provider: 'basic1', + provider: { type: 'basic' as 'basic', name: 'basic1' }, }; mockSessionStorage.get.mockResolvedValue({ idleTimeoutExpiration: mockInfo.idleTimeoutExpiration, lifespanExpiration: mockInfo.lifespanExpiration, state, - provider: { type: 'basic', name: mockInfo.provider }, + provider: mockInfo.provider, path: mockOptions.basePath.serverBasePath, }); jest.spyOn(Date, 'now').mockImplementation(() => currentDate); @@ -1274,4 +1415,84 @@ describe('Authenticator', () => { expect(authenticator.isProviderTypeEnabled('saml')).toBe(true); }); }); + + describe('`acknowledgeAccessAgreement` method', () => { + let authenticator: Authenticator; + let mockOptions: ReturnType; + let mockSessionStorage: jest.Mocked>; + let mockSessionValue: any; + beforeEach(() => { + mockOptions = getMockOptions({ providers: { basic: { basic1: { order: 0 } } } }); + mockSessionStorage = sessionStorageMock.create(); + mockOptions.sessionStorageFactory.asScoped.mockReturnValue(mockSessionStorage); + mockSessionValue = { + idleTimeoutExpiration: null, + lifespanExpiration: null, + state: { authorization: 'Basic xxx' }, + provider: { type: 'basic', name: 'basic1' }, + path: mockOptions.basePath.serverBasePath, + }; + mockSessionStorage.get.mockResolvedValue(mockSessionValue); + mockOptions.getCurrentUser.mockReturnValue(mockAuthenticatedUser()); + mockOptions.license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, + } as SecurityLicenseFeatures); + + authenticator = new Authenticator(mockOptions); + }); + + it('fails if user is not authenticated', async () => { + mockOptions.getCurrentUser.mockReturnValue(null); + + await expect( + authenticator.acknowledgeAccessAgreement(httpServerMock.createKibanaRequest()) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Cannot acknowledge access agreement for unauthenticated user."` + ); + + expect(mockSessionStorage.set).not.toHaveBeenCalled(); + }); + + it('fails if cannot retrieve user session', async () => { + mockSessionStorage.get.mockResolvedValue(null); + + await expect( + authenticator.acknowledgeAccessAgreement(httpServerMock.createKibanaRequest()) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Cannot acknowledge access agreement for unauthenticated user."` + ); + + expect(mockSessionStorage.set).not.toHaveBeenCalled(); + }); + + it('fails if license doesn allow access agreement acknowledgement', async () => { + mockOptions.license.getFeatures.mockReturnValue({ + allowAccessAgreement: false, + } as SecurityLicenseFeatures); + + await expect( + authenticator.acknowledgeAccessAgreement(httpServerMock.createKibanaRequest()) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Current license does not allow access agreement acknowledgement."` + ); + + expect(mockSessionStorage.set).not.toHaveBeenCalled(); + }); + + it('properly acknowledges access agreement for the authenticated user', async () => { + await authenticator.acknowledgeAccessAgreement(httpServerMock.createKibanaRequest()); + + expect(mockSessionStorage.set).toHaveBeenCalledTimes(1); + expect(mockSessionStorage.set).toHaveBeenCalledWith({ + ...mockSessionValue, + accessAgreementAcknowledged: true, + }); + + expect(mockOptions.auditLogger.accessAgreementAcknowledged).toHaveBeenCalledTimes(1); + expect(mockOptions.auditLogger.accessAgreementAcknowledged).toHaveBeenCalledWith('user', { + type: 'basic', + name: 'basic1', + }); + }); + }); }); diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index caf5b485d05e35..58dea2b23e5463 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -14,6 +14,10 @@ import { HttpServiceSetup, IClusterClient, } from '../../../../../src/core/server'; +import { SecurityLicense } from '../../common/licensing'; +import { AuthenticatedUser } from '../../common/model'; +import { AuthenticationProvider, SessionInfo } from '../../common/types'; +import { SecurityAuditLogger } from '../audit'; import { ConfigType } from '../config'; import { getErrorStatusCode } from '../errors'; @@ -32,7 +36,6 @@ import { import { AuthenticationResult } from './authentication_result'; import { DeauthenticationResult } from './deauthentication_result'; import { Tokens } from './tokens'; -import { SessionInfo } from '../../public'; import { canRedirectRequest } from './can_redirect_request'; import { HTTPAuthorizationHeader } from './http_authentication'; @@ -43,7 +46,7 @@ export interface ProviderSession { /** * Name and type of the provider this session belongs to. */ - provider: { type: string; name: string }; + provider: AuthenticationProvider; /** * The Unix time in ms when the session should be considered expired. If `null`, session will stay @@ -67,6 +70,11 @@ export interface ProviderSession { * Cookie "Path" attribute that is validated against the current Kibana server configuration. */ path: string; + + /** + * Indicates whether user acknowledged access agreement or not. + */ + accessAgreementAcknowledged?: boolean; } /** @@ -76,7 +84,7 @@ export interface ProviderLoginAttempt { /** * Name or type of the provider this login attempt is targeted for. */ - provider: { name: string } | { type: string }; + provider: Pick | Pick; /** * Login attempt can have any form and defined by the specific provider. @@ -85,8 +93,11 @@ export interface ProviderLoginAttempt { } export interface AuthenticatorOptions { + auditLogger: SecurityAuditLogger; + getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; config: Pick; basePath: HttpServiceSetup['basePath']; + license: SecurityLicense; loggers: LoggerFactory; clusterClient: IClusterClient; sessionStorageFactory: SessionStorageFactory; @@ -109,6 +120,11 @@ const providerMap = new Map< [PKIAuthenticationProvider.type, PKIAuthenticationProvider], ]); +/** + * The route to the access agreement UI. + */ +const ACCESS_AGREEMENT_ROUTE = '/security/access_agreement'; + function assertRequest(request: KibanaRequest) { if (!(request instanceof KibanaRequest)) { throw new Error(`Request should be a valid "KibanaRequest" instance, was [${typeof request}].`); @@ -135,7 +151,7 @@ function isLoginAttemptWithProviderName( function isLoginAttemptWithProviderType( attempt: unknown -): attempt is { value: unknown; provider: { type: string } } { +): attempt is { value: unknown; provider: Pick } { return ( typeof attempt === 'object' && (attempt as any)?.provider?.type && @@ -341,14 +357,7 @@ export class Authenticator { const sessionStorage = this.options.sessionStorageFactory.asScoped(request); const existingSession = await this.getSessionValue(sessionStorage); - // If request doesn't have any session information, isn't attributed with HTTP Authorization - // header and Login Selector is enabled, we must redirect user to the login selector. - const useLoginSelector = - !existingSession && - this.options.config.authc.selector.enabled && - canRedirectRequest(request) && - HTTPAuthorizationHeader.parseFromRequest(request) == null; - if (useLoginSelector) { + if (this.shouldRedirectToLoginSelector(request, existingSession)) { this.logger.debug('Redirecting request to Login Selector.'); return AuthenticationResult.redirectTo( `${this.options.basePath.serverBasePath}/login?next=${encodeURIComponent( @@ -368,7 +377,7 @@ export class Authenticator { ownsSession ? existingSession!.state : null ); - this.updateSessionValue(sessionStorage, { + const updatedSession = this.updateSessionValue(sessionStorage, { provider: { type: provider.type, name: providerName }, isSystemRequest: request.isSystemRequest, authenticationResult, @@ -376,6 +385,20 @@ export class Authenticator { }); if (!authenticationResult.notHandled()) { + if ( + authenticationResult.succeeded() && + this.shouldRedirectToAccessAgreement(request, updatedSession) + ) { + this.logger.debug('Redirecting user to the access agreement screen.'); + return AuthenticationResult.redirectTo( + `${ + this.options.basePath.serverBasePath + }${ACCESS_AGREEMENT_ROUTE}?next=${encodeURIComponent( + `${this.options.basePath.get(request)}${request.url.path}` + )}` + ); + } + return authenticationResult; } } @@ -441,7 +464,7 @@ export class Authenticator { now: Date.now(), idleTimeoutExpiration: sessionValue.idleTimeoutExpiration, lifespanExpiration: sessionValue.lifespanExpiration, - provider: sessionValue.provider.name, + provider: sessionValue.provider, }; } return null; @@ -455,6 +478,32 @@ export class Authenticator { return [...this.providers.values()].some(provider => provider.type === providerType); } + /** + * Acknowledges access agreement on behalf of the currently authenticated user. + * @param request Request instance. + */ + async acknowledgeAccessAgreement(request: KibanaRequest) { + assertRequest(request); + + const sessionStorage = this.options.sessionStorageFactory.asScoped(request); + const existingSession = await this.getSessionValue(sessionStorage); + const currentUser = this.options.getCurrentUser(request); + if (!existingSession || !currentUser) { + throw new Error('Cannot acknowledge access agreement for unauthenticated user.'); + } + + if (!this.options.license.getFeatures().allowAccessAgreement) { + throw new Error('Current license does not allow access agreement acknowledgement.'); + } + + sessionStorage.set({ ...existingSession, accessAgreementAcknowledged: true }); + + this.options.auditLogger.accessAgreementAcknowledged( + currentUser.username, + existingSession.provider + ); + } + /** * Initializes HTTP Authentication provider and appends it to the end of the list of enabled * authentication providers. @@ -538,14 +587,14 @@ export class Authenticator { existingSession, isSystemRequest, }: { - provider: { type: string; name: string }; + provider: AuthenticationProvider; authenticationResult: AuthenticationResult; existingSession: ProviderSession | null; isSystemRequest: boolean; } ) { if (!existingSession && !authenticationResult.shouldUpdateState()) { - return; + return null; } // If authentication succeeds or requires redirect we should automatically extend existing user session, @@ -563,9 +612,12 @@ export class Authenticator { (authenticationResult.failed() && getErrorStatusCode(authenticationResult.error) === 401) ) { sessionStorage.clear(); - } else if (sessionCanBeUpdated) { + return null; + } + + if (sessionCanBeUpdated) { const { idleTimeoutExpiration, lifespanExpiration } = this.calculateExpiry(existingSession); - sessionStorage.set({ + const updatedSession = { state: authenticationResult.shouldUpdateState() ? authenticationResult.state : existingSession!.state, @@ -573,8 +625,13 @@ export class Authenticator { idleTimeoutExpiration, lifespanExpiration, path: this.serverBasePath, - }); + accessAgreementAcknowledged: existingSession?.accessAgreementAcknowledged, + }; + sessionStorage.set(updatedSession); + return updatedSession; } + + return existingSession; } private getProviderName(query: any): string | null { @@ -600,4 +657,48 @@ export class Authenticator { return { idleTimeoutExpiration, lifespanExpiration }; } + + /** + * Checks whether request should be redirected to the Login Selector UI. + * @param request Request instance. + * @param session Current session value if any. + */ + private shouldRedirectToLoginSelector(request: KibanaRequest, session: ProviderSession | null) { + // Request should be redirected to Login Selector UI only if all following conditions are met: + // 1. Request can be redirected (not API call) + // 2. Request is not authenticated yet + // 3. Login Selector UI is enabled + // 4. Request isn't attributed with HTTP Authorization header + return ( + canRedirectRequest(request) && + !session && + this.options.config.authc.selector.enabled && + HTTPAuthorizationHeader.parseFromRequest(request) == null + ); + } + + /** + * Checks whether request should be redirected to the Access Agreement UI. + * @param request Request instance. + * @param session Current session value if any. + */ + private shouldRedirectToAccessAgreement(request: KibanaRequest, session: ProviderSession | null) { + // Request should be redirected to Access Agreement UI only if all following conditions are met: + // 1. Request can be redirected (not API call) + // 2. Request is authenticated, but user hasn't acknowledged access agreement in the current + // session yet (based on the flag we store in the session) + // 3. Request is authenticated by the provider that has `accessAgreement` configured + // 4. Current license allows access agreement + // 5. And it's not a request to the Access Agreement UI itself + return ( + canRedirectRequest(request) && + session != null && + !session.accessAgreementAcknowledged && + (this.options.config.authc.providers as Record)[session.provider.type]?.[ + session.provider.name + ]?.accessAgreement && + this.options.license.getFeatures().allowAccessAgreement && + request.url.pathname !== ACCESS_AGREEMENT_ROUTE + ); + } } diff --git a/x-pack/plugins/security/server/authentication/index.mock.ts b/x-pack/plugins/security/server/authentication/index.mock.ts index 9397a7a42b3262..7cd3ac18634f78 100644 --- a/x-pack/plugins/security/server/authentication/index.mock.ts +++ b/x-pack/plugins/security/server/authentication/index.mock.ts @@ -19,5 +19,6 @@ export const authenticationMock = { invalidateAPIKeyAsInternalUser: jest.fn(), isAuthenticated: jest.fn(), getSessionInfo: jest.fn(), + acknowledgeAccessAgreement: jest.fn(), }), }; diff --git a/x-pack/plugins/security/server/authentication/index.test.ts b/x-pack/plugins/security/server/authentication/index.test.ts index 6609f8707976b7..1c1e0ed781f18e 100644 --- a/x-pack/plugins/security/server/authentication/index.test.ts +++ b/x-pack/plugins/security/server/authentication/index.test.ts @@ -19,6 +19,7 @@ import { elasticsearchServiceMock, } from '../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; +import { securityAuditLoggerMock } from '../audit/index.mock'; import { AuthenticationHandler, @@ -40,9 +41,11 @@ import { InvalidateAPIKeyParams, } from './api_keys'; import { SecurityLicense } from '../../common/licensing'; +import { SecurityAuditLogger } from '../audit'; describe('setupAuthentication()', () => { let mockSetupAuthenticationParams: { + auditLogger: jest.Mocked; config: ConfigType; loggers: LoggerFactory; http: jest.Mocked; @@ -52,6 +55,7 @@ describe('setupAuthentication()', () => { let mockScopedClusterClient: jest.Mocked>; beforeEach(() => { mockSetupAuthenticationParams = { + auditLogger: securityAuditLoggerMock.create(), http: coreMock.createSetup().http, config: createConfig( ConfigSchema.validate({ diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index d76a5a533d4983..779b852195b028 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -10,12 +10,13 @@ import { KibanaRequest, LoggerFactory, } from '../../../../../src/core/server'; +import { SecurityLicense } from '../../common/licensing'; import { AuthenticatedUser } from '../../common/model'; +import { SecurityAuditLogger } from '../audit'; import { ConfigType } from '../config'; import { getErrorStatusCode } from '../errors'; import { Authenticator, ProviderSession } from './authenticator'; import { APIKeys, CreateAPIKeyParams, InvalidateAPIKeyParams } from './api_keys'; -import { SecurityLicense } from '../../common/licensing'; export { canRedirectRequest } from './can_redirect_request'; export { Authenticator, ProviderLoginAttempt } from './authenticator'; @@ -35,6 +36,7 @@ export { } from './http_authentication'; interface SetupAuthenticationParams { + auditLogger: SecurityAuditLogger; http: CoreSetup['http']; clusterClient: IClusterClient; config: ConfigType; @@ -45,6 +47,7 @@ interface SetupAuthenticationParams { export type Authentication = UnwrapPromise>; export async function setupAuthentication({ + auditLogger, http, clusterClient, config, @@ -82,9 +85,12 @@ export async function setupAuthentication({ }; const authenticator = new Authenticator({ + auditLogger, + getCurrentUser, clusterClient, basePath: http.basePath, config: { session: config.session, authc: config.authc }, + license, loggers, sessionStorageFactory: await http.createCookieSessionStorageFactory({ encryptionKey: config.encryptionKey, @@ -171,6 +177,7 @@ export async function setupAuthentication({ logout: authenticator.logout.bind(authenticator), getSessionInfo: authenticator.getSessionInfo.bind(authenticator), isProviderTypeEnabled: authenticator.isProviderTypeEnabled.bind(authenticator), + acknowledgeAccessAgreement: authenticator.acknowledgeAccessAgreement.bind(authenticator), getCurrentUser, areAPIKeysEnabled: () => apiKeys.areAPIKeysEnabled(), createAPIKey: (request: KibanaRequest, params: CreateAPIKeyParams) => diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 46a7ee79ee60c5..2c248646499774 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -27,8 +27,11 @@ describe('config schema', () => { "providers": Object { "basic": Object { "basic": Object { + "accessAgreement": undefined, "description": undefined, "enabled": true, + "hint": undefined, + "icon": undefined, "order": 0, "showInSelector": true, }, @@ -69,8 +72,11 @@ describe('config schema', () => { "providers": Object { "basic": Object { "basic": Object { + "accessAgreement": undefined, "description": undefined, "enabled": true, + "hint": undefined, + "icon": undefined, "order": 0, "showInSelector": true, }, @@ -111,8 +117,11 @@ describe('config schema', () => { "providers": Object { "basic": Object { "basic": Object { + "accessAgreement": undefined, "description": undefined, "enabled": true, + "hint": undefined, + "icon": undefined, "order": 0, "showInSelector": true, }, @@ -361,20 +370,6 @@ describe('config schema', () => { `); }); - it('does not allow custom description', () => { - expect(() => - ConfigSchema.validate({ - authc: { - providers: { basic: { basic1: { order: 0, description: 'Some description' } } }, - }, - }) - ).toThrowErrorMatchingInlineSnapshot(` -"[authc.providers]: types that failed validation: -- [authc.providers.0]: expected value of type [array] but got [Object] -- [authc.providers.1.basic.basic1.description]: \`basic\` provider does not support custom description." -`); - }); - it('cannot be hidden from selector', () => { expect(() => ConfigSchema.validate({ @@ -410,7 +405,9 @@ describe('config schema', () => { Object { "basic": Object { "basic1": Object { + "description": "Log in with Elasticsearch", "enabled": true, + "icon": "logoElastic", "order": 0, "showInSelector": true, }, @@ -433,20 +430,6 @@ describe('config schema', () => { `); }); - it('does not allow custom description', () => { - expect(() => - ConfigSchema.validate({ - authc: { - providers: { token: { token1: { order: 0, description: 'Some description' } } }, - }, - }) - ).toThrowErrorMatchingInlineSnapshot(` -"[authc.providers]: types that failed validation: -- [authc.providers.0]: expected value of type [array] but got [Object] -- [authc.providers.1.token.token1.description]: \`token\` provider does not support custom description." -`); - }); - it('cannot be hidden from selector', () => { expect(() => ConfigSchema.validate({ @@ -482,7 +465,9 @@ describe('config schema', () => { Object { "token": Object { "token1": Object { + "description": "Log in with Elasticsearch", "enabled": true, + "icon": "logoElastic", "order": 0, "showInSelector": true, }, @@ -759,12 +744,16 @@ describe('config schema', () => { Object { "basic": Object { "basic1": Object { + "description": "Log in with Elasticsearch", "enabled": true, + "icon": "logoElastic", "order": 0, "showInSelector": true, }, "basic2": Object { + "description": "Log in with Elasticsearch", "enabled": false, + "icon": "logoElastic", "order": 1, "showInSelector": true, }, @@ -911,20 +900,12 @@ describe('createConfig()', () => { "sortedProviders": Array [ Object { "name": "saml", - "options": Object { - "description": undefined, - "order": 0, - "showInSelector": true, - }, + "order": 0, "type": "saml", }, Object { "name": "basic", - "options": Object { - "description": undefined, - "order": 1, - "showInSelector": true, - }, + "order": 1, "type": "basic", }, ], @@ -1015,47 +996,27 @@ describe('createConfig()', () => { Array [ Object { "name": "oidc1", - "options": Object { - "description": undefined, - "order": 0, - "showInSelector": true, - }, + "order": 0, "type": "oidc", }, Object { "name": "saml2", - "options": Object { - "description": undefined, - "order": 1, - "showInSelector": true, - }, + "order": 1, "type": "saml", }, Object { "name": "saml1", - "options": Object { - "description": undefined, - "order": 2, - "showInSelector": true, - }, + "order": 2, "type": "saml", }, Object { "name": "basic1", - "options": Object { - "description": undefined, - "order": 3, - "showInSelector": true, - }, + "order": 3, "type": "basic", }, Object { "name": "oidc2", - "options": Object { - "description": undefined, - "order": 4, - "showInSelector": true, - }, + "order": 4, "type": "oidc", }, ] diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 97ff7d00a43362..8fe79a788ac51c 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -6,6 +6,7 @@ import crypto from 'crypto'; import { schema, Type, TypeOf } from '@kbn/config-schema'; +import { i18n } from '@kbn/i18n'; import { Logger } from '../../../../src/core/server'; export type ConfigType = ReturnType; @@ -21,7 +22,7 @@ const providerOptionsSchema = (providerType: string, optionsSchema: Type) = ); type ProvidersCommonConfigType = Record< - 'enabled' | 'showInSelector' | 'order' | 'description', + 'enabled' | 'showInSelector' | 'order' | 'description' | 'hint' | 'icon', Type >; function getCommonProviderSchemaProperties(overrides: Partial = {}) { @@ -30,6 +31,9 @@ function getCommonProviderSchemaProperties(overrides: Partial; const providersConfigSchema = schema.object( { basic: getUniqueProviderSchema('basic', { - description: schema.maybe( - schema.any({ - validate: () => '`basic` provider does not support custom description.', - }) - ), + description: schema.string({ + defaultValue: i18n.translate('xpack.security.loginWithElasticsearchLabel', { + defaultMessage: 'Log in with Elasticsearch', + }), + }), + icon: schema.string({ defaultValue: 'logoElastic' }), showInSelector: schema.boolean({ defaultValue: true, validate: value => { @@ -68,11 +73,12 @@ const providersConfigSchema = schema.object( }), }), token: getUniqueProviderSchema('token', { - description: schema.maybe( - schema.any({ - validate: () => '`token` provider does not support custom description.', - }) - ), + description: schema.string({ + defaultValue: i18n.translate('xpack.security.loginWithElasticsearchLabel', { + defaultMessage: 'Log in with Elasticsearch', + }), + }), + icon: schema.string({ defaultValue: 'logoElastic' }), showInSelector: schema.boolean({ defaultValue: true, validate: value => { @@ -131,6 +137,7 @@ const providersConfigSchema = schema.object( export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), loginAssistanceMessage: schema.string({ defaultValue: '' }), + loginHelp: schema.maybe(schema.string()), cookieName: schema.string({ defaultValue: 'sid' }), encryptionKey: schema.conditional( schema.contextRef('dist'), @@ -147,7 +154,17 @@ export const ConfigSchema = schema.object({ selector: schema.object({ enabled: schema.maybe(schema.boolean()) }), providers: schema.oneOf([schema.arrayOf(schema.string()), providersConfigSchema], { defaultValue: { - basic: { basic: { enabled: true, showInSelector: true, order: 0, description: undefined } }, + basic: { + basic: { + enabled: true, + showInSelector: true, + order: 0, + description: undefined, + hint: undefined, + icon: undefined, + accessAgreement: undefined, + }, + }, token: undefined, saml: undefined, oidc: undefined, @@ -225,25 +242,19 @@ export function createConfig( const sortedProviders: Array<{ type: keyof ProvidersConfigType; name: string; - options: { order: number; showInSelector: boolean; description?: string }; + order: number; }> = []; for (const [type, providerGroup] of Object.entries(providers)) { - for (const [name, { enabled, showInSelector, order, description }] of Object.entries( - providerGroup ?? {} - )) { + for (const [name, { enabled, order }] of Object.entries(providerGroup ?? {})) { if (!enabled) { delete providerGroup![name]; } else { - sortedProviders.push({ - type: type as any, - name, - options: { order, showInSelector, description }, - }); + sortedProviders.push({ type: type as any, name, order }); } } } - sortedProviders.sort(({ options: { order: orderA } }, { options: { order: orderB } }) => + sortedProviders.sort(({ order: orderA }, { order: orderB }) => orderA < orderB ? -1 : orderA > orderB ? 1 : 0 ); @@ -253,7 +264,8 @@ export function createConfig( typeof config.authc.selector.enabled === 'boolean' ? config.authc.selector.enabled : !isUsingLegacyProvidersFormat && - sortedProviders.filter(provider => provider.options.showInSelector).length > 1; + sortedProviders.filter(({ type, name }) => providers[type]?.[name].showInSelector).length > + 1; return { ...config, diff --git a/x-pack/plugins/security/server/mocks.ts b/x-pack/plugins/security/server/mocks.ts index ababf12c2be606..a6407366bbd3b2 100644 --- a/x-pack/plugins/security/server/mocks.ts +++ b/x-pack/plugins/security/server/mocks.ts @@ -8,6 +8,7 @@ import { SecurityPluginSetup } from './plugin'; import { authenticationMock } from './authentication/index.mock'; import { authorizationMock } from './authorization/index.mock'; +import { licenseMock } from '../common/licensing/index.mock'; function createSetupMock() { const mockAuthz = authorizationMock.create(); @@ -19,6 +20,7 @@ function createSetupMock() { mode: mockAuthz.mode, }, registerSpacesService: jest.fn(), + license: licenseMock.create(), __legacyCompat: {} as SecurityPluginSetup['__legacyCompat'], }; } diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index 3ce0198273af97..d58c999ddccdf8 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -50,21 +50,6 @@ describe('Security Plugin', () => { await expect(plugin.setup(mockCoreSetup, mockDependencies)).resolves.toMatchInlineSnapshot(` Object { "__legacyCompat": Object { - "license": Object { - "features$": Observable { - "_isScalar": false, - "operator": MapOperator { - "project": [Function], - "thisArg": undefined, - }, - "source": Observable { - "_isScalar": false, - "_subscribe": [Function], - }, - }, - "getFeatures": [Function], - "isEnabled": [Function], - }, "registerLegacyAPI": [Function], "registerPrivilegesWithCluster": [Function], }, @@ -72,14 +57,10 @@ describe('Security Plugin', () => { "areAPIKeysEnabled": [Function], "createAPIKey": [Function], "getCurrentUser": [Function], - "getSessionInfo": [Function], "grantAPIKeyAsInternalUser": [Function], "invalidateAPIKey": [Function], "invalidateAPIKeyAsInternalUser": [Function], "isAuthenticated": [Function], - "isProviderTypeEnabled": [Function], - "login": [Function], - "logout": [Function], }, "authz": Object { "actions": Actions { @@ -107,6 +88,21 @@ describe('Security Plugin', () => { "useRbacForRequest": [Function], }, }, + "license": Object { + "features$": Observable { + "_isScalar": false, + "operator": MapOperator { + "project": [Function], + "thisArg": undefined, + }, + "source": Observable { + "_isScalar": false, + "_subscribe": [Function], + }, + }, + "getFeatures": [Function], + "isEnabled": [Function], + }, "registerSpacesService": [Function], } `); diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 9dd4aaafa3494f..97f5aea888dc7c 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -48,8 +48,18 @@ export interface LegacyAPI { * Describes public Security plugin contract returned at the `setup` stage. */ export interface SecurityPluginSetup { - authc: Authentication; + authc: Pick< + Authentication, + | 'isAuthenticated' + | 'getCurrentUser' + | 'areAPIKeysEnabled' + | 'createAPIKey' + | 'invalidateAPIKey' + | 'grantAPIKeyAsInternalUser' + | 'invalidateAPIKeyAsInternalUser' + >; authz: Pick; + license: SecurityLicense; /** * If Spaces plugin is available it's supposed to register its SpacesService with Security plugin @@ -64,7 +74,6 @@ export interface SecurityPluginSetup { __legacyCompat: { registerLegacyAPI: (legacyAPI: LegacyAPI) => void; registerPrivilegesWithCluster: () => void; - license: SecurityLicense; }; } @@ -126,7 +135,9 @@ export class Plugin { license$: licensing.license$, }); + const auditLogger = new SecurityAuditLogger(() => this.getLegacyAPI().auditLogger); const authc = await setupAuthentication({ + auditLogger, http: core.http, clusterClient: this.clusterClient, config, @@ -146,7 +157,7 @@ export class Plugin { }); setupSavedObjects({ - auditLogger: new SecurityAuditLogger(() => this.getLegacyAPI().auditLogger), + auditLogger, authz, savedObjects: core.savedObjects, getSpacesService: this.getSpacesService, @@ -167,7 +178,15 @@ export class Plugin { }); return deepFreeze({ - authc, + authc: { + isAuthenticated: authc.isAuthenticated, + getCurrentUser: authc.getCurrentUser, + areAPIKeysEnabled: authc.areAPIKeysEnabled, + createAPIKey: authc.createAPIKey, + invalidateAPIKey: authc.invalidateAPIKey, + grantAPIKeyAsInternalUser: authc.grantAPIKeyAsInternalUser, + invalidateAPIKeyAsInternalUser: authc.invalidateAPIKeyAsInternalUser, + }, authz: { actions: authz.actions, @@ -175,6 +194,8 @@ export class Plugin { mode: authz.mode, }, + license, + registerSpacesService: service => { if (this.wasSpacesServiceAccessed()) { throw new Error('Spaces service has been accessed before registration.'); @@ -187,8 +208,6 @@ export class Plugin { registerLegacyAPI: (legacyAPI: LegacyAPI) => (this.legacyAPI = legacyAPI), registerPrivilegesWithCluster: async () => await authz.registerPrivilegesWithCluster(), - - license, }, }); } diff --git a/x-pack/plugins/security/server/routes/authentication/common.test.ts b/x-pack/plugins/security/server/routes/authentication/common.test.ts index 156c03e90210b7..5a0401e6320b46 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.test.ts @@ -12,6 +12,7 @@ import { RequestHandlerContext, RouteConfig, } from '../../../../../../src/core/server'; +import { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; import { Authentication, AuthenticationResult, @@ -28,11 +29,13 @@ import { routeDefinitionParamsMock } from '../index.mock'; describe('Common authentication routes', () => { let router: jest.Mocked; let authc: jest.Mocked; + let license: jest.Mocked; let mockContext: RequestHandlerContext; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; authc = routeParamsMock.authc; + license = routeParamsMock.license; mockContext = ({ licensing: { @@ -433,4 +436,61 @@ describe('Common authentication routes', () => { }); }); }); + + describe('acknowledge access agreement', () => { + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + beforeEach(() => { + const [acsRouteConfig, acsRouteHandler] = router.post.mock.calls.find( + ([{ path }]) => path === '/internal/security/access_agreement/acknowledge' + )!; + + license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, + } as SecurityLicenseFeatures); + + routeConfig = acsRouteConfig; + routeHandler = acsRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.validate).toBe(false); + }); + + it(`returns 403 if current license doesn't allow access agreement acknowledgement.`, async () => { + license.getFeatures.mockReturnValue({ + allowAccessAgreement: false, + } as SecurityLicenseFeatures); + + const request = httpServerMock.createKibanaRequest(); + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + status: 403, + payload: { message: `Current license doesn't support access agreement.` }, + options: { body: { message: `Current license doesn't support access agreement.` } }, + }); + }); + + it('returns 500 if acknowledge throws unhandled exception.', async () => { + const unhandledException = new Error('Something went wrong.'); + authc.acknowledgeAccessAgreement.mockRejectedValue(unhandledException); + + const request = httpServerMock.createKibanaRequest(); + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + status: 500, + payload: 'Internal Error', + options: {}, + }); + }); + + it('returns 204 if successfully acknowledged.', async () => { + authc.acknowledgeAccessAgreement.mockResolvedValue(undefined); + + const request = httpServerMock.createKibanaRequest(); + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + status: 204, + options: {}, + }); + }); + }); }); diff --git a/x-pack/plugins/security/server/routes/authentication/common.ts b/x-pack/plugins/security/server/routes/authentication/common.ts index abab67c9cd1d28..91783140539a5b 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.ts @@ -18,7 +18,13 @@ import { RouteDefinitionParams } from '..'; /** * Defines routes that are common to various authentication mechanisms. */ -export function defineCommonRoutes({ router, authc, basePath, logger }: RouteDefinitionParams) { +export function defineCommonRoutes({ + router, + authc, + basePath, + license, + logger, +}: RouteDefinitionParams) { // Generate two identical routes with new and deprecated URL and issue a warning if route with deprecated URL is ever used. for (const path of ['/api/security/logout', '/api/security/v1/logout']) { router.get( @@ -135,4 +141,26 @@ export function defineCommonRoutes({ router, authc, basePath, logger }: RouteDef } }) ); + + router.post( + { path: '/internal/security/access_agreement/acknowledge', validate: false }, + createLicensedRouteHandler(async (context, request, response) => { + // If license doesn't allow access agreement we shouldn't handle request. + if (!license.getFeatures().allowAccessAgreement) { + logger.warn(`Attempted to acknowledge access agreement when license doesn't allow it.`); + return response.forbidden({ + body: { message: `Current license doesn't support access agreement.` }, + }); + } + + try { + await authc.acknowledgeAccessAgreement(request); + } catch (err) { + logger.error(err); + return response.internalError(); + } + + return response.noContent(); + }) + ); } diff --git a/x-pack/plugins/security/server/routes/licensed_route_handler.ts b/x-pack/plugins/security/server/routes/licensed_route_handler.ts index b113b2ca59e3ef..d8c212aa2d2174 100644 --- a/x-pack/plugins/security/server/routes/licensed_route_handler.ts +++ b/x-pack/plugins/security/server/routes/licensed_route_handler.ts @@ -4,10 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler } from 'kibana/server'; +import { KibanaResponseFactory, RequestHandler, RouteMethod } from 'kibana/server'; -export const createLicensedRouteHandler = (handler: RequestHandler) => { - const licensedRouteHandler: RequestHandler = (context, request, responseToolkit) => { +export const createLicensedRouteHandler = < + P, + Q, + B, + M extends RouteMethod, + R extends KibanaResponseFactory +>( + handler: RequestHandler +) => { + const licensedRouteHandler: RequestHandler = ( + context, + request, + responseToolkit + ) => { const { license } = context.licensing; const licenseCheck = license.check('security', 'basic'); if (licenseCheck.state === 'unavailable' || licenseCheck.state === 'invalid') { diff --git a/x-pack/plugins/security/server/routes/users/change_password.test.ts b/x-pack/plugins/security/server/routes/users/change_password.test.ts index fd05821f9d5206..c163ff4e256cd2 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.test.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.test.ts @@ -53,7 +53,7 @@ describe('Change password', () => { now: Date.now(), idleTimeoutExpiration: null, lifespanExpiration: null, - provider: 'basic', + provider: { type: 'basic', name: 'basic' }, }); mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts new file mode 100644 index 00000000000000..3d616575b84131 --- /dev/null +++ b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts @@ -0,0 +1,177 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, + RouteConfig, + kibanaResponseFactory, + IRouter, + HttpResources, + HttpResourcesRequestHandler, + RequestHandlerContext, +} from '../../../../../../src/core/server'; +import { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; +import { AuthenticationProvider } from '../../../common/types'; +import { ConfigType } from '../../config'; +import { defineAccessAgreementRoutes } from './access_agreement'; + +import { httpResourcesMock, httpServerMock } from '../../../../../../src/core/server/mocks'; +import { routeDefinitionParamsMock } from '../index.mock'; +import { Authentication } from '../../authentication'; + +describe('Access agreement view routes', () => { + let httpResources: jest.Mocked; + let router: jest.Mocked; + let config: ConfigType; + let authc: jest.Mocked; + let license: jest.Mocked; + let mockContext: RequestHandlerContext; + beforeEach(() => { + const routeParamsMock = routeDefinitionParamsMock.create(); + router = routeParamsMock.router; + httpResources = routeParamsMock.httpResources; + authc = routeParamsMock.authc; + config = routeParamsMock.config; + license = routeParamsMock.license; + + license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, + } as SecurityLicenseFeatures); + + mockContext = ({ + licensing: { + license: { check: jest.fn().mockReturnValue({ check: 'valid' }) }, + }, + } as unknown) as RequestHandlerContext; + + defineAccessAgreementRoutes(routeParamsMock); + }); + + describe('View route', () => { + let routeHandler: HttpResourcesRequestHandler; + let routeConfig: RouteConfig; + beforeEach(() => { + const [viewRouteConfig, viewRouteHandler] = httpResources.register.mock.calls.find( + ([{ path }]) => path === '/security/access_agreement' + )!; + + routeConfig = viewRouteConfig; + routeHandler = viewRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.validate).toBe(false); + }); + + it('does not render view if current license does not allow access agreement.', async () => { + const request = httpServerMock.createKibanaRequest(); + const responseFactory = httpResourcesMock.createResponseFactory(); + + license.getFeatures.mockReturnValue({ + allowAccessAgreement: false, + } as SecurityLicenseFeatures); + + await routeHandler(mockContext, request, responseFactory); + + expect(responseFactory.renderCoreApp).not.toHaveBeenCalledWith(); + expect(responseFactory.forbidden).toHaveBeenCalledTimes(1); + }); + + it('renders view.', async () => { + const request = httpServerMock.createKibanaRequest(); + const responseFactory = httpResourcesMock.createResponseFactory(); + + await routeHandler(mockContext, request, responseFactory); + + expect(responseFactory.renderCoreApp).toHaveBeenCalledWith(); + }); + }); + + describe('Access agreement state route', () => { + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + beforeEach(() => { + const [loginStateRouteConfig, loginStateRouteHandler] = router.get.mock.calls.find( + ([{ path }]) => path === '/internal/security/access_agreement/state' + )!; + + routeConfig = loginStateRouteConfig; + routeHandler = loginStateRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.validate).toBe(false); + }); + + it('returns `403` if current license does not allow access agreement.', async () => { + const request = httpServerMock.createKibanaRequest(); + + license.getFeatures.mockReturnValue({ + allowAccessAgreement: false, + } as SecurityLicenseFeatures); + + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + status: 403, + payload: { message: `Current license doesn't support access agreement.` }, + options: { body: { message: `Current license doesn't support access agreement.` } }, + }); + }); + + it('returns empty `accessAgreement` if session info is not available.', async () => { + const request = httpServerMock.createKibanaRequest(); + + authc.getSessionInfo.mockResolvedValue(null); + + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + options: { body: { accessAgreement: '' } }, + payload: { accessAgreement: '' }, + status: 200, + }); + }); + + it('returns non-empty `accessAgreement` only if it is configured.', async () => { + const request = httpServerMock.createKibanaRequest(); + + config.authc = routeDefinitionParamsMock.create({ + authc: { + providers: { + basic: { basic1: { order: 0 } }, + saml: { + saml1: { + order: 1, + realm: 'realm1', + accessAgreement: { message: 'Some access agreement' }, + }, + }, + }, + }, + }).config.authc; + + const cases: Array<[AuthenticationProvider, string]> = [ + [{ type: 'basic', name: 'basic1' }, ''], + [{ type: 'saml', name: 'saml1' }, 'Some access agreement'], + [{ type: 'unknown-type', name: 'unknown-name' }, ''], + ]; + + for (const [sessionProvider, expectedAccessAgreement] of cases) { + authc.getSessionInfo.mockResolvedValue({ + now: Date.now(), + idleTimeoutExpiration: null, + lifespanExpiration: null, + provider: sessionProvider, + }); + + await expect(routeHandler(mockContext, request, kibanaResponseFactory)).resolves.toEqual({ + options: { body: { accessAgreement: expectedAccessAgreement } }, + payload: { accessAgreement: expectedAccessAgreement }, + status: 200, + }); + } + }); + }); +}); diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.ts b/x-pack/plugins/security/server/routes/views/access_agreement.ts new file mode 100644 index 00000000000000..49e1ff42a28a2a --- /dev/null +++ b/x-pack/plugins/security/server/routes/views/access_agreement.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ConfigType } from '../../config'; +import { createLicensedRouteHandler } from '../licensed_route_handler'; +import { RouteDefinitionParams } from '..'; + +/** + * Defines routes required for the Access Agreement view. + */ +export function defineAccessAgreementRoutes({ + authc, + httpResources, + license, + config, + router, + logger, +}: RouteDefinitionParams) { + // If license doesn't allow access agreement we shouldn't handle request. + const canHandleRequest = () => license.getFeatures().allowAccessAgreement; + + httpResources.register( + { path: '/security/access_agreement', validate: false }, + createLicensedRouteHandler(async (context, request, response) => + canHandleRequest() + ? response.renderCoreApp() + : response.forbidden({ + body: { message: `Current license doesn't support access agreement.` }, + }) + ) + ); + + router.get( + { path: '/internal/security/access_agreement/state', validate: false }, + createLicensedRouteHandler(async (context, request, response) => { + if (!canHandleRequest()) { + return response.forbidden({ + body: { message: `Current license doesn't support access agreement.` }, + }); + } + + // It's not guaranteed that we'll have session for the authenticated user (e.g. when user is + // authenticated with the help of HTTP authentication), that means we should safely check if + // we have it and can get a corresponding configuration. + try { + const session = await authc.getSessionInfo(request); + const accessAgreement = + (session && + config.authc.providers[ + session.provider.type as keyof ConfigType['authc']['providers'] + ]?.[session.provider.name]?.accessAgreement?.message) || + ''; + + return response.ok({ body: { accessAgreement } }); + } catch (err) { + logger.error(err); + return response.internalError(); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/views/index.test.ts b/x-pack/plugins/security/server/routes/views/index.test.ts index a8e7e905b119af..7cddef9bf2b982 100644 --- a/x-pack/plugins/security/server/routes/views/index.test.ts +++ b/x-pack/plugins/security/server/routes/views/index.test.ts @@ -20,15 +20,18 @@ describe('View routes', () => { expect(routeParamsMock.httpResources.register.mock.calls.map(([{ path }]) => path)) .toMatchInlineSnapshot(` Array [ + "/security/access_agreement", "/security/account", "/security/logged_out", "/logout", "/security/overwritten_session", ] `); - expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot( - `Array []` - ); + expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(` + Array [ + "/internal/security/access_agreement/state", + ] + `); }); it('registers Login routes if `basic` provider is enabled', () => { @@ -43,6 +46,7 @@ describe('View routes', () => { .toMatchInlineSnapshot(` Array [ "/login", + "/security/access_agreement", "/security/account", "/security/logged_out", "/logout", @@ -52,6 +56,7 @@ describe('View routes', () => { expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(` Array [ "/internal/security/login_state", + "/internal/security/access_agreement/state", ] `); }); @@ -68,6 +73,7 @@ describe('View routes', () => { .toMatchInlineSnapshot(` Array [ "/login", + "/security/access_agreement", "/security/account", "/security/logged_out", "/logout", @@ -77,6 +83,7 @@ describe('View routes', () => { expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(` Array [ "/internal/security/login_state", + "/internal/security/access_agreement/state", ] `); }); @@ -93,6 +100,7 @@ describe('View routes', () => { .toMatchInlineSnapshot(` Array [ "/login", + "/security/access_agreement", "/security/account", "/security/logged_out", "/logout", @@ -102,6 +110,7 @@ describe('View routes', () => { expect(routeParamsMock.router.get.mock.calls.map(([{ path }]) => path)).toMatchInlineSnapshot(` Array [ "/internal/security/login_state", + "/internal/security/access_agreement/state", ] `); }); diff --git a/x-pack/plugins/security/server/routes/views/index.ts b/x-pack/plugins/security/server/routes/views/index.ts index 255989dfeb90cd..b9de58d47fe407 100644 --- a/x-pack/plugins/security/server/routes/views/index.ts +++ b/x-pack/plugins/security/server/routes/views/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { defineAccessAgreementRoutes } from './access_agreement'; import { defineAccountManagementRoutes } from './account_management'; import { defineLoggedOutRoutes } from './logged_out'; import { defineLoginRoutes } from './login'; @@ -20,6 +21,7 @@ export function defineViewRoutes(params: RouteDefinitionParams) { defineLoginRoutes(params); } + defineAccessAgreementRoutes(params); defineAccountManagementRoutes(params); defineLoggedOutRoutes(params); defineLogoutRoutes(params); diff --git a/x-pack/plugins/security/server/routes/views/logged_out.test.ts b/x-pack/plugins/security/server/routes/views/logged_out.test.ts index 3ff05d242d9dde..7cb73c49f9cbc8 100644 --- a/x-pack/plugins/security/server/routes/views/logged_out.test.ts +++ b/x-pack/plugins/security/server/routes/views/logged_out.test.ts @@ -39,7 +39,7 @@ describe('LoggedOut view routes', () => { it('redirects user to the root page if they have a session already.', async () => { authc.getSessionInfo.mockResolvedValue({ - provider: 'basic', + provider: { type: 'basic', name: 'basic' }, now: 0, idleTimeoutExpiration: null, lifespanExpiration: null, diff --git a/x-pack/plugins/security/server/routes/views/login.test.ts b/x-pack/plugins/security/server/routes/views/login.test.ts index d43319efbdfb9f..014ad390a3d53b 100644 --- a/x-pack/plugins/security/server/routes/views/login.test.ts +++ b/x-pack/plugins/security/server/routes/views/login.test.ts @@ -15,7 +15,7 @@ import { RouteConfig, } from '../../../../../../src/core/server'; import { SecurityLicense } from '../../../common/licensing'; -import { LoginState } from '../../../common/login_state'; +import { LoginSelectorProvider } from '../../../common/login_state'; import { ConfigType } from '../../config'; import { defineLoginRoutes } from './login'; @@ -141,6 +141,10 @@ describe('Login view routes', () => { }); describe('Login state route', () => { + function getAuthcConfig(authcConfig: Record = {}) { + return routeDefinitionParamsMock.create({ authc: { ...authcConfig } }).config.authc; + } + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { @@ -159,6 +163,7 @@ describe('Login view routes', () => { it('returns only required license features.', async () => { license.getFeatures.mockReturnValue({ + allowAccessAgreement: true, allowLogin: true, allowRbac: false, allowRoleDocumentLevelSecurity: true, @@ -176,9 +181,11 @@ describe('Login view routes', () => { const expectedPayload = { allowLogin: true, layout: 'error-es-unavailable', - showLoginForm: true, requiresSecureConnection: false, - selector: { enabled: false, providers: [] }, + selector: { + enabled: false, + providers: [{ name: 'basic', type: 'basic', usesLoginForm: true }], + }, }; await expect( routeHandler({ core: contextMock } as any, request, kibanaResponseFactory) @@ -198,9 +205,11 @@ describe('Login view routes', () => { const expectedPayload = { allowLogin: true, layout: 'form', - showLoginForm: true, requiresSecureConnection: false, - selector: { enabled: false, providers: [] }, + selector: { + enabled: false, + providers: [{ name: 'basic', type: 'basic', usesLoginForm: true }], + }, }; await expect( routeHandler({ core: contextMock } as any, request, kibanaResponseFactory) @@ -229,22 +238,46 @@ describe('Login view routes', () => { }); }); - it('returns `showLoginForm: true` only if either `basic` or `token` provider is enabled.', async () => { + it('returns `useLoginForm: true` for `basic` and `token` providers.', async () => { license.getFeatures.mockReturnValue({ allowLogin: true, showLogin: true } as any); const request = httpServerMock.createKibanaRequest(); const contextMock = coreMock.createRequestHandlerContext(); - const cases: Array<[boolean, ConfigType['authc']['sortedProviders']]> = [ - [false, []], - [true, [{ type: 'basic', name: 'basic1', options: { order: 0, showInSelector: true } }]], - [true, [{ type: 'token', name: 'token1', options: { order: 0, showInSelector: true } }]], + const cases: Array<[LoginSelectorProvider[], ConfigType['authc']]> = [ + [[], getAuthcConfig({ providers: { basic: { basic1: { order: 0, enabled: false } } } })], + [ + [ + { + name: 'basic1', + type: 'basic', + usesLoginForm: true, + icon: 'logoElastic', + description: 'Log in with Elasticsearch', + }, + ], + getAuthcConfig({ providers: { basic: { basic1: { order: 0 } } } }), + ], + [ + [ + { + name: 'token1', + type: 'token', + usesLoginForm: true, + icon: 'logoElastic', + description: 'Log in with Elasticsearch', + }, + ], + getAuthcConfig({ providers: { token: { token1: { order: 0 } } } }), + ], ]; - for (const [showLoginForm, sortedProviders] of cases) { - config.authc.sortedProviders = sortedProviders; + for (const [providers, authcConfig] of cases) { + config.authc = authcConfig; - const expectedPayload = expect.objectContaining({ showLoginForm }); + const expectedPayload = expect.objectContaining({ + selector: { enabled: false, providers }, + }); await expect( routeHandler({ core: contextMock } as any, request, kibanaResponseFactory) ).resolves.toEqual({ @@ -261,81 +294,142 @@ describe('Login view routes', () => { const request = httpServerMock.createKibanaRequest(); const contextMock = coreMock.createRequestHandlerContext(); - const cases: Array<[ - boolean, - ConfigType['authc']['sortedProviders'], - LoginState['selector']['providers'] - ]> = [ - // selector is disabled, providers shouldn't be returned. + const cases: Array<[ConfigType['authc'], LoginSelectorProvider[]]> = [ + // selector is disabled, multiple providers, but only basic provider should be returned. [ - false, + getAuthcConfig({ + selector: { enabled: false }, + providers: { + basic: { basic1: { order: 0 } }, + saml: { saml1: { order: 1, realm: 'realm1' } }, + }, + }), [ - { type: 'basic', name: 'basic1', options: { order: 0, showInSelector: true } }, - { type: 'saml', name: 'saml1', options: { order: 1, showInSelector: true } }, + { + name: 'basic1', + type: 'basic', + usesLoginForm: true, + icon: 'logoElastic', + description: 'Log in with Elasticsearch', + }, ], - [], ], - // selector is enabled, but only basic/token is available, providers shouldn't be returned. + // selector is enabled, but only basic/token is available and should be returned. [ - true, - [{ type: 'basic', name: 'basic1', options: { order: 0, showInSelector: true } }], - [], + getAuthcConfig({ + selector: { enabled: true }, + providers: { basic: { basic1: { order: 0 } } }, + }), + [ + { + name: 'basic1', + type: 'basic', + usesLoginForm: true, + icon: 'logoElastic', + description: 'Log in with Elasticsearch', + }, + ], ], - // selector is enabled, non-basic/token providers should be returned + // selector is enabled, all providers should be returned [ - true, + getAuthcConfig({ + selector: { enabled: true }, + providers: { + basic: { + basic1: { + order: 0, + description: 'some-desc1', + hint: 'some-hint1', + icon: 'logoElastic', + }, + }, + saml: { + saml1: { order: 1, description: 'some-desc2', realm: 'realm1', icon: 'some-icon2' }, + saml2: { order: 2, description: 'some-desc3', hint: 'some-hint3', realm: 'realm2' }, + }, + }, + }), [ { type: 'basic', name: 'basic1', - options: { order: 0, showInSelector: true, description: 'some-desc1' }, + description: 'some-desc1', + hint: 'some-hint1', + icon: 'logoElastic', + usesLoginForm: true, }, { type: 'saml', name: 'saml1', - options: { order: 1, showInSelector: true, description: 'some-desc2' }, + description: 'some-desc2', + icon: 'some-icon2', + usesLoginForm: false, }, { type: 'saml', name: 'saml2', - options: { order: 2, showInSelector: true, description: 'some-desc3' }, + description: 'some-desc3', + hint: 'some-hint3', + usesLoginForm: false, }, ], - [ - { type: 'saml', name: 'saml1', description: 'some-desc2' }, - { type: 'saml', name: 'saml2', description: 'some-desc3' }, - ], ], - // selector is enabled, only non-basic/token providers that are enabled in selector should be returned. + // selector is enabled, only providers that are enabled should be returned. [ - true, + getAuthcConfig({ + selector: { enabled: true }, + providers: { + basic: { + basic1: { + order: 0, + description: 'some-desc1', + hint: 'some-hint1', + icon: 'some-icon1', + }, + }, + saml: { + saml1: { + order: 1, + description: 'some-desc2', + realm: 'realm1', + showInSelector: false, + }, + saml2: { + order: 2, + description: 'some-desc3', + hint: 'some-hint3', + icon: 'some-icon3', + realm: 'realm2', + }, + }, + }, + }), [ { type: 'basic', name: 'basic1', - options: { order: 0, showInSelector: true, description: 'some-desc1' }, - }, - { - type: 'saml', - name: 'saml1', - options: { order: 1, showInSelector: false, description: 'some-desc2' }, + description: 'some-desc1', + hint: 'some-hint1', + icon: 'some-icon1', + usesLoginForm: true, }, { type: 'saml', name: 'saml2', - options: { order: 2, showInSelector: true, description: 'some-desc3' }, + description: 'some-desc3', + hint: 'some-hint3', + icon: 'some-icon3', + usesLoginForm: false, }, ], - [{ type: 'saml', name: 'saml2', description: 'some-desc3' }], ], ]; - for (const [selectorEnabled, sortedProviders, expectedProviders] of cases) { - config.authc.selector.enabled = selectorEnabled; - config.authc.sortedProviders = sortedProviders; + for (const [authcConfig, expectedProviders] of cases) { + config.authc = authcConfig; const expectedPayload = expect.objectContaining({ - selector: { enabled: selectorEnabled, providers: expectedProviders }, + selector: { enabled: authcConfig.selector.enabled, providers: expectedProviders }, }); await expect( routeHandler({ core: contextMock } as any, request, kibanaResponseFactory) diff --git a/x-pack/plugins/security/server/routes/views/login.ts b/x-pack/plugins/security/server/routes/views/login.ts index 4d6747de713f76..f72facb2e24cc9 100644 --- a/x-pack/plugins/security/server/routes/views/login.ts +++ b/x-pack/plugins/security/server/routes/views/login.ts @@ -55,15 +55,16 @@ export function defineLoginRoutes({ const { allowLogin, layout = 'form' } = license.getFeatures(); const { sortedProviders, selector } = config.authc; - let showLoginForm = false; const providers = []; - for (const { type, name, options } of sortedProviders) { - if (options.showInSelector) { - if (type === 'basic' || type === 'token') { - showLoginForm = true; - } else if (selector.enabled) { - providers.push({ type, name, description: options.description }); - } + for (const { type, name } of sortedProviders) { + // Since `config.authc.sortedProviders` is based on `config.authc.providers` config we can + // be sure that config is present for every provider in `config.authc.sortedProviders`. + const { showInSelector, description, hint, icon } = config.authc.providers[type]?.[name]!; + + // Include provider into the list if either selector is enabled or provider uses login form. + const usesLoginForm = type === 'basic' || type === 'token'; + if (showInSelector && (usesLoginForm || selector.enabled)) { + providers.push({ type, name, usesLoginForm, description, hint, icon }); } } @@ -71,7 +72,7 @@ export function defineLoginRoutes({ allowLogin, layout, requiresSecureConnection: config.secureCookies, - showLoginForm, + loginHelp: config.loginHelp, selector: { enabled: selector.enabled, providers }, }; diff --git a/x-pack/plugins/siem/server/lib/timeline/types.ts b/x-pack/plugins/siem/common/types/timeline/index.ts similarity index 85% rename from x-pack/plugins/siem/server/lib/timeline/types.ts rename to x-pack/plugins/siem/common/types/timeline/index.ts index 0bce3300591c2d..55b4f9c6aca4dc 100644 --- a/x-pack/plugins/siem/server/lib/timeline/types.ts +++ b/x-pack/plugins/siem/common/types/timeline/index.ts @@ -7,14 +7,11 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import * as runtimeTypes from 'io-ts'; +import { SavedObjectsClient } from 'kibana/server'; -import { unionWithNullType } from '../framework'; -import { NoteSavedObjectToReturnRuntimeType, NoteSavedObject } from '../note/types'; -import { - PinnedEventToReturnSavedObjectRuntimeType, - PinnedEventSavedObject, -} from '../pinned_event/types'; -import { SavedObjectsClient } from '../../../../../../src/core/server'; +import { unionWithNullType } from '../../utility_types'; +import { NoteSavedObject, NoteSavedObjectToReturnRuntimeType } from './note'; +import { PinnedEventToReturnSavedObjectRuntimeType, PinnedEventSavedObject } from './pinned_event'; /* * ColumnHeader Types @@ -136,6 +133,17 @@ const SavedSortRuntimeType = runtimeTypes.partial({ /* * Timeline Types */ + +export enum TimelineType { + default = 'default', + template = 'template', +} + +export const TimelineTypeLiteralRt = runtimeTypes.union([ + runtimeTypes.literal(TimelineType.template), + runtimeTypes.literal(TimelineType.default), +]); + export const SavedTimelineRuntimeType = runtimeTypes.partial({ columns: unionWithNullType(runtimeTypes.array(SavedColumnHeaderRuntimeType)), dataProviders: unionWithNullType(runtimeTypes.array(SavedDataProviderRuntimeType)), @@ -146,6 +154,9 @@ export const SavedTimelineRuntimeType = runtimeTypes.partial({ kqlMode: unionWithNullType(runtimeTypes.string), kqlQuery: unionWithNullType(SavedFilterQueryQueryRuntimeType), title: unionWithNullType(runtimeTypes.string), + templateTimelineId: unionWithNullType(runtimeTypes.string), + templateTimelineVersion: unionWithNullType(runtimeTypes.number), + timelineType: unionWithNullType(TimelineTypeLiteralRt), dateRange: unionWithNullType(SavedDateRangePickerRuntimeType), savedQueryId: unionWithNullType(runtimeTypes.string), sort: unionWithNullType(SavedSortRuntimeType), @@ -192,6 +203,25 @@ export const TimelineSavedToReturnObjectRuntimeType = runtimeTypes.intersection( export interface TimelineSavedObject extends runtimeTypes.TypeOf {} +/** + * All Timeline Saved object type with metadata + */ +export const TimelineResponseType = runtimeTypes.type({ + data: runtimeTypes.type({ + persistTimeline: runtimeTypes.intersection([ + runtimeTypes.partial({ + code: unionWithNullType(runtimeTypes.number), + message: unionWithNullType(runtimeTypes.string), + }), + runtimeTypes.type({ + timeline: TimelineSavedToReturnObjectRuntimeType, + }), + ]), + }), +}); + +export interface TimelineResponse extends runtimeTypes.TypeOf {} + /** * All Timeline Saved object type with metadata */ @@ -234,6 +264,11 @@ export type ExportedTimelines = TimelineSavedObject & pinnedEventIds: string[]; }; +export interface ExportTimelineNotFoundError { + statusCode: number; + message: string; +} + export interface BulkGetInput { type: string; id: string; diff --git a/x-pack/plugins/siem/server/lib/note/types.ts b/x-pack/plugins/siem/common/types/timeline/note/index.ts similarity index 90% rename from x-pack/plugins/siem/server/lib/note/types.ts rename to x-pack/plugins/siem/common/types/timeline/note/index.ts index f7a10317bd84d0..c8e674997c19c1 100644 --- a/x-pack/plugins/siem/server/lib/note/types.ts +++ b/x-pack/plugins/siem/common/types/timeline/note/index.ts @@ -8,7 +8,7 @@ import * as runtimeTypes from 'io-ts'; -import { unionWithNullType } from '../framework'; +import { unionWithNullType } from '../../../utility_types'; /* * Note Types @@ -56,11 +56,7 @@ export const NoteSavedObjectToReturnRuntimeType = runtimeTypes.intersection([ version: runtimeTypes.string, }), runtimeTypes.partial({ - timelineVersion: runtimeTypes.union([ - runtimeTypes.string, - runtimeTypes.null, - runtimeTypes.undefined, - ]), + timelineVersion: unionWithNullType(runtimeTypes.string), }), ]); diff --git a/x-pack/plugins/siem/server/lib/pinned_event/types.ts b/x-pack/plugins/siem/common/types/timeline/pinned_event/index.ts similarity index 83% rename from x-pack/plugins/siem/server/lib/pinned_event/types.ts rename to x-pack/plugins/siem/common/types/timeline/pinned_event/index.ts index e598f039350470..89a619598f7c19 100644 --- a/x-pack/plugins/siem/server/lib/pinned_event/types.ts +++ b/x-pack/plugins/siem/common/types/timeline/pinned_event/index.ts @@ -8,7 +8,7 @@ import * as runtimeTypes from 'io-ts'; -import { unionWithNullType } from '../framework'; +import { unionWithNullType } from '../../../utility_types'; /* * Note Types @@ -40,11 +40,7 @@ export const PinnedEventSavedObjectRuntimeType = runtimeTypes.intersection([ }), runtimeTypes.partial({ pinnedEventId: unionWithNullType(runtimeTypes.string), - timelineVersion: runtimeTypes.union([ - runtimeTypes.string, - runtimeTypes.null, - runtimeTypes.undefined, - ]), + timelineVersion: unionWithNullType(runtimeTypes.string), }), ]); @@ -55,11 +51,7 @@ export const PinnedEventToReturnSavedObjectRuntimeType = runtimeTypes.intersecti }), SavedPinnedEventRuntimeType, runtimeTypes.partial({ - timelineVersion: runtimeTypes.union([ - runtimeTypes.string, - runtimeTypes.null, - runtimeTypes.undefined, - ]), + timelineVersion: unionWithNullType(runtimeTypes.string), }), ]); diff --git a/x-pack/plugins/siem/common/utility_types.ts b/x-pack/plugins/siem/common/utility_types.ts index b46ccdbbe3d05a..a12dd926a91810 100644 --- a/x-pack/plugins/siem/common/utility_types.ts +++ b/x-pack/plugins/siem/common/utility_types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import * as runtimeTypes from 'io-ts'; import { ReactNode } from 'react'; // This type is for typing EuiDescriptionList @@ -11,3 +12,6 @@ export interface DescriptionList { title: NonNullable; description: NonNullable; } + +export const unionWithNullType = (type: T) => + runtimeTypes.union([type, runtimeTypes.null]); diff --git a/x-pack/plugins/siem/public/components/and_or_badge/index.tsx b/x-pack/plugins/siem/public/components/and_or_badge/index.tsx index e2078bb2473f57..28355372df1463 100644 --- a/x-pack/plugins/siem/public/components/and_or_badge/index.tsx +++ b/x-pack/plugins/siem/public/components/and_or_badge/index.tsx @@ -10,7 +10,7 @@ import styled from 'styled-components'; import * as i18n from './translations'; -const RoundedBadge = styled(EuiBadge)` +const RoundedBadge = (styled(EuiBadge)` align-items: center; border-radius: 100%; display: inline-flex; @@ -30,7 +30,7 @@ const RoundedBadge = styled(EuiBadge)` .euiBadge__text { text-overflow: clip; } -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; RoundedBadge.displayName = 'RoundedBadge'; diff --git a/x-pack/plugins/siem/public/components/embeddables/map_tool_tip/line_tool_tip_content.tsx b/x-pack/plugins/siem/public/components/embeddables/map_tool_tip/line_tool_tip_content.tsx index ef2cd856674089..7c2d5e51d813f6 100644 --- a/x-pack/plugins/siem/public/components/embeddables/map_tool_tip/line_tool_tip_content.tsx +++ b/x-pack/plugins/siem/public/components/embeddables/map_tool_tip/line_tool_tip_content.tsx @@ -17,10 +17,10 @@ import { import { FeatureProperty } from '../types'; import * as i18n from '../translations'; -const FlowBadge = styled(EuiBadge)` +const FlowBadge = (styled(EuiBadge)` height: 45px; min-width: 85px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; const EuiFlexGroupStyled = styled(EuiFlexGroup)` margin: 0 auto; diff --git a/x-pack/plugins/siem/public/components/flyout/index.tsx b/x-pack/plugins/siem/public/components/flyout/index.tsx index b0f6494e2d663c..404ca4a16e0f1c 100644 --- a/x-pack/plugins/siem/public/components/flyout/index.tsx +++ b/x-pack/plugins/siem/public/components/flyout/index.tsx @@ -18,14 +18,14 @@ import { DEFAULT_TIMELINE_WIDTH } from '../timeline/body/constants'; import { StatefulTimeline } from '../timeline'; import { TimelineById } from '../../store/timeline/types'; -export const Badge = styled(EuiBadge)` +export const Badge = (styled(EuiBadge)` position: absolute; padding-left: 4px; padding-right: 4px; right: 0%; top: 0%; border-bottom-left-radius: 5px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; diff --git a/x-pack/plugins/siem/public/components/header_page/index.tsx b/x-pack/plugins/siem/public/components/header_page/index.tsx index acbb337a7c2ab8..e1559cf9e0c48d 100644 --- a/x-pack/plugins/siem/public/components/header_page/index.tsx +++ b/x-pack/plugins/siem/public/components/header_page/index.tsx @@ -52,9 +52,9 @@ const LinkBack = styled.div.attrs({ `; LinkBack.displayName = 'LinkBack'; -const Badge = styled(EuiBadge)` +const Badge = (styled(EuiBadge)` letter-spacing: 0; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; interface BackOptions { diff --git a/x-pack/plugins/siem/public/components/header_page/title.tsx b/x-pack/plugins/siem/public/components/header_page/title.tsx index 47dd0dc55d7032..43b50c24f6b5b7 100644 --- a/x-pack/plugins/siem/public/components/header_page/title.tsx +++ b/x-pack/plugins/siem/public/components/header_page/title.tsx @@ -17,9 +17,9 @@ const StyledEuiBetaBadge = styled(EuiBetaBadge)` StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge'; -const Badge = styled(EuiBadge)` +const Badge = (styled(EuiBadge)` letter-spacing: 0; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; interface Props { diff --git a/x-pack/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap b/x-pack/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap index 5aa846d15b684b..c4bdff7ea649a7 100644 --- a/x-pack/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/siem/public/components/matrix_histogram/__snapshots__/index.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Matrix Histogram Component not initial load it renders no MatrixLoader 1`] = `"
"`; +exports[`Matrix Histogram Component not initial load it renders no MatrixLoader 1`] = `"
"`; -exports[`Matrix Histogram Component on initial load it renders MatrixLoader 1`] = `"
"`; +exports[`Matrix Histogram Component on initial load it renders MatrixLoader 1`] = `"
"`; diff --git a/x-pack/plugins/siem/public/components/open_timeline/export_timeline/index.tsx b/x-pack/plugins/siem/public/components/open_timeline/export_timeline/index.tsx index 946c4b3a612dd1..12cf952bb1ff86 100644 --- a/x-pack/plugins/siem/public/components/open_timeline/export_timeline/index.tsx +++ b/x-pack/plugins/siem/public/components/open_timeline/export_timeline/index.tsx @@ -9,7 +9,7 @@ import { DeleteTimelines } from '../types'; import { TimelineDownloader } from './export_timeline'; import { DeleteTimelineModalOverlay } from '../delete_timeline_modal'; -import { exportSelectedTimeline } from '../../../containers/timeline/all/api'; +import { exportSelectedTimeline } from '../../../containers/timeline/api'; export interface ExportTimeline { disableExportTimelineDownloader: () => void; diff --git a/x-pack/plugins/siem/public/components/open_timeline/index.test.tsx b/x-pack/plugins/siem/public/components/open_timeline/index.test.tsx index 04f0abe0d00d17..ea28bc06ef915b 100644 --- a/x-pack/plugins/siem/public/components/open_timeline/index.test.tsx +++ b/x-pack/plugins/siem/public/components/open_timeline/index.test.tsx @@ -15,15 +15,34 @@ import { TestProviderWithoutDragAndDrop, apolloClient } from '../../mock/test_pr import { mockOpenTimelineQueryResults } from '../../mock/timeline_results'; import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines/timelines_page'; -import { StatefulOpenTimeline } from '.'; import { NotePreviews } from './note_previews'; import { OPEN_TIMELINE_CLASS_NAME } from './helpers'; - +import { StatefulOpenTimeline } from '.'; +import { useGetAllTimeline, getAllTimeline } from '../../containers/timeline/all'; jest.mock('../../lib/kibana'); +jest.mock('../../containers/timeline/all', () => { + const originalModule = jest.requireActual('../../containers/timeline/all'); + return { + useGetAllTimeline: jest.fn(), + getAllTimeline: originalModule.getAllTimeline, + }; +}); describe('StatefulOpenTimeline', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const title = 'All Timelines / Open Timelines'; + beforeEach(() => { + ((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({ + fetchAllTimeline: jest.fn(), + timelines: getAllTimeline( + '', + mockOpenTimelineQueryResults[0].result.data?.getAllTimeline?.timeline ?? [] + ), + loading: false, + totalCount: mockOpenTimelineQueryResults[0].result.data.getAllTimeline.totalCount, + refetch: jest.fn(), + }); + }); test('it has the expected initial state', () => { const wrapper = mount( @@ -459,6 +478,8 @@ describe('StatefulOpenTimeline', () => { .find('[data-test-subj="expand-notes"]') .first() .simulate('click'); + expect(wrapper.find('[data-test-subj="note-previews-container"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="updated-by"]').exists()).toEqual(true); expect( wrapper @@ -532,7 +553,7 @@ describe('StatefulOpenTimeline', () => { test('it renders the expected count of matching timelines when no query has been entered', async () => { const wrapper = mount( - + ( /** The requested field to sort on */ const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); + const { fetchAllTimeline, timelines, loading, totalCount, refetch } = useGetAllTimeline(); + /** Invoked when the user presses enters to submit the text in the search input */ const onQueryChange: OnQueryChange = useCallback((query: EuiSearchBarQuery) => { setSearch(query.queryText.trim()); @@ -133,45 +134,41 @@ export const StatefulOpenTimelineComponent = React.memo( // } // }; - const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback( - (timelineIds: string[]) => { - deleteTimelines(timelineIds, { - search, - pageInfo: { - pageIndex: pageIndex + 1, - pageSize, - }, - sort: { - sortField: sortField as SortFieldTimeline, - sortOrder: sortDirection as Direction, - }, - onlyUserFavorite: onlyFavorites, + const deleteTimelines: DeleteTimelines = useCallback( + async (timelineIds: string[]) => { + if (timelineIds.includes(timeline.savedObjectId || '')) { + createNewTimeline({ id: 'timeline-1', columns: defaultHeaders, show: false }); + } + await apolloClient.mutate< + DeleteTimelineMutation.Mutation, + DeleteTimelineMutation.Variables + >({ + mutation: deleteTimelineMutation, + fetchPolicy: 'no-cache', + variables: { id: timelineIds }, }); + refetch(); + }, + [apolloClient, createNewTimeline, refetch, timeline] + ); + + const onDeleteOneTimeline: OnDeleteOneTimeline = useCallback( + async (timelineIds: string[]) => { + await deleteTimelines(timelineIds); }, - [search, pageIndex, pageSize, sortField, sortDirection, onlyFavorites] + [deleteTimelines] ); /** Invoked when the user clicks the action to delete the selected timelines */ - const onDeleteSelected: OnDeleteSelected = useCallback(() => { - deleteTimelines(getSelectedTimelineIds(selectedItems), { - search, - pageInfo: { - pageIndex: pageIndex + 1, - pageSize, - }, - sort: { - sortField: sortField as SortFieldTimeline, - sortOrder: sortDirection as Direction, - }, - onlyUserFavorite: onlyFavorites, - }); + const onDeleteSelected: OnDeleteSelected = useCallback(async () => { + await deleteTimelines(getSelectedTimelineIds(selectedItems)); // NOTE: we clear the selection state below, but if the server fails to // delete a timeline, it will remain selected in the table: resetSelectionState(); // TODO: the query must re-execute to show the results of the deletion - }, [selectedItems, search, pageIndex, pageSize, sortField, sortDirection, onlyFavorites]); + }, [selectedItems, deleteTimelines]); /** Invoked when the user selects (or de-selects) timelines */ const onSelectionChange: OnSelectionChange = useCallback( @@ -227,99 +224,88 @@ export const StatefulOpenTimelineComponent = React.memo( [apolloClient, updateIsLoading, updateTimeline] ); - const deleteTimelines: DeleteTimelines = useCallback( - (timelineIds: string[], variables?: AllTimelinesVariables) => { - if (timelineIds.includes(timeline.savedObjectId || '')) { - createNewTimeline({ id: 'timeline-1', columns: defaultHeaders, show: false }); - } - apolloClient.mutate({ - mutation: deleteTimelineMutation, - fetchPolicy: 'no-cache', - variables: { id: timelineIds }, - refetchQueries: [ - { - query: allTimelinesQuery, - variables, - }, - ], - }); - }, - [apolloClient, createNewTimeline, timeline] - ); - useEffect(() => { focusInput(); }, []); - return ( - { + fetchAllTimeline({ + pageInfo: { pageIndex: pageIndex + 1, pageSize, - }} - search={search} - sort={{ sortField: sortField as SortFieldTimeline, sortOrder: sortDirection as Direction }} - onlyUserFavorite={onlyFavorites} - > - {({ timelines, loading, totalCount, refetch }) => { - return !isModal ? ( - - ) : ( - - ); - }} - + }, + search, + sort: { sortField: sortField as SortFieldTimeline, sortOrder: sortDirection as Direction }, + onlyUserFavorite: onlyFavorites, + timelines, + totalCount, + }); + }, [ + pageIndex, + pageSize, + search, + sortField, + sortDirection, + timelines, + totalCount, + onlyFavorites, + ]); + + return !isModal ? ( + + ) : ( + ); } ); @@ -328,7 +314,6 @@ const makeMapStateToProps = () => { const getTimeline = timelineSelectors.getTimelineByIdSelector(); const mapStateToProps = (state: State) => { const timeline = getTimeline(state, 'timeline-1') ?? timelineDefaults; - return { timeline, }; diff --git a/x-pack/plugins/siem/public/components/open_timeline/open_timeline.tsx b/x-pack/plugins/siem/public/components/open_timeline/open_timeline.tsx index 6b2f953b82de42..26aeab87e3510c 100644 --- a/x-pack/plugins/siem/public/components/open_timeline/open_timeline.tsx +++ b/x-pack/plugins/siem/public/components/open_timeline/open_timeline.tsx @@ -14,7 +14,7 @@ import { TimelinesTable } from './timelines_table'; import { TitleRow } from './title_row'; import { ImportDataModal } from '../import_data_modal'; import * as i18n from './translations'; -import { importTimelines } from '../../containers/timeline/all/api'; +import { importTimelines } from '../../containers/timeline/api'; import { UtilityBarGroup, diff --git a/x-pack/plugins/siem/public/components/open_timeline/open_timeline_modal/index.test.tsx b/x-pack/plugins/siem/public/components/open_timeline/open_timeline_modal/index.test.tsx index ca8fa50c572fe3..46a0d46c1e0d16 100644 --- a/x-pack/plugins/siem/public/components/open_timeline/open_timeline_modal/index.test.tsx +++ b/x-pack/plugins/siem/public/components/open_timeline/open_timeline_modal/index.test.tsx @@ -13,6 +13,7 @@ import { ThemeProvider } from 'styled-components'; import { wait } from '../../../lib/helpers'; import { TestProviderWithoutDragAndDrop } from '../../../mock/test_providers'; import { mockOpenTimelineQueryResults } from '../../../mock/timeline_results'; +import { useGetAllTimeline, getAllTimeline } from '../../../containers/timeline/all'; import { OpenTimelineModal } from '.'; @@ -20,9 +21,28 @@ jest.mock('../../../lib/kibana'); jest.mock('../../../utils/apollo_context', () => ({ useApolloClient: () => ({}), })); +jest.mock('../../../containers/timeline/all', () => { + const originalModule = jest.requireActual('../../../containers/timeline/all'); + return { + useGetAllTimeline: jest.fn(), + getAllTimeline: originalModule.getAllTimeline, + }; +}); describe('OpenTimelineModal', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); + beforeEach(() => { + ((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({ + fetchAllTimeline: jest.fn(), + timelines: getAllTimeline( + '', + mockOpenTimelineQueryResults[0].result.data?.getAllTimeline?.timeline ?? [] + ), + loading: false, + totalCount: mockOpenTimelineQueryResults[0].result.data.getAllTimeline.totalCount, + refetch: jest.fn(), + }); + }); test('it renders the expected modal', async () => { const wrapper = mount( diff --git a/x-pack/plugins/siem/public/components/open_timeline/types.ts b/x-pack/plugins/siem/public/components/open_timeline/types.ts index b7cc92ebd183f1..41999c62492776 100644 --- a/x-pack/plugins/siem/public/components/open_timeline/types.ts +++ b/x-pack/plugins/siem/public/components/open_timeline/types.ts @@ -9,6 +9,7 @@ import { AllTimelinesVariables } from '../../containers/timeline/all'; import { TimelineModel } from '../../store/timeline/model'; import { NoteResult } from '../../graphql/types'; import { Refetch } from '../../store/inputs/model'; +import { TimelineType } from '../../../common/types/timeline'; /** The users who added a timeline to favorites */ export interface FavoriteTimelineResult { @@ -47,6 +48,8 @@ export interface OpenTimelineResult { pinnedEventIds?: Readonly> | null; savedObjectId?: string | null; title?: string | null; + templateTimelineId?: string | null; + type?: TimelineType.template | TimelineType.default; updated?: number | null; updatedBy?: string | null; } diff --git a/x-pack/plugins/siem/public/components/page/index.tsx b/x-pack/plugins/siem/public/components/page/index.tsx index 44dc75cd6bad37..5feb2ef73c57f3 100644 --- a/x-pack/plugins/siem/public/components/page/index.tsx +++ b/x-pack/plugins/siem/public/components/page/index.tsx @@ -165,9 +165,9 @@ export const Pane1FlexContent = styled.div` Pane1FlexContent.displayName = 'Pane1FlexContent'; -export const CountBadge = styled(EuiBadge)` +export const CountBadge = (styled(EuiBadge)` margin-left: 5px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; CountBadge.displayName = 'CountBadge'; @@ -177,9 +177,9 @@ export const Spacer = styled.span` Spacer.displayName = 'Spacer'; -export const Badge = styled(EuiBadge)` +export const Badge = (styled(EuiBadge)` vertical-align: top; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; diff --git a/x-pack/plugins/siem/public/components/recent_timelines/index.tsx b/x-pack/plugins/siem/public/components/recent_timelines/index.tsx index 5b851701b973c4..b641038f35ba6d 100644 --- a/x-pack/plugins/siem/public/components/recent_timelines/index.tsx +++ b/x-pack/plugins/siem/public/components/recent_timelines/index.tsx @@ -6,11 +6,11 @@ import ApolloClient from 'apollo-client'; import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; -import { AllTimelinesQuery } from '../../containers/timeline/all'; +import { useGetAllTimeline } from '../../containers/timeline/all'; import { SortFieldTimeline, Direction } from '../../graphql/types'; import { queryTimelineById, dispatchUpdateTimeline } from '../open_timeline/helpers'; import { OnOpenTimeline } from '../open_timeline/types'; @@ -62,35 +62,39 @@ const StatefulRecentTimelinesComponent = React.memo( [filterBy] ); - return ( - { + fetchAllTimeline({ + pageInfo: { pageIndex: 1, pageSize: PAGE_SIZE, - }} - search={''} - sort={{ + }, + search: '', + sort: { sortField: SortFieldTimeline.updated, sortOrder: Direction.desc, - }} - onlyUserFavorite={filterBy === 'favorites'} - > - {({ timelines, loading }) => ( - <> - {loading ? ( - loadingPlaceholders - ) : ( - - )} - - {linkAllTimelines} - + }, + onlyUserFavorite: filterBy === 'favorites', + timelines, + totalCount, + }); + }, [filterBy, timelines, totalCount]); + + return ( + <> + {loading ? ( + loadingPlaceholders + ) : ( + )} - + + {linkAllTimelines} + ); } ); diff --git a/x-pack/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx index d05ab6bcc378f9..66c559729cccdf 100644 --- a/x-pack/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx +++ b/x-pack/plugins/siem/public/components/timeline/body/renderers/suricata/suricata_signature.tsx @@ -28,9 +28,9 @@ const SignatureFlexItem = styled(EuiFlexItem)` SignatureFlexItem.displayName = 'SignatureFlexItem'; -const Badge = styled(EuiBadge)` +const Badge = (styled(EuiBadge)` vertical-align: top; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; diff --git a/x-pack/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx index 31525a4904bc23..4cb8140e22ceff 100644 --- a/x-pack/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/plugins/siem/public/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -19,9 +19,9 @@ import { IS_OPERATOR } from '../../../data_providers/data_provider'; import * as i18n from './translations'; -const Badge = styled(EuiBadge)` +const Badge = (styled(EuiBadge)` vertical-align: top; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; Badge.displayName = 'Badge'; diff --git a/x-pack/plugins/siem/public/components/timeline/data_providers/empty.tsx b/x-pack/plugins/siem/public/components/timeline/data_providers/empty.tsx index ddb07b4636b886..60c868f780ff3d 100644 --- a/x-pack/plugins/siem/public/components/timeline/data_providers/empty.tsx +++ b/x-pack/plugins/siem/public/components/timeline/data_providers/empty.tsx @@ -21,12 +21,12 @@ const Text = styled(EuiText)` Text.displayName = 'Text'; -const BadgeHighlighted = styled(EuiBadge)` +const BadgeHighlighted = (styled(EuiBadge)` height: 20px; margin: 0 5px 0 5px; maxwidth: 85px; minwidth: 85px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; BadgeHighlighted.displayName = 'BadgeHighlighted'; diff --git a/x-pack/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx b/x-pack/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx index a5525df5ef3c36..e04aed17c6d671 100644 --- a/x-pack/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx +++ b/x-pack/plugins/siem/public/components/timeline/data_providers/provider_badge.tsx @@ -18,7 +18,7 @@ import { EXISTS_OPERATOR, QueryOperator } from './data_provider'; import * as i18n from './translations'; -const ProviderBadgeStyled = styled(EuiBadge)` +const ProviderBadgeStyled = (styled(EuiBadge)` .euiToolTipAnchor { &::after { font-style: normal; @@ -44,7 +44,7 @@ const ProviderBadgeStyled = styled(EuiBadge)` margin-right: 0; margin-left: 4px; } -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; ProviderBadgeStyled.displayName = 'ProviderBadgeStyled'; diff --git a/x-pack/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx b/x-pack/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx index 0ae952412a973d..3a691d2bbc6219 100644 --- a/x-pack/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx +++ b/x-pack/plugins/siem/public/components/timeline/data_providers/provider_item_and_drag_drop.tsx @@ -54,9 +54,9 @@ const DropAndTargetDataProviders = styled.div<{ hasAndItem: boolean }>` DropAndTargetDataProviders.displayName = 'DropAndTargetDataProviders'; -const NumberProviderAndBadge = styled(EuiBadge)` +const NumberProviderAndBadge = (styled(EuiBadge)` margin: 0px 5px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; NumberProviderAndBadge.displayName = 'NumberProviderAndBadge'; diff --git a/x-pack/plugins/siem/public/components/timeline/properties/helpers.tsx b/x-pack/plugins/siem/public/components/timeline/properties/helpers.tsx index bf953cfd006aa5..4c64c8a100b41a 100644 --- a/x-pack/plugins/siem/public/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/siem/public/components/timeline/properties/helpers.tsx @@ -37,9 +37,9 @@ export const historyToolTip = 'The chronological history of actions related to t export const streamLiveToolTip = 'Update the Timeline as new data arrives'; export const newTimelineToolTip = 'Create a new timeline'; -const NotesCountBadge = styled(EuiBadge)` +const NotesCountBadge = (styled(EuiBadge)` margin-left: 5px; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; NotesCountBadge.displayName = 'NotesCountBadge'; diff --git a/x-pack/plugins/siem/public/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/siem/public/components/timeline/selectable_timeline/index.tsx index 639d30bbe7bb90..4cc89e5bdba73d 100644 --- a/x-pack/plugins/siem/public/components/timeline/selectable_timeline/index.tsx +++ b/x-pack/plugins/siem/public/components/timeline/selectable_timeline/index.tsx @@ -17,11 +17,11 @@ import { EuiFilterButton, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { memo, useCallback, useMemo, useState } from 'react'; +import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; import { ListProps } from 'react-virtualized'; import styled from 'styled-components'; -import { AllTimelinesQuery } from '../../../containers/timeline/all'; +import { useGetAllTimeline } from '../../../containers/timeline/all'; import { SortFieldTimeline, Direction } from '../../../graphql/types'; import { isUntitled } from '../../open_timeline/helpers'; import * as i18nTimeline from '../../open_timeline/translations'; @@ -96,6 +96,7 @@ const SelectableTimelineComponent: React.FC = ({ const [searchTimelineValue, setSearchTimelineValue] = useState(''); const [onlyFavorites, setOnlyFavorites] = useState(false); const [searchRef, setSearchRef] = useState(null); + const { fetchAllTimeline, timelines, loading, totalCount: timelineCount } = useGetAllTimeline(); const onSearchTimeline = useCallback(val => { setSearchTimelineValue(val); @@ -215,61 +216,64 @@ const SelectableTimelineComponent: React.FC = ({ [searchRef, onlyFavorites, handleOnToggleOnlyFavorites] ); + useEffect(() => { + fetchAllTimeline({ + pageInfo: { + pageIndex: 1, + pageSize, + }, + search: searchTimelineValue, + sort: { + sortField: SortFieldTimeline.updated, + sortOrder: Direction.desc, + }, + onlyUserFavorite: onlyFavorites, + timelines, + totalCount: timelineCount, + }); + }, [onlyFavorites, pageSize, searchTimelineValue, timelines, timelineCount]); + return ( - <> - + !hideUntitled || t.title !== '').length, + timelineCount + ), + } as unknown) as ListProps, + }} + renderOption={renderTimelineOption} + onChange={handleTimelineChange} + searchable + searchProps={{ + 'data-test-subj': 'timeline-super-select-search-box', + isLoading: loading, + placeholder: i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER, + onSearch: onSearchTimeline, + incremental: false, + inputRef: (ref: HTMLElement) => { + setSearchRef(ref); + }, }} - search={searchTimelineValue} - sort={{ sortField: SortFieldTimeline.updated, sortOrder: Direction.desc }} - onlyUserFavorite={onlyFavorites} + singleSelection={true} + options={getSelectableOptions({ timelines, onlyFavorites, searchTimelineValue })} > - {({ timelines, loading, totalCount }) => ( - - !hideUntitled || t.title !== '').length, - totalCount - ), - } as unknown) as ListProps, - }} - renderOption={renderTimelineOption} - onChange={handleTimelineChange} - searchable - searchProps={{ - 'data-test-subj': 'timeline-super-select-search-box', - isLoading: loading, - placeholder: i18n.SEARCH_BOX_TIMELINE_PLACEHOLDER, - onSearch: onSearchTimeline, - incremental: false, - inputRef: (ref: HTMLElement) => { - setSearchRef(ref); - }, - }} - singleSelection={true} - options={getSelectableOptions({ timelines, onlyFavorites, searchTimelineValue })} - > - {(list, search) => ( - <> - {search} - {favoritePortal} - {list} - - )} - - + {(list, search) => ( + <> + {search} + {favoritePortal} + {list} + )} - - + + ); }; diff --git a/x-pack/plugins/siem/public/containers/timeline/all/api.ts b/x-pack/plugins/siem/public/containers/timeline/all/api.ts deleted file mode 100644 index 09c8374bad1138..00000000000000 --- a/x-pack/plugins/siem/public/containers/timeline/all/api.ts +++ /dev/null @@ -1,48 +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 { TIMELINE_IMPORT_URL, TIMELINE_EXPORT_URL } from '../../../../common/constants'; -import { ImportDataProps, ImportDataResponse } from '../../detection_engine/rules'; -import { KibanaServices } from '../../../lib/kibana'; -import { ExportSelectedData } from '../../../components/generic_downloader'; - -export const importTimelines = async ({ - fileToImport, - overwrite = false, - signal, -}: ImportDataProps): Promise => { - const formData = new FormData(); - formData.append('file', fileToImport); - - return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, { - method: 'POST', - headers: { 'Content-Type': undefined }, - query: { overwrite }, - body: formData, - signal, - }); -}; - -export const exportSelectedTimeline: ExportSelectedData = async ({ - excludeExportDetails = false, - filename = `timelines_export.ndjson`, - ids = [], - signal, -}): Promise => { - const body = ids.length > 0 ? JSON.stringify({ ids }) : undefined; - const response = await KibanaServices.get().http.fetch(`${TIMELINE_EXPORT_URL}`, { - method: 'POST', - body, - query: { - exclude_export_details: excludeExportDetails, - file_name: filename, - }, - signal, - asResponse: true, - }); - - return response.body!; -}; diff --git a/x-pack/plugins/siem/public/containers/timeline/all/index.gql_query.ts b/x-pack/plugins/siem/public/containers/timeline/all/index.gql_query.ts index e380e46e77070c..7d30b6c22a1100 100644 --- a/x-pack/plugins/siem/public/containers/timeline/all/index.gql_query.ts +++ b/x-pack/plugins/siem/public/containers/timeline/all/index.gql_query.ts @@ -55,6 +55,9 @@ export const allTimelinesQuery = gql` noteIds pinnedEventIds title + timelineType + templateTimelineId + templateTimelineVersion created createdBy updated diff --git a/x-pack/plugins/siem/public/containers/timeline/all/index.tsx b/x-pack/plugins/siem/public/containers/timeline/all/index.tsx index b5c91ca287f0b8..62c8d21a2e9448 100644 --- a/x-pack/plugins/siem/public/containers/timeline/all/index.tsx +++ b/x-pack/plugins/siem/public/containers/timeline/all/index.tsx @@ -3,23 +3,28 @@ * 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 { getOr } from 'lodash/fp'; -import memoizeOne from 'memoize-one'; -import { Query } from 'react-apollo'; +import { getOr, noop } from 'lodash/fp'; +import memoizeOne from 'memoize-one'; +import { useCallback, useState, useRef, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; -import { ApolloQueryResult } from 'apollo-client'; import { OpenTimelineResult } from '../../../components/open_timeline/types'; +import { errorToToaster, useStateToaster } from '../../../components/toasters'; import { GetAllTimeline, PageInfoTimeline, SortTimeline, TimelineResult, } from '../../../graphql/types'; +import { inputsModel, inputsActions } from '../../../store/inputs'; +import { useApolloClient } from '../../../utils/apollo_context'; + import { allTimelinesQuery } from './index.gql_query'; +import * as i18n from '../../../pages/timelines/translations'; export interface AllTimelinesArgs { + fetchAllTimeline: ({ onlyUserFavorite, pageInfo, search, sort }: AllTimelinesVariables) => void; timelines: OpenTimelineResult[]; loading: boolean; totalCount: number; @@ -31,17 +36,13 @@ export interface AllTimelinesVariables { pageInfo: PageInfoTimeline; search: string; sort: SortTimeline; + timelines: OpenTimelineResult[]; + totalCount: number; } -interface OwnProps extends AllTimelinesVariables { - children?: (args: AllTimelinesArgs) => React.ReactNode; -} - -type Refetch = ( - variables: GetAllTimeline.Variables | undefined -) => Promise>; +export const ALL_TIMELINE_QUERY_ID = 'FETCH_ALL_TIMELINES'; -const getAllTimeline = memoizeOne( +export const getAllTimeline = memoizeOne( (variables: string, timelines: TimelineResult[]): OpenTimelineResult[] => timelines.map(timeline => ({ created: timeline.created, @@ -76,41 +77,117 @@ const getAllTimeline = memoizeOne( })) ); -const AllTimelinesQueryComponent: React.FC = ({ - children, - onlyUserFavorite, - pageInfo, - search, - sort, -}) => { - const variables: GetAllTimeline.Variables = { - onlyUserFavorite, - pageInfo, - search, - sort, - }; - const handleRefetch = useCallback((refetch: Refetch) => refetch(variables), [variables]); +export const useGetAllTimeline = (): AllTimelinesArgs => { + const dispatch = useDispatch(); + const apolloClient = useApolloClient(); + const refetch = useRef(); + const [, dispatchToaster] = useStateToaster(); + const [allTimelines, setAllTimelines] = useState({ + fetchAllTimeline: noop, + loading: false, + refetch: refetch.current ?? noop, + totalCount: 0, + timelines: [], + }); + + const fetchAllTimeline = useCallback( + async ({ + onlyUserFavorite, + pageInfo, + search, + sort, + timelines, + totalCount, + }: AllTimelinesVariables) => { + let didCancel = false; + const abortCtrl = new AbortController(); - return ( - - query={allTimelinesQuery} - fetchPolicy="network-only" - notifyOnNetworkStatusChange - variables={variables} - > - {({ data, loading, refetch }) => - children!({ - loading, - refetch: handleRefetch.bind(null, refetch), - totalCount: getOr(0, 'getAllTimeline.totalCount', data), - timelines: getAllTimeline( - JSON.stringify(variables), - getOr([], 'getAllTimeline.timeline', data) - ), - }) - } - + const fetchData = async () => { + try { + if (apolloClient != null) { + setAllTimelines({ + ...allTimelines, + timelines: timelines ?? allTimelines.timelines, + totalCount: totalCount ?? allTimelines.totalCount, + loading: true, + }); + const variables: GetAllTimeline.Variables = { + onlyUserFavorite, + pageInfo, + search, + sort, + }; + const response = await apolloClient.query< + GetAllTimeline.Query, + GetAllTimeline.Variables + >({ + query: allTimelinesQuery, + fetchPolicy: 'network-only', + variables, + context: { + fetchOptions: { + abortSignal: abortCtrl.signal, + }, + }, + }); + if (!didCancel) { + dispatch( + inputsActions.setQuery({ + inputId: 'global', + id: ALL_TIMELINE_QUERY_ID, + loading: false, + refetch: refetch.current ?? noop, + inspect: null, + }) + ); + setAllTimelines({ + fetchAllTimeline, + loading: false, + refetch: refetch.current ?? noop, + totalCount: getOr(0, 'getAllTimeline.totalCount', response.data), + timelines: getAllTimeline( + JSON.stringify(variables), + getOr([], 'getAllTimeline.timeline', response.data) + ), + }); + } + } + } catch (error) { + if (!didCancel) { + errorToToaster({ + title: i18n.ERROR_FETCHING_TIMELINES_TITLE, + error: error.body && error.body.message ? new Error(error.body.message) : error, + dispatchToaster, + }); + setAllTimelines({ + fetchAllTimeline, + loading: false, + refetch: noop, + totalCount: 0, + timelines: [], + }); + } + } + }; + refetch.current = fetchData; + fetchData(); + return () => { + didCancel = true; + abortCtrl.abort(); + }; + }, + [apolloClient, allTimelines] ); -}; -export const AllTimelinesQuery = React.memo(AllTimelinesQueryComponent); + useEffect(() => { + return () => { + dispatch(inputsActions.deleteOneQuery({ inputId: 'global', id: ALL_TIMELINE_QUERY_ID })); + }; + }, [dispatch]); + + return { + ...allTimelines, + fetchAllTimeline, + refetch: refetch.current ?? noop, + }; +}; diff --git a/x-pack/plugins/siem/public/containers/timeline/api.ts b/x-pack/plugins/siem/public/containers/timeline/api.ts new file mode 100644 index 00000000000000..023e2e6af9f88e --- /dev/null +++ b/x-pack/plugins/siem/public/containers/timeline/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 { throwErrors } from '../../../../case/common/api'; +import { + SavedTimeline, + TimelineResponse, + TimelineResponseType, +} from '../../../common/types/timeline'; +import { TIMELINE_URL, TIMELINE_IMPORT_URL, TIMELINE_EXPORT_URL } from '../../../common/constants'; + +import { KibanaServices } from '../../lib/kibana'; +import { ExportSelectedData } from '../../components/generic_downloader'; + +import { createToasterPlainError } from '../case/utils'; +import { ImportDataProps, ImportDataResponse } from '../detection_engine/rules'; + +interface RequestPostTimeline { + timeline: SavedTimeline; + signal?: AbortSignal; +} + +interface RequestPatchTimeline extends RequestPostTimeline { + timelineId: T; + version: T; +} + +type RequestPersistTimeline = RequestPostTimeline & Partial>; + +const decodeTimelineResponse = (respTimeline?: TimelineResponse) => + pipe( + TimelineResponseType.decode(respTimeline), + fold(throwErrors(createToasterPlainError), identity) + ); + +const postTimeline = async ({ timeline }: RequestPostTimeline): Promise => { + const response = await KibanaServices.get().http.post(TIMELINE_URL, { + method: 'POST', + body: JSON.stringify({ timeline }), + }); + + return decodeTimelineResponse(response); +}; + +const patchTimeline = async ({ + timelineId, + timeline, + version, +}: RequestPatchTimeline): Promise => { + const response = await KibanaServices.get().http.patch(TIMELINE_URL, { + method: 'PATCH', + body: JSON.stringify({ timeline, timelineId, version }), + }); + + return decodeTimelineResponse(response); +}; + +export const persistTimeline = async ({ + timelineId, + timeline, + version, +}: RequestPersistTimeline): Promise => { + if (timelineId == null) { + return postTimeline({ timeline }); + } + return patchTimeline({ + timelineId, + timeline, + version: version ?? '', + }); +}; + +export const importTimelines = async ({ + fileToImport, + overwrite = false, + signal, +}: ImportDataProps): Promise => { + const formData = new FormData(); + formData.append('file', fileToImport); + + return KibanaServices.get().http.fetch(`${TIMELINE_IMPORT_URL}`, { + method: 'POST', + headers: { 'Content-Type': undefined }, + query: { overwrite }, + body: formData, + signal, + }); +}; + +export const exportSelectedTimeline: ExportSelectedData = async ({ + excludeExportDetails = false, + filename = `timelines_export.ndjson`, + ids = [], + signal, +}): Promise => { + const body = ids.length > 0 ? JSON.stringify({ ids }) : undefined; + const response = await KibanaServices.get().http.fetch(`${TIMELINE_EXPORT_URL}`, { + method: 'POST', + body, + query: { + exclude_export_details: excludeExportDetails, + file_name: filename, + }, + signal, + asResponse: true, + }); + + return response.body!; +}; diff --git a/x-pack/plugins/siem/public/graphql/introspection.json b/x-pack/plugins/siem/public/graphql/introspection.json index 2a9dd8f2aacfe8..4026a043c7778c 100644 --- a/x-pack/plugins/siem/public/graphql/introspection.json +++ b/x-pack/plugins/siem/public/graphql/introspection.json @@ -9728,6 +9728,30 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "templateTimelineId", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "templateTimelineVersion", + "description": "", + "args": [], + "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "timelineType", + "description": "", + "args": [], + "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "updated", "description": "", @@ -10323,6 +10347,39 @@ "enumValues": 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", + "description": "", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "template", + "description": "", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "PageInfoTimeline", @@ -10863,6 +10920,24 @@ "type": { "kind": "SCALAR", "name": "String", "ofType": null }, "defaultValue": null }, + { + "name": "templateTimelineId", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "templateTimelineVersion", + "description": "", + "type": { "kind": "SCALAR", "name": "Int", "ofType": null }, + "defaultValue": null + }, + { + "name": "timelineType", + "description": "", + "type": { "kind": "ENUM", "name": "TimelineType", "ofType": null }, + "defaultValue": null + }, { "name": "dateRange", "description": "", diff --git a/x-pack/plugins/siem/public/graphql/types.ts b/x-pack/plugins/siem/public/graphql/types.ts index e15c099a007ad3..8c39d5e58b99e1 100644 --- a/x-pack/plugins/siem/public/graphql/types.ts +++ b/x-pack/plugins/siem/public/graphql/types.ts @@ -132,6 +132,12 @@ export interface TimelineInput { title?: Maybe; + templateTimelineId?: Maybe; + + templateTimelineVersion?: Maybe; + + timelineType?: Maybe; + dateRange?: Maybe; savedQueryId?: Maybe; @@ -334,6 +340,11 @@ export enum TlsFields { _id = '_id', } +export enum TimelineType { + default = 'default', + template = 'template', +} + export enum SortFieldTimeline { title = 'title', description = 'description', @@ -1944,6 +1955,12 @@ export interface TimelineResult { title?: Maybe; + templateTimelineId?: Maybe; + + templateTimelineVersion?: Maybe; + + timelineType?: Maybe; + updated?: Maybe; updatedBy?: Maybe; @@ -4030,6 +4047,12 @@ export namespace GetAllTimeline { title: Maybe; + timelineType: Maybe; + + templateTimelineId: Maybe; + + templateTimelineVersion: Maybe; + created: Maybe; createdBy: Maybe; diff --git a/x-pack/plugins/siem/public/lib/telemetry/middleware.ts b/x-pack/plugins/siem/public/lib/telemetry/middleware.ts index 59c6cb35669070..ca889e20e695f3 100644 --- a/x-pack/plugins/siem/public/lib/telemetry/middleware.ts +++ b/x-pack/plugins/siem/public/lib/telemetry/middleware.ts @@ -7,7 +7,7 @@ import { Action, Dispatch, MiddlewareAPI } from 'redux'; import { track, METRIC_TYPE, TELEMETRY_EVENT } from './'; -import { timelineActions } from '../../store/actions'; +import * as timelineActions from '../../store/timeline/actions'; export const telemetryMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => (action: Action) => { if (timelineActions.endTimelineSaving.match(action)) { diff --git a/x-pack/plugins/siem/public/mock/timeline_results.ts b/x-pack/plugins/siem/public/mock/timeline_results.ts index 0e61457873bfde..edd1c737718296 100644 --- a/x-pack/plugins/siem/public/mock/timeline_results.ts +++ b/x-pack/plugins/siem/public/mock/timeline_results.ts @@ -168,6 +168,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 1', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -294,6 +297,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 2', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -420,6 +426,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 2', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -546,6 +555,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 3', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -672,6 +684,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 4', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -798,6 +813,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 5', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -924,6 +942,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 6', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1050,6 +1071,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1176,6 +1200,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1302,6 +1329,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1428,6 +1458,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1554,6 +1587,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, @@ -1680,6 +1716,9 @@ export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ 'ZF0W12oB9v5HJNSHwY6L', ], title: 'test 7', + timelineType: null, + templateTimelineId: null, + templateTimelineVersion: null, created: 1558386787614, createdBy: 'elastic', updated: 1558390951234, diff --git a/x-pack/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index e54441f0118a13..1ac371a3f68297 100644 --- a/x-pack/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -37,11 +37,11 @@ const NoteDescriptionContainer = styled(EuiFlexItem)` export const isNotEmptyArray = (values: string[]) => !isEmpty(values.join('')); -const EuiBadgeWrap = styled(EuiBadge)` +const EuiBadgeWrap = (styled(EuiBadge)` .euiBadge__text { white-space: pre-wrap !important; } -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +` as unknown) as typeof EuiBadge; export const buildQueryBarDescription = ({ field, diff --git a/x-pack/plugins/siem/public/pages/timelines/translations.ts b/x-pack/plugins/siem/public/pages/timelines/translations.ts index 723d164ad2cdd5..304474bbff2c54 100644 --- a/x-pack/plugins/siem/public/pages/timelines/translations.ts +++ b/x-pack/plugins/siem/public/pages/timelines/translations.ts @@ -23,3 +23,10 @@ export const ALL_TIMELINES_IMPORT_TIMELINE_TITLE = i18n.translate( defaultMessage: 'Import Timeline', } ); + +export const ERROR_FETCHING_TIMELINES_TITLE = i18n.translate( + 'xpack.siem.timelines.allTimelines.errorFetchingTimelinesTitle', + { + defaultMessage: 'Failed to query all timelines data', + } +); diff --git a/x-pack/plugins/siem/public/store/model.ts b/x-pack/plugins/siem/public/store/model.ts index 9e9e663a59fe09..686dc096e61b01 100644 --- a/x-pack/plugins/siem/public/store/model.ts +++ b/x-pack/plugins/siem/public/store/model.ts @@ -9,15 +9,4 @@ export { dragAndDropModel } from './drag_and_drop'; export { hostsModel } from './hosts'; export { inputsModel } from './inputs'; export { networkModel } from './network'; - -export type KueryFilterQueryKind = 'kuery' | 'lucene'; - -export interface KueryFilterQuery { - kind: KueryFilterQueryKind; - expression: string; -} - -export interface SerializedFilterQuery { - kuery: KueryFilterQuery | null; - serializedQuery: string; -} +export * from './types'; diff --git a/x-pack/plugins/siem/public/store/store.ts b/x-pack/plugins/siem/public/store/store.ts index d3559e7a7adde9..2af0f87b4494d9 100644 --- a/x-pack/plugins/siem/public/store/store.ts +++ b/x-pack/plugins/siem/public/store/store.ts @@ -32,6 +32,7 @@ export const createStore = ( const middlewareDependencies = { apolloClient$: apolloClient, + selectAllTimelineQuery: inputsSelectors.globalQueryByIdSelector, selectNotesByIdSelector: appSelectors.selectNotesByIdSelector, timelineByIdSelector: timelineSelectors.timelineByIdSelector, timelineTimeRangeSelector: inputsSelectors.timelineTimeRangeSelector, diff --git a/x-pack/plugins/siem/public/store/timeline/actions.ts b/x-pack/plugins/siem/public/store/timeline/actions.ts index a03cc2643e014a..12155decf40d44 100644 --- a/x-pack/plugins/siem/public/store/timeline/actions.ts +++ b/x-pack/plugins/siem/public/store/timeline/actions.ts @@ -12,7 +12,7 @@ import { DataProvider, QueryOperator, } from '../../components/timeline/data_providers/data_provider'; -import { KueryFilterQuery, SerializedFilterQuery } from '../model'; +import { KueryFilterQuery, SerializedFilterQuery } from '../types'; import { EventType, KqlMode, TimelineModel, ColumnHeaderOptions } from './model'; import { TimelineNonEcsData } from '../../graphql/types'; diff --git a/x-pack/plugins/siem/public/store/timeline/epic.ts b/x-pack/plugins/siem/public/store/timeline/epic.ts index 4fece0572274f3..6812d8d8aa672b 100644 --- a/x-pack/plugins/siem/public/store/timeline/epic.ts +++ b/x-pack/plugins/siem/public/store/timeline/epic.ts @@ -29,17 +29,11 @@ import { } from 'rxjs/operators'; import { esFilters, Filter, MatchAllFilter } from '../../../../../../src/plugins/data/public'; -import { persistTimelineMutation } from '../../containers/timeline/persist.gql_query'; -import { - PersistTimelineMutation, - TimelineInput, - ResponseTimeline, - TimelineResult, -} from '../../graphql/types'; +import { TimelineInput, ResponseTimeline, TimelineResult } from '../../graphql/types'; import { AppApolloClient } from '../../lib/lib'; import { addError } from '../app/actions'; import { NotesById } from '../app/model'; -import { TimeRange } from '../inputs/model'; +import { inputsModel } from '../inputs'; import { applyKqlFilterQuery, @@ -75,13 +69,15 @@ import { epicPersistPinnedEvent, timelinePinnedEventActionsType } from './epic_p import { epicPersistTimelineFavorite, timelineFavoriteActionsType } from './epic_favorite'; import { isNotNull } from './helpers'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; -import { refetchQueries } from './refetch_queries'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { ActionTimeline, TimelineById } from './types'; +import { persistTimeline } from '../../containers/timeline/api'; +import { ALL_TIMELINE_QUERY_ID } from '../../containers/timeline/all'; interface TimelineEpicDependencies { timelineByIdSelector: (state: State) => TimelineById; - timelineTimeRangeSelector: (state: State) => TimeRange; + timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange; + selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; selectNotesByIdSelector: (state: State) => NotesById; apolloClient$: Observable; } @@ -119,10 +115,24 @@ export const createTimelineEpic = (): Epic< > => ( action$, state$, - { selectNotesByIdSelector, timelineByIdSelector, timelineTimeRangeSelector, apolloClient$ } + { + selectAllTimelineQuery, + selectNotesByIdSelector, + timelineByIdSelector, + timelineTimeRangeSelector, + apolloClient$, + } ) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); + const allTimelineQuery$ = state$.pipe( + map(state => { + const getQuery = selectAllTimelineQuery(); + return getQuery(state, ALL_TIMELINE_QUERY_ID); + }), + filter(isNotNull) + ); + const notes$ = state$.pipe(map(selectNotesByIdSelector), filter(isNotNull)); const timelineTimeRange$ = state$.pipe(map(timelineTimeRangeSelector), filter(isNotNull)); @@ -168,33 +178,52 @@ export const createTimelineEpic = (): Epic< const version = myEpicTimelineId.getTimelineVersion(); if (timelineNoteActionsType.includes(action.type)) { - return epicPersistNote(apolloClient, action, timeline, notes, action$, timeline$, notes$); + return epicPersistNote( + apolloClient, + action, + timeline, + notes, + action$, + timeline$, + notes$, + allTimelineQuery$ + ); } else if (timelinePinnedEventActionsType.includes(action.type)) { - return epicPersistPinnedEvent(apolloClient, action, timeline, action$, timeline$); + return epicPersistPinnedEvent( + apolloClient, + action, + timeline, + action$, + timeline$, + allTimelineQuery$ + ); } else if (timelineFavoriteActionsType.includes(action.type)) { - return epicPersistTimelineFavorite(apolloClient, action, timeline, action$, timeline$); + return epicPersistTimelineFavorite( + apolloClient, + action, + timeline, + action$, + timeline$, + allTimelineQuery$ + ); } else if (timelineActionsType.includes(action.type)) { return from( - apolloClient.mutate< - PersistTimelineMutation.Mutation, - PersistTimelineMutation.Variables - >({ - mutation: persistTimelineMutation, - fetchPolicy: 'no-cache', - variables: { - timelineId, - version, - timeline: convertTimelineAsInput(timeline[action.payload.id], timelineTimeRange), - }, - refetchQueries, + persistTimeline({ + timelineId, + version, + timeline: convertTimelineAsInput(timeline[action.payload.id], timelineTimeRange), }) ).pipe( - withLatestFrom(timeline$), - mergeMap(([result, recentTimeline]) => { + withLatestFrom(timeline$, allTimelineQuery$), + mergeMap(([result, recentTimeline, allTimelineQuery]) => { const savedTimeline = recentTimeline[action.payload.id]; const response: ResponseTimeline = get('data.persistTimeline', result); const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + if (allTimelineQuery.refetch != null) { + (allTimelineQuery.refetch as inputsModel.Refetch)(); + } + return [ response.code === 409 ? updateAutoSaveMsg({ @@ -261,7 +290,7 @@ const timelineInput: TimelineInput = { export const convertTimelineAsInput = ( timeline: TimelineModel, - timelineTimeRange: TimeRange + timelineTimeRange: inputsModel.TimeRange ): TimelineInput => Object.keys(timelineInput).reduce((acc, key) => { if (has(key, timeline)) { diff --git a/x-pack/plugins/siem/public/store/timeline/epic_favorite.ts b/x-pack/plugins/siem/public/store/timeline/epic_favorite.ts index 4d1b73aa70a6ee..6a1dadb8a59f59 100644 --- a/x-pack/plugins/siem/public/store/timeline/epic_favorite.ts +++ b/x-pack/plugins/siem/public/store/timeline/epic_favorite.ts @@ -26,6 +26,7 @@ import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persi import { refetchQueries } from './refetch_queries'; import { myEpicTimelineId } from './my_epic_timeline_id'; import { ActionTimeline, TimelineById } from './types'; +import { inputsModel } from '../inputs'; export const timelineFavoriteActionsType = [updateIsFavorite.type]; @@ -34,7 +35,8 @@ export const epicPersistTimelineFavorite = ( action: ActionTimeline, timeline: TimelineById, action$: Observable, - timeline$: Observable + timeline$: Observable, + allTimelineQuery$: Observable // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Observable => from( @@ -50,12 +52,16 @@ export const epicPersistTimelineFavorite = ( refetchQueries, }) ).pipe( - withLatestFrom(timeline$), - mergeMap(([result, recentTimelines]) => { + withLatestFrom(timeline$, allTimelineQuery$), + mergeMap(([result, recentTimelines, allTimelineQuery]) => { const savedTimeline = recentTimelines[action.payload.id]; const response: ResponseFavoriteTimeline = get('data.persistFavorite', result); const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + if (allTimelineQuery.refetch != null) { + (allTimelineQuery.refetch as inputsModel.Refetch)(); + } + return [ ...callOutMsg, updateTimeline({ diff --git a/x-pack/plugins/siem/public/store/timeline/epic_note.ts b/x-pack/plugins/siem/public/store/timeline/epic_note.ts index e5a712fe2c666f..3722a6ad8036ca 100644 --- a/x-pack/plugins/siem/public/store/timeline/epic_note.ts +++ b/x-pack/plugins/siem/public/store/timeline/epic_note.ts @@ -16,6 +16,7 @@ import { persistTimelineNoteMutation } from '../../containers/timeline/notes/per import { PersistTimelineNoteMutation, ResponseNote } from '../../graphql/types'; import { updateNote, addError } from '../app/actions'; import { NotesById } from '../app/model'; +import { inputsModel } from '../inputs'; import { addNote, @@ -39,7 +40,8 @@ export const epicPersistNote = ( notes: NotesById, action$: Observable, timeline$: Observable, - notes$: Observable + notes$: Observable, + allTimelineQuery$: Observable // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Observable => from( @@ -61,12 +63,16 @@ export const epicPersistNote = ( refetchQueries, }) ).pipe( - withLatestFrom(timeline$, notes$), - mergeMap(([result, recentTimeline, recentNotes]) => { + withLatestFrom(timeline$, notes$, allTimelineQuery$), + mergeMap(([result, recentTimeline, recentNotes, allTimelineQuery]) => { const noteIdRedux = action.payload.noteId; const response: ResponseNote = get('data.persistNote', result); const callOutMsg = response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + if (allTimelineQuery.refetch != null) { + (allTimelineQuery.refetch as inputsModel.Refetch)(); + } + return [ ...callOutMsg, recentTimeline[action.payload.id].savedObjectId == null diff --git a/x-pack/plugins/siem/public/store/timeline/epic_pinned_event.ts b/x-pack/plugins/siem/public/store/timeline/epic_pinned_event.ts index 2260999a91e7bc..a1281250ba72af 100644 --- a/x-pack/plugins/siem/public/store/timeline/epic_pinned_event.ts +++ b/x-pack/plugins/siem/public/store/timeline/epic_pinned_event.ts @@ -15,6 +15,8 @@ import { filter, mergeMap, startWith, withLatestFrom, takeUntil } from 'rxjs/ope import { persistTimelinePinnedEventMutation } from '../../containers/timeline/pinned_event/persist.gql_query'; import { PersistTimelinePinnedEventMutation, PinnedEvent } from '../../graphql/types'; import { addError } from '../app/actions'; +import { inputsModel } from '../inputs'; + import { pinEvent, endTimelineSaving, @@ -35,7 +37,8 @@ export const epicPersistPinnedEvent = ( action: ActionTimeline, timeline: TimelineById, action$: Observable, - timeline$: Observable + timeline$: Observable, + allTimelineQuery$: Observable // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Observable => from( @@ -57,12 +60,16 @@ export const epicPersistPinnedEvent = ( refetchQueries, }) ).pipe( - withLatestFrom(timeline$), - mergeMap(([result, recentTimeline]) => { + withLatestFrom(timeline$, allTimelineQuery$), + mergeMap(([result, recentTimeline, allTimelineQuery]) => { const savedTimeline = recentTimeline[action.payload.id]; const response: PinnedEvent = get('data.persistPinnedEventOnTimeline', result); const callOutMsg = response && response.code === 403 ? [showCallOutUnauthorizedMsg()] : []; + if (allTimelineQuery.refetch != null) { + (allTimelineQuery.refetch as inputsModel.Refetch)(); + } + return [ response != null ? updateTimeline({ diff --git a/x-pack/plugins/siem/public/store/types.ts b/x-pack/plugins/siem/public/store/types.ts new file mode 100644 index 00000000000000..2c679ba41116ee --- /dev/null +++ b/x-pack/plugins/siem/public/store/types.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 type KueryFilterQueryKind = 'kuery' | 'lucene'; + +export interface KueryFilterQuery { + kind: KueryFilterQueryKind; + expression: string; +} + +export interface SerializedFilterQuery { + kuery: KueryFilterQuery | null; + serializedQuery: string; +} diff --git a/x-pack/plugins/siem/server/graphql/timeline/schema.gql.ts b/x-pack/plugins/siem/server/graphql/timeline/schema.gql.ts index 9dd04247b7f47c..bc2b3a53d85f3e 100644 --- a/x-pack/plugins/siem/server/graphql/timeline/schema.gql.ts +++ b/x-pack/plugins/siem/server/graphql/timeline/schema.gql.ts @@ -125,6 +125,11 @@ export const timelineSchema = gql` script: String } + enum TimelineType { + default + template + } + input TimelineInput { columns: [ColumnHeaderInput!] dataProviders: [DataProviderInput!] @@ -134,6 +139,9 @@ export const timelineSchema = gql` kqlMode: String kqlQuery: SerializedFilterQueryInput title: String + templateTimelineId: String + templateTimelineVersion: Int + timelineType: TimelineType dateRange: DateRangePickerInput savedQueryId: String sort: SortTimelineInput @@ -237,6 +245,9 @@ export const timelineSchema = gql` savedObjectId: String! sort: SortTimelineResult title: String + templateTimelineId: String + templateTimelineVersion: Int + timelineType: TimelineType updated: Float updatedBy: String version: String! diff --git a/x-pack/plugins/siem/server/graphql/types.ts b/x-pack/plugins/siem/server/graphql/types.ts index d272b7ff59b79a..6a35ba08f8e43f 100644 --- a/x-pack/plugins/siem/server/graphql/types.ts +++ b/x-pack/plugins/siem/server/graphql/types.ts @@ -134,6 +134,12 @@ export interface TimelineInput { title?: Maybe; + templateTimelineId?: Maybe; + + templateTimelineVersion?: Maybe; + + timelineType?: Maybe; + dateRange?: Maybe; savedQueryId?: Maybe; @@ -336,6 +342,11 @@ export enum TlsFields { _id = '_id', } +export enum TimelineType { + default = 'default', + template = 'template', +} + export enum SortFieldTimeline { title = 'title', description = 'description', @@ -1946,6 +1957,12 @@ export interface TimelineResult { title?: Maybe; + templateTimelineId?: Maybe; + + templateTimelineVersion?: Maybe; + + timelineType?: Maybe; + updated?: Maybe; updatedBy?: Maybe; @@ -8023,6 +8040,12 @@ export namespace TimelineResultResolvers { title?: TitleResolver, TypeParent, TContext>; + templateTimelineId?: TemplateTimelineIdResolver, TypeParent, TContext>; + + templateTimelineVersion?: TemplateTimelineVersionResolver, TypeParent, TContext>; + + timelineType?: TimelineTypeResolver, TypeParent, TContext>; + updated?: UpdatedResolver, TypeParent, TContext>; updatedBy?: UpdatedByResolver, TypeParent, TContext>; @@ -8130,6 +8153,21 @@ export namespace TimelineResultResolvers { Parent = TimelineResult, TContext = SiemContext > = Resolver; + export type TemplateTimelineIdResolver< + R = Maybe, + Parent = TimelineResult, + TContext = SiemContext + > = Resolver; + export type TemplateTimelineVersionResolver< + R = Maybe, + Parent = TimelineResult, + TContext = SiemContext + > = Resolver; + export type TimelineTypeResolver< + R = Maybe, + Parent = TimelineResult, + TContext = SiemContext + > = Resolver; export type UpdatedResolver< R = Maybe, Parent = TimelineResult, diff --git a/x-pack/plugins/siem/server/lib/compose/kibana.ts b/x-pack/plugins/siem/server/lib/compose/kibana.ts index 9c46f3320e37ec..4a595032e43eb2 100644 --- a/x-pack/plugins/siem/server/lib/compose/kibana.ts +++ b/x-pack/plugins/siem/server/lib/compose/kibana.ts @@ -27,9 +27,9 @@ import { ElasticsearchSourceStatusAdapter, SourceStatus } from '../source_status import { ConfigurationSourcesAdapter, Sources } from '../sources'; import { AppBackendLibs, AppDomainLibs } from '../types'; import { ElasticsearchUncommonProcessesAdapter, UncommonProcesses } from '../uncommon_processes'; -import { Note } from '../note/saved_object'; -import { PinnedEvent } from '../pinned_event/saved_object'; -import { Timeline } from '../timeline/saved_object'; +import * as note from '../note/saved_object'; +import * as pinnedEvent from '../pinned_event/saved_object'; +import * as timeline from '../timeline/saved_object'; import { ElasticsearchMatrixHistogramAdapter, MatrixHistogram } from '../matrix_histogram'; export function compose( @@ -41,10 +41,6 @@ export function compose( const sources = new Sources(new ConfigurationSourcesAdapter()); const sourceStatus = new SourceStatus(new ElasticsearchSourceStatusAdapter(framework)); - const timeline = new Timeline(); - const note = new Note(); - const pinnedEvent = new PinnedEvent(); - const domainLibs: AppDomainLibs = { authentications: new Authentications(new ElasticsearchAuthenticationAdapter(framework)), events: new Events(new ElasticsearchEventsAdapter(framework)), diff --git a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh index 750c5574f4a729..2028216e6770f2 100755 --- a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh +++ b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh @@ -10,7 +10,7 @@ set -e ./check_env_variables.sh # Example: ./get_action_instances.sh -# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/actions/README.md#get-apiaction_find-find-actions +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/actions/README.md#get-apiaction_find-find-actions curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/action/_getAll \ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_types.sh b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_types.sh index 8d8cbdd70a8036..c587e9a2041822 100755 --- a/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_types.sh +++ b/x-pack/plugins/siem/server/lib/detection_engine/scripts/get_action_types.sh @@ -10,7 +10,7 @@ set -e ./check_env_variables.sh # Example: ./get_action_types.sh -# https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/actions/README.md +# https://github.com/elastic/kibana/blob/master/x-pack/plugins/actions/README.md curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ -X GET ${KIBANA_URL}${SPACE_URL}/api/action/types \ diff --git a/x-pack/plugins/siem/server/lib/detection_engine/scripts/hard_reset.sh b/x-pack/plugins/siem/server/lib/detection_engine/scripts/hard_reset.sh index a56d788d69c16c..22b602f935187e 100755 --- a/x-pack/plugins/siem/server/lib/detection_engine/scripts/hard_reset.sh +++ b/x-pack/plugins/siem/server/lib/detection_engine/scripts/hard_reset.sh @@ -9,9 +9,15 @@ set -e ./check_env_variables.sh +# Clean up and remove all actions and alerts from SIEM +# within saved objects ./delete_all_actions.sh ./delete_all_alerts.sh ./delete_all_alert_tasks.sh + +# delete all the statuses from the signal index ./delete_all_statuses.sh + +# re-create the signal index ./delete_signal_index.sh ./post_signal_index.sh diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index d298f1cc7cbc66..a8cc6dc6804102 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { flow, set, omit } from 'lodash/fp'; +import { flow, omit } from 'lodash/fp'; +import set from 'set-value'; import { SearchResponse } from 'elasticsearch'; import { Logger } from '../../../../../../../src/core/server'; @@ -55,8 +56,11 @@ export const transformAnomalyFieldsToEcs = (anomaly: Anomaly): EcsAnomaly => { } const omitDottedFields = omit(errantFields.map(field => field.name)); - const setNestedFields = errantFields.map(field => set(field.name, field.value)); - const setTimestamp = set('@timestamp', new Date(timestamp).toISOString()); + const setNestedFields = errantFields.map(field => (_anomaly: Anomaly) => + set(_anomaly, field.name, field.value) + ); + const setTimestamp = (_anomaly: Anomaly) => + set(_anomaly, '@timestamp', new Date(timestamp).toISOString()); return flow(omitDottedFields, setNestedFields, setTimestamp)(anomaly); }; diff --git a/x-pack/plugins/siem/server/lib/note/saved_object.ts b/x-pack/plugins/siem/server/lib/note/saved_object.ts index 3eae30625e4223..219465f5514574 100644 --- a/x-pack/plugins/siem/server/lib/note/saved_object.ts +++ b/x-pack/plugins/siem/server/lib/note/saved_object.ts @@ -15,6 +15,11 @@ import { identity } from 'fp-ts/lib/function'; import { SavedObjectsFindOptions } from '../../../../../../src/core/server'; import { AuthenticatedUser } from '../../../../security/common/model'; import { UNAUTHENTICATED_USER } from '../../../common/constants'; +import { + SavedNote, + NoteSavedObjectRuntimeType, + NoteSavedObject, +} from '../../../common/types/timeline/note'; import { PageInfoNote, ResponseNote, @@ -23,178 +28,198 @@ import { NoteResult, } from '../../graphql/types'; import { FrameworkRequest } from '../framework'; -import { SavedNote, NoteSavedObjectRuntimeType, NoteSavedObject } from './types'; import { noteSavedObjectType } from './saved_object_mappings'; import { pickSavedTimeline } from '../timeline/pick_saved_timeline'; import { convertSavedObjectToSavedTimeline } from '../timeline/convert_saved_object_to_savedtimeline'; import { timelineSavedObjectType } from '../timeline/saved_object_mappings'; -export class Note { - public async deleteNote(request: FrameworkRequest, noteIds: string[]) { - const savedObjectsClient = request.context.core.savedObjects.client; - - await Promise.all( - noteIds.map(noteId => savedObjectsClient.delete(noteSavedObjectType, noteId)) - ); - } - - public async deleteNoteByTimelineId(request: FrameworkRequest, timelineId: string) { - const options: SavedObjectsFindOptions = { - type: noteSavedObjectType, - search: timelineId, - searchFields: ['timelineId'], - }; - const notesToBeDeleted = await this.getAllSavedNote(request, options); - const savedObjectsClient = request.context.core.savedObjects.client; - - await Promise.all( - notesToBeDeleted.notes.map(note => - savedObjectsClient.delete(noteSavedObjectType, note.noteId) - ) - ); - } - - public async getNote(request: FrameworkRequest, noteId: string): Promise { - return this.getSavedNote(request, noteId); - } - - public async getNotesByEventId( - request: FrameworkRequest, - eventId: string - ): Promise { - const options: SavedObjectsFindOptions = { - type: noteSavedObjectType, - search: eventId, - searchFields: ['eventId'], - }; - const notesByEventId = await this.getAllSavedNote(request, options); - return notesByEventId.notes; - } - - public async getNotesByTimelineId( - request: FrameworkRequest, - timelineId: string - ): Promise { - const options: SavedObjectsFindOptions = { - type: noteSavedObjectType, - search: timelineId, - searchFields: ['timelineId'], - }; - const notesByTimelineId = await this.getAllSavedNote(request, options); - return notesByTimelineId.notes; - } - - public async getAllNotes( +export interface Note { + deleteNote: (request: FrameworkRequest, noteIds: string[]) => Promise; + deleteNoteByTimelineId: (request: FrameworkRequest, noteIds: string) => Promise; + getNote: (request: FrameworkRequest, noteId: string) => Promise; + getNotesByEventId: (request: FrameworkRequest, noteId: string) => Promise; + getNotesByTimelineId: (request: FrameworkRequest, noteId: string) => Promise; + getAllNotes: ( request: FrameworkRequest, pageInfo: PageInfoNote | null, search: string | null, sort: SortNote | null - ): Promise { - const options: SavedObjectsFindOptions = { - type: noteSavedObjectType, - perPage: pageInfo != null ? pageInfo.pageSize : undefined, - page: pageInfo != null ? pageInfo.pageIndex : undefined, - search: search != null ? search : undefined, - searchFields: ['note'], - sortField: sort != null ? sort.sortField : undefined, - sortOrder: sort != null ? sort.sortOrder : undefined, - }; - return this.getAllSavedNote(request, options); - } - - public async persistNote( + ) => Promise; + persistNote: ( request: FrameworkRequest, noteId: string | null, version: string | null, note: SavedNote - ): Promise { - try { - const savedObjectsClient = request.context.core.savedObjects.client; - - if (noteId == null) { - const timelineVersionSavedObject = - note.timelineId == null - ? await (async () => { - const timelineResult = convertSavedObjectToSavedTimeline( - await savedObjectsClient.create( - timelineSavedObjectType, - pickSavedTimeline(null, {}, request.user) - ) - ); - note.timelineId = timelineResult.savedObjectId; - return timelineResult.version; - })() - : null; - - // Create new note - return { - code: 200, - message: 'success', - note: convertSavedObjectToSavedNote( - await savedObjectsClient.create( - noteSavedObjectType, - pickSavedNote(noteId, note, request.user) - ), - timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined - ), - }; - } + ) => Promise; + convertSavedObjectToSavedNote: ( + savedObject: unknown, + timelineVersion?: string | undefined | null + ) => NoteSavedObject; +} + +export const deleteNote = async (request: FrameworkRequest, noteIds: string[]) => { + const savedObjectsClient = request.context.core.savedObjects.client; + + await Promise.all(noteIds.map(noteId => savedObjectsClient.delete(noteSavedObjectType, noteId))); +}; + +export const deleteNoteByTimelineId = async (request: FrameworkRequest, timelineId: string) => { + const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, + search: timelineId, + searchFields: ['timelineId'], + }; + const notesToBeDeleted = await getAllSavedNote(request, options); + const savedObjectsClient = request.context.core.savedObjects.client; + + await Promise.all( + notesToBeDeleted.notes.map(note => savedObjectsClient.delete(noteSavedObjectType, note.noteId)) + ); +}; + +export const getNote = async ( + request: FrameworkRequest, + noteId: string +): Promise => { + return getSavedNote(request, noteId); +}; + +export const getNotesByEventId = async ( + request: FrameworkRequest, + eventId: string +): Promise => { + const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, + search: eventId, + searchFields: ['eventId'], + }; + const notesByEventId = await getAllSavedNote(request, options); + return notesByEventId.notes; +}; + +export const getNotesByTimelineId = async ( + request: FrameworkRequest, + timelineId: string +): Promise => { + const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, + search: timelineId, + searchFields: ['timelineId'], + }; + const notesByTimelineId = await getAllSavedNote(request, options); + return notesByTimelineId.notes; +}; + +export const getAllNotes = async ( + request: FrameworkRequest, + pageInfo: PageInfoNote | null, + search: string | null, + sort: SortNote | null +): Promise => { + const options: SavedObjectsFindOptions = { + type: noteSavedObjectType, + perPage: pageInfo != null ? pageInfo.pageSize : undefined, + page: pageInfo != null ? pageInfo.pageIndex : undefined, + search: search != null ? search : undefined, + searchFields: ['note'], + sortField: sort != null ? sort.sortField : undefined, + sortOrder: sort != null ? sort.sortOrder : undefined, + }; + return getAllSavedNote(request, options); +}; + +export const persistNote = async ( + request: FrameworkRequest, + noteId: string | null, + version: string | null, + note: SavedNote +): Promise => { + try { + const savedObjectsClient = request.context.core.savedObjects.client; - // Update new note + if (noteId == null) { + const timelineVersionSavedObject = + note.timelineId == null + ? await (async () => { + const timelineResult = convertSavedObjectToSavedTimeline( + await savedObjectsClient.create( + timelineSavedObjectType, + pickSavedTimeline(null, {}, request.user) + ) + ); + note.timelineId = timelineResult.savedObjectId; + return timelineResult.version; + })() + : null; - const existingNote = await this.getSavedNote(request, noteId); + // Create new note return { code: 200, message: 'success', note: convertSavedObjectToSavedNote( - await savedObjectsClient.update( + await savedObjectsClient.create( noteSavedObjectType, - noteId, - pickSavedNote(noteId, note, request.user), - { - version: existingNote.version || undefined, - } - ) + pickSavedNote(noteId, note, request.user) + ), + timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined ), }; - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 403) { - const noteToReturn: NoteResult = { - ...note, - noteId: uuid.v1(), - version: '', - timelineId: '', - timelineVersion: '', - }; - return { - code: 403, - message: err.message, - note: noteToReturn, - }; - } - throw err; } - } - private async getSavedNote(request: FrameworkRequest, NoteId: string) { - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObject = await savedObjectsClient.get(noteSavedObjectType, NoteId); - - return convertSavedObjectToSavedNote(savedObject); - } - - private async getAllSavedNote(request: FrameworkRequest, options: SavedObjectsFindOptions) { - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObjects = await savedObjectsClient.find(options); + // Update new note + const existingNote = await getSavedNote(request, noteId); return { - totalCount: savedObjects.total, - notes: savedObjects.saved_objects.map(savedObject => - convertSavedObjectToSavedNote(savedObject) + code: 200, + message: 'success', + note: convertSavedObjectToSavedNote( + await savedObjectsClient.update( + noteSavedObjectType, + noteId, + pickSavedNote(noteId, note, request.user), + { + version: existingNote.version || undefined, + } + ) ), }; + } catch (err) { + if (getOr(null, 'output.statusCode', err) === 403) { + const noteToReturn: NoteResult = { + ...note, + noteId: uuid.v1(), + version: '', + timelineId: '', + timelineVersion: '', + }; + return { + code: 403, + message: err.message, + note: noteToReturn, + }; + } + throw err; } -} +}; + +const getSavedNote = async (request: FrameworkRequest, NoteId: string) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObject = await savedObjectsClient.get(noteSavedObjectType, NoteId); + + return convertSavedObjectToSavedNote(savedObject); +}; + +const getAllSavedNote = async (request: FrameworkRequest, options: SavedObjectsFindOptions) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjects = await savedObjectsClient.find(options); + + return { + totalCount: savedObjects.total, + notes: savedObjects.saved_objects.map(savedObject => + convertSavedObjectToSavedNote(savedObject) + ), + }; +}; export const convertSavedObjectToSavedNote = ( savedObject: unknown, diff --git a/x-pack/plugins/siem/server/lib/pinned_event/saved_object.ts b/x-pack/plugins/siem/server/lib/pinned_event/saved_object.ts index 1e3a481e17106f..c653f23d5c1499 100644 --- a/x-pack/plugins/siem/server/lib/pinned_event/saved_object.ts +++ b/x-pack/plugins/siem/server/lib/pinned_event/saved_object.ts @@ -13,174 +13,224 @@ import { identity } from 'fp-ts/lib/function'; import { SavedObjectsFindOptions } from '../../../../../../src/core/server'; import { AuthenticatedUser } from '../../../../security/common/model'; import { UNAUTHENTICATED_USER } from '../../../common/constants'; -import { FrameworkRequest } from '../framework'; import { PinnedEventSavedObject, PinnedEventSavedObjectRuntimeType, SavedPinnedEvent, -} from './types'; +} from '../../../common/types/timeline/pinned_event'; +import { FrameworkRequest } from '../framework'; + import { PageInfoNote, SortNote, PinnedEvent as PinnedEventResponse } from '../../graphql/types'; import { pickSavedTimeline } from '../timeline/pick_saved_timeline'; import { convertSavedObjectToSavedTimeline } from '../timeline/convert_saved_object_to_savedtimeline'; import { pinnedEventSavedObjectType } from './saved_object_mappings'; import { timelineSavedObjectType } from '../timeline/saved_object_mappings'; -export class PinnedEvent { - public async deletePinnedEventOnTimeline(request: FrameworkRequest, pinnedEventIds: string[]) { - const savedObjectsClient = request.context.core.savedObjects.client; - - await Promise.all( - pinnedEventIds.map(pinnedEventId => - savedObjectsClient.delete(pinnedEventSavedObjectType, pinnedEventId) - ) - ); - } +export interface PinnedEvent { + deletePinnedEventOnTimeline: ( + request: FrameworkRequest, + pinnedEventIds: string[] + ) => Promise; - public async deleteAllPinnedEventsOnTimeline(request: FrameworkRequest, timelineId: string) { - const savedObjectsClient = request.context.core.savedObjects.client; - const options: SavedObjectsFindOptions = { - type: pinnedEventSavedObjectType, - search: timelineId, - searchFields: ['timelineId'], - }; - const pinnedEventToBeDeleted = await this.getAllSavedPinnedEvents(request, options); - await Promise.all( - pinnedEventToBeDeleted.map(pinnedEvent => - savedObjectsClient.delete(pinnedEventSavedObjectType, pinnedEvent.pinnedEventId) - ) - ); - } + deleteAllPinnedEventsOnTimeline: (request: FrameworkRequest, timelineId: string) => Promise; - public async getPinnedEvent( + getPinnedEvent: ( request: FrameworkRequest, pinnedEventId: string - ): Promise { - return this.getSavedPinnedEvent(request, pinnedEventId); - } + ) => Promise; - public async getAllPinnedEventsByTimelineId( + getAllPinnedEventsByTimelineId: ( request: FrameworkRequest, timelineId: string - ): Promise { - const options: SavedObjectsFindOptions = { - type: pinnedEventSavedObjectType, - search: timelineId, - searchFields: ['timelineId'], - }; - return this.getAllSavedPinnedEvents(request, options); - } + ) => Promise; - public async getAllPinnedEvents( + getAllPinnedEvents: ( request: FrameworkRequest, pageInfo: PageInfoNote | null, search: string | null, sort: SortNote | null - ): Promise { - const options: SavedObjectsFindOptions = { - type: pinnedEventSavedObjectType, - perPage: pageInfo != null ? pageInfo.pageSize : undefined, - page: pageInfo != null ? pageInfo.pageIndex : undefined, - search: search != null ? search : undefined, - searchFields: ['timelineId', 'eventId'], - sortField: sort != null ? sort.sortField : undefined, - sortOrder: sort != null ? sort.sortOrder : undefined, - }; - return this.getAllSavedPinnedEvents(request, options); - } + ) => Promise; - public async persistPinnedEventOnTimeline( + persistPinnedEventOnTimeline: ( request: FrameworkRequest, pinnedEventId: string | null, // pinned event saved object id eventId: string, timelineId: string | null - ): Promise { - const savedObjectsClient = request.context.core.savedObjects.client; - - try { - if (pinnedEventId == null) { - const timelineVersionSavedObject = - timelineId == null - ? await (async () => { - const timelineResult = convertSavedObjectToSavedTimeline( - await savedObjectsClient.create( - timelineSavedObjectType, - pickSavedTimeline(null, {}, request.user || null) - ) - ); - timelineId = timelineResult.savedObjectId; // eslint-disable-line no-param-reassign - return timelineResult.version; - })() - : null; - - if (timelineId != null) { - const allPinnedEventId = await this.getAllPinnedEventsByTimelineId(request, timelineId); - const isPinnedAlreadyExisting = allPinnedEventId.filter( - pinnedEvent => pinnedEvent.eventId === eventId - ); + ) => Promise; - if (isPinnedAlreadyExisting.length === 0) { - const savedPinnedEvent: SavedPinnedEvent = { - eventId, - timelineId, - }; - // create Pinned Event on Timeline - return convertSavedObjectToSavedPinnedEvent( - await savedObjectsClient.create( - pinnedEventSavedObjectType, - pickSavedPinnedEvent(pinnedEventId, savedPinnedEvent, request.user || null) - ), - timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined - ); - } - return isPinnedAlreadyExisting[0]; + convertSavedObjectToSavedPinnedEvent: ( + savedObject: unknown, + timelineVersion?: string | undefined | null + ) => PinnedEventSavedObject; + + pickSavedPinnedEvent: ( + pinnedEventId: string | null, + savedPinnedEvent: SavedPinnedEvent, + userInfo: AuthenticatedUser | null + ) => // eslint-disable-next-line @typescript-eslint/no-explicit-any + any; +} + +export const deletePinnedEventOnTimeline = async ( + request: FrameworkRequest, + pinnedEventIds: string[] +) => { + const savedObjectsClient = request.context.core.savedObjects.client; + + await Promise.all( + pinnedEventIds.map(pinnedEventId => + savedObjectsClient.delete(pinnedEventSavedObjectType, pinnedEventId) + ) + ); +}; + +export const deleteAllPinnedEventsOnTimeline = async ( + request: FrameworkRequest, + timelineId: string +) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, + search: timelineId, + searchFields: ['timelineId'], + }; + const pinnedEventToBeDeleted = await getAllSavedPinnedEvents(request, options); + await Promise.all( + pinnedEventToBeDeleted.map(pinnedEvent => + savedObjectsClient.delete(pinnedEventSavedObjectType, pinnedEvent.pinnedEventId) + ) + ); +}; + +export const getPinnedEvent = async ( + request: FrameworkRequest, + pinnedEventId: string +): Promise => { + return getSavedPinnedEvent(request, pinnedEventId); +}; + +export const getAllPinnedEventsByTimelineId = async ( + request: FrameworkRequest, + timelineId: string +): Promise => { + const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, + search: timelineId, + searchFields: ['timelineId'], + }; + return getAllSavedPinnedEvents(request, options); +}; + +export const getAllPinnedEvents = async ( + request: FrameworkRequest, + pageInfo: PageInfoNote | null, + search: string | null, + sort: SortNote | null +): Promise => { + const options: SavedObjectsFindOptions = { + type: pinnedEventSavedObjectType, + perPage: pageInfo != null ? pageInfo.pageSize : undefined, + page: pageInfo != null ? pageInfo.pageIndex : undefined, + search: search != null ? search : undefined, + searchFields: ['timelineId', 'eventId'], + sortField: sort != null ? sort.sortField : undefined, + sortOrder: sort != null ? sort.sortOrder : undefined, + }; + return getAllSavedPinnedEvents(request, options); +}; + +export const persistPinnedEventOnTimeline = async ( + request: FrameworkRequest, + pinnedEventId: string | null, // pinned event saved object id + eventId: string, + timelineId: string | null +): Promise => { + const savedObjectsClient = request.context.core.savedObjects.client; + + try { + if (pinnedEventId == null) { + const timelineVersionSavedObject = + timelineId == null + ? await (async () => { + const timelineResult = convertSavedObjectToSavedTimeline( + await savedObjectsClient.create( + timelineSavedObjectType, + pickSavedTimeline(null, {}, request.user || null) + ) + ); + timelineId = timelineResult.savedObjectId; // eslint-disable-line no-param-reassign + return timelineResult.version; + })() + : null; + + if (timelineId != null) { + const allPinnedEventId = await getAllPinnedEventsByTimelineId(request, timelineId); + const isPinnedAlreadyExisting = allPinnedEventId.filter( + pinnedEvent => pinnedEvent.eventId === eventId + ); + + if (isPinnedAlreadyExisting.length === 0) { + const savedPinnedEvent: SavedPinnedEvent = { + eventId, + timelineId, + }; + // create Pinned Event on Timeline + return convertSavedObjectToSavedPinnedEvent( + await savedObjectsClient.create( + pinnedEventSavedObjectType, + pickSavedPinnedEvent(pinnedEventId, savedPinnedEvent, request.user || null) + ), + timelineVersionSavedObject != null ? timelineVersionSavedObject : undefined + ); } - throw new Error('You can NOT pinned event without a timelineID'); + return isPinnedAlreadyExisting[0]; } - // Delete Pinned Event on Timeline - await this.deletePinnedEventOnTimeline(request, [pinnedEventId]); + throw new Error('You can NOT pinned event without a timelineID'); + } + // Delete Pinned Event on Timeline + await deletePinnedEventOnTimeline(request, [pinnedEventId]); + return null; + } catch (err) { + if (getOr(null, 'output.statusCode', err) === 404) { + /* + * Why we are doing that, because if it is not found for sure that it will be unpinned + * There is no need to bring back this error since we can assume that it is unpinned + */ return null; - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 404) { - /* - * Why we are doing that, because if it is not found for sure that it will be unpinned - * There is no need to bring back this error since we can assume that it is unpinned - */ - return null; - } - if (getOr(null, 'output.statusCode', err) === 403) { - return pinnedEventId != null - ? { - code: 403, - message: err.message, - pinnedEventId: eventId, - timelineId: '', - timelineVersion: '', - } - : null; - } - throw err; } + if (getOr(null, 'output.statusCode', err) === 403) { + return pinnedEventId != null + ? { + code: 403, + message: err.message, + pinnedEventId: eventId, + timelineId: '', + timelineVersion: '', + } + : null; + } + throw err; } +}; - private async getSavedPinnedEvent(request: FrameworkRequest, pinnedEventId: string) { - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObject = await savedObjectsClient.get(pinnedEventSavedObjectType, pinnedEventId); +const getSavedPinnedEvent = async (request: FrameworkRequest, pinnedEventId: string) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObject = await savedObjectsClient.get(pinnedEventSavedObjectType, pinnedEventId); - return convertSavedObjectToSavedPinnedEvent(savedObject); - } + return convertSavedObjectToSavedPinnedEvent(savedObject); +}; - private async getAllSavedPinnedEvents( - request: FrameworkRequest, - options: SavedObjectsFindOptions - ) { - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObjects = await savedObjectsClient.find(options); - - return savedObjects.saved_objects.map(savedObject => - convertSavedObjectToSavedPinnedEvent(savedObject) - ); - } -} +const getAllSavedPinnedEvents = async ( + request: FrameworkRequest, + options: SavedObjectsFindOptions +) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObjects = await savedObjectsClient.find(options); + + return savedObjects.saved_objects.map(savedObject => + convertSavedObjectToSavedPinnedEvent(savedObject) + ); +}; export const convertSavedObjectToSavedPinnedEvent = ( savedObject: unknown, diff --git a/x-pack/plugins/siem/server/lib/timeline/convert_saved_object_to_savedtimeline.ts b/x-pack/plugins/siem/server/lib/timeline/convert_saved_object_to_savedtimeline.ts index ea5db565483c83..bde24a338ec846 100644 --- a/x-pack/plugins/siem/server/lib/timeline/convert_saved_object_to_savedtimeline.ts +++ b/x-pack/plugins/siem/server/lib/timeline/convert_saved_object_to_savedtimeline.ts @@ -8,16 +8,21 @@ import { failure } from 'io-ts/lib/PathReporter'; import { pipe } from 'fp-ts/lib/pipeable'; import { map, fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; -import { TimelineSavedObjectRuntimeType, TimelineSavedObject } from './types'; +import { + TimelineSavedObjectRuntimeType, + TimelineSavedObject, +} from '../../../common/types/timeline'; export const convertSavedObjectToSavedTimeline = (savedObject: unknown): TimelineSavedObject => { const timeline = pipe( TimelineSavedObjectRuntimeType.decode(savedObject), - map(savedTimeline => ({ - savedObjectId: savedTimeline.id, - version: savedTimeline.version, - ...savedTimeline.attributes, - })), + map(savedTimeline => { + return { + savedObjectId: savedTimeline.id, + version: savedTimeline.version, + ...savedTimeline.attributes, + }; + }), fold(errors => { throw new Error(failure(errors).join('\n')); }, identity) diff --git a/x-pack/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts b/x-pack/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts index abe8de9bf5b94b..6b4017b5e4d5c7 100644 --- a/x-pack/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts +++ b/x-pack/plugins/siem/server/lib/timeline/create_timelines_stream_from_ndjson.ts @@ -22,6 +22,7 @@ import { import { ImportTimelineResponse } from './routes/utils/import_timelines'; import { ImportTimelinesSchemaRt } from './routes/schemas/import_timelines_schema'; +import { BadRequestError } from '../detection_engine/errors/bad_request_error'; type ErrorFactory = (message: string) => Error; @@ -38,8 +39,11 @@ export const decodeOrThrow = ( pipe(runtimeType.decode(inputValue), fold(throwErrors(createError), identity)); export const validateTimelines = (): Transform => - createMapStream((obj: ImportTimelineResponse) => decodeOrThrow(ImportTimelinesSchemaRt)(obj)); - + createMapStream((obj: ImportTimelineResponse) => + obj instanceof Error + ? new BadRequestError(obj.message) + : decodeOrThrow(ImportTimelinesSchemaRt)(obj) + ); export const createTimelinesStreamFromNdJson = (ruleLimit: number) => { return [ createSplitStream('\n'), diff --git a/x-pack/plugins/siem/server/lib/timeline/pick_saved_timeline.ts b/x-pack/plugins/siem/server/lib/timeline/pick_saved_timeline.ts index 19adb7ac1045a0..eeded1cc2532de 100644 --- a/x-pack/plugins/siem/server/lib/timeline/pick_saved_timeline.ts +++ b/x-pack/plugins/siem/server/lib/timeline/pick_saved_timeline.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import uuid from 'uuid'; import { AuthenticatedUser } from '../../../../security/common/model'; import { UNAUTHENTICATED_USER } from '../../../common/constants'; -import { SavedTimeline } from './types'; +import { SavedTimeline, TimelineType } from '../../../common/types/timeline'; export const pickSavedTimeline = ( timelineId: string | null, @@ -24,5 +25,21 @@ export const pickSavedTimeline = ( savedTimeline.updated = dateNow; savedTimeline.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; } + + if (savedTimeline.timelineType === TimelineType.template) { + savedTimeline.timelineType = TimelineType.template; + if (savedTimeline.templateTimelineId == null) { + savedTimeline.templateTimelineId = uuid.v4(); + } + + if (savedTimeline.templateTimelineVersion == null) { + savedTimeline.templateTimelineVersion = 1; + } + } else { + savedTimeline.timelineType = TimelineType.default; + savedTimeline.templateTimelineId = null; + savedTimeline.templateTimelineVersion = null; + } + return savedTimeline; }; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/README.md b/x-pack/plugins/siem/server/lib/timeline/routes/README.md new file mode 100644 index 00000000000000..2c5547e39fc4e5 --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/README.md @@ -0,0 +1,326 @@ +**Timeline apis** + + 1. Create timeline api + 2. Update timeline api + 3. Create template timeline api + 4. Update template timeline api + + +## Create timeline api +#### POST /api/timeline +##### Authorization +Type: Basic Auth +username: Your Kibana username +password: Your Kibana password + + +##### Request header +``` +Content-Type: application/json +kbn-version: 8.0.0 +``` +##### Request body +```json +{ + "timeline": { + "columns": [ + { + "columnHeaderType": "not-filtered", + "id": "@timestamp" + }, + { + "columnHeaderType": "not-filtered", + "id": "message" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.category" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.action" + }, + { + "columnHeaderType": "not-filtered", + "id": "host.name" + }, + { + "columnHeaderType": "not-filtered", + "id": "source.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "destination.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "user.name" + } + ], + "dataProviders": [], + "description": "", + "eventType": "all", + "filters": [], + "kqlMode": "filter", + "kqlQuery": { + "filterQuery": null + }, + "title": "abd", + "dateRange": { + "start": 1587370079200, + "end": 1587456479201 + }, + "savedQueryId": null, + "sort": { + "columnId": "@timestamp", + "sortDirection": "desc" + } + }, + "timelineId":null, // Leave this as null + "version":null // Leave this as null +} +``` + + +## Update timeline api +#### PATCH /api/timeline +##### Authorization +Type: Basic Auth +username: Your Kibana username +password: Your Kibana password + + +##### Request header +``` +Content-Type: application/json +kbn-version: 8.0.0 +``` +##### Request body +```json +{ + "timeline": { + "columns": [ + { + "columnHeaderType": "not-filtered", + "id": "@timestamp" + }, + { + "columnHeaderType": "not-filtered", + "id": "message" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.category" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.action" + }, + { + "columnHeaderType": "not-filtered", + "id": "host.name" + }, + { + "columnHeaderType": "not-filtered", + "id": "source.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "destination.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "user.name" + } + ], + "dataProviders": [], + "description": "", + "eventType": "all", + "filters": [], + "kqlMode": "filter", + "kqlQuery": { + "filterQuery": null + }, + "title": "abd", + "dateRange": { + "start": 1587370079200, + "end": 1587456479201 + }, + "savedQueryId": null, + "sort": { + "columnId": "@timestamp", + "sortDirection": "desc" + }, + "created": 1587468588922, + "createdBy": "casetester", + "updated": 1587468588922, + "updatedBy": "casetester", + "timelineType": "default" + }, + "timelineId":"68ea5330-83c3-11ea-bff9-ab01dd7cb6cc", // Have to match the existing timeline savedObject id + "version":"WzYwLDFd" // Have to match the existing timeline version +} +``` + +## Create template timeline api +#### POST /api/timeline +##### Authorization +Type: Basic Auth +username: Your Kibana username +password: Your Kibana password + + +##### Request header +``` +Content-Type: application/json +kbn-version: 8.0.0 +``` +##### Request body +```json +{ + "timeline": { + "columns": [ + { + "columnHeaderType": "not-filtered", + "id": "@timestamp" + }, + { + "columnHeaderType": "not-filtered", + "id": "message" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.category" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.action" + }, + { + "columnHeaderType": "not-filtered", + "id": "host.name" + }, + { + "columnHeaderType": "not-filtered", + "id": "source.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "destination.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "user.name" + } + ], + "dataProviders": [ + + ], + "description": "", + "eventType": "all", + "filters": [ + + ], + "kqlMode": "filter", + "kqlQuery": { + "filterQuery": null + }, + "title": "abd", + "dateRange": { + "start": 1587370079200, + "end": 1587456479201 + }, + "savedQueryId": null, + "sort": { + "columnId": "@timestamp", + "sortDirection": "desc" + }, + "timelineType": "template" // This is the difference between create timeline + }, + "timelineId":null, // Leave this as null + "version":null // Leave this as null +} +``` + + +## Update template timeline api +#### PATCH /api/timeline +##### Authorization +Type: Basic Auth +username: Your Kibana username +password: Your Kibana password + + +##### Request header +``` +Content-Type: application/json +kbn-version: 8.0.0 +``` +##### Request body +```json +{ + "timeline": { + "columns": [ + { + "columnHeaderType": "not-filtered", + "id": "@timestamp" + }, + { + "columnHeaderType": "not-filtered", + "id": "message" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.category" + }, + { + "columnHeaderType": "not-filtered", + "id": "event.action" + }, + { + "columnHeaderType": "not-filtered", + "id": "host.name" + }, + { + "columnHeaderType": "not-filtered", + "id": "source.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "destination.ip" + }, + { + "columnHeaderType": "not-filtered", + "id": "user.name" + } + ], + "dataProviders": [], + "description": "", + "eventType": "all", + "filters": [], + "kqlMode": "filter", + "kqlQuery": { + "filterQuery": null + }, + "title": "abd", + "dateRange": { + "start": 1587370079200, + "end": 1587456479201 + }, + "savedQueryId": null, + "sort": { + "columnId": "@timestamp", + "sortDirection": "desc" + }, + "timelineType": "template", + "created": 1587473119992, + "createdBy": "casetester", + "updated": 1587473119992, + "updatedBy": "casetester", + "templateTimelineId": "745d0316-6af7-43bf-afd6-9747119754fb", // Please provide the existing template timeline version + "templateTimelineVersion": 2 // Please provide a template timeline version grater than existing one + }, + "timelineId":"f5a4bd10-83cd-11ea-bf78-0547a65f1281", // This is a must as well + "version":"Wzg2LDFd" // Please provide the existing timeline version +} +``` \ No newline at end of file diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/import_timelines.ts b/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/import_timelines.ts index 686f2b491cf881..a832c818d48b07 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/import_timelines.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/import_timelines.ts @@ -5,6 +5,7 @@ */ import { omit } from 'lodash/fp'; +import { TimelineType } from '../../../../../common/types/timeline'; export const mockDuplicateIdErrors = []; @@ -148,6 +149,13 @@ export const mockGetTimelineValue = { pinnedEventIds: ['k-gi8nABm-sIqJ_scOoS'], }; +export const mockGetTemplateTimelineValue = { + ...mockGetTimelineValue, + timelineType: TimelineType.template, + templateTimelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', + templateTimelineVersion: 1, +}; + export const mockParsedTimelineObject = omit( [ 'globalNotes', diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts b/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts index a83c4437733021..304ca309775ff8 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/__mocks__/request_responses.ts @@ -3,10 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { TIMELINE_EXPORT_URL, TIMELINE_IMPORT_URL } from '../../../../../common/constants'; -import { requestMock } from '../../../detection_engine/routes/__mocks__'; +import * as rt from 'io-ts'; +import { + TIMELINE_EXPORT_URL, + TIMELINE_IMPORT_URL, + TIMELINE_URL, +} from '../../../../../common/constants'; import stream from 'stream'; +import { requestMock } from '../../../detection_engine/routes/__mocks__'; +import { SavedTimeline, TimelineType } from '../../../../../common/types/timeline'; +import { updateTimelineSchema } from '../schemas/update_timelines_schema'; +import { createTimelineSchema } from '../schemas/create_timelines_schema'; + const readable = new stream.Readable(); export const getExportTimelinesRequest = () => requestMock.create({ @@ -31,6 +39,96 @@ export const getImportTimelinesRequest = (filename?: string) => }, }); +export const inputTimeline: SavedTimeline = { + columns: [ + { columnHeaderType: 'not-filtered', id: '@timestamp' }, + { columnHeaderType: 'not-filtered', id: 'message' }, + { columnHeaderType: 'not-filtered', id: 'event.category' }, + { columnHeaderType: 'not-filtered', id: 'event.action' }, + { columnHeaderType: 'not-filtered', id: 'host.name' }, + { columnHeaderType: 'not-filtered', id: 'source.ip' }, + { columnHeaderType: 'not-filtered', id: 'destination.ip' }, + { columnHeaderType: 'not-filtered', id: 'user.name' }, + ], + dataProviders: [], + description: '', + eventType: 'all', + filters: [], + kqlMode: 'filter', + kqlQuery: { filterQuery: null }, + title: 't', + timelineType: TimelineType.default, + templateTimelineId: null, + templateTimelineVersion: null, + dateRange: { start: 1585227005527, end: 1585313405527 }, + savedQueryId: null, + sort: { columnId: '@timestamp', sortDirection: 'desc' }, +}; + +export const inputTemplateTimeline = { + ...inputTimeline, + timelineType: TimelineType.template, + templateTimelineId: null, + templateTimelineVersion: null, +}; + +export const createTimelineWithoutTimelineId = { + templateTimelineId: null, + timeline: inputTimeline, + timelineId: null, + version: null, + timelineType: TimelineType.default, +}; + +export const createTemplateTimelineWithoutTimelineId = { + templateTimelineId: null, + timeline: inputTemplateTimeline, + timelineId: null, + version: null, + timelineType: TimelineType.template, +}; + +export const createTimelineWithTimelineId = { + ...createTimelineWithoutTimelineId, + timelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', +}; + +export const createTemplateTimelineWithTimelineId = { + ...createTemplateTimelineWithoutTimelineId, + timelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', + templateTimelineId: 'existing template timeline id', +}; + +export const updateTimelineWithTimelineId = { + timeline: inputTimeline, + timelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', + version: 'WzEyMjUsMV0=', +}; + +export const updateTemplateTimelineWithTimelineId = { + timeline: { + ...inputTemplateTimeline, + templateTimelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', + templateTimelineVersion: 2, + }, + timelineId: '79deb4c0-6bc1-11ea-a90b-f5341fb7a189', + version: 'WzEyMjUsMV0=', +}; + +export const getCreateTimelinesRequest = (mockBody: rt.TypeOf) => + requestMock.create({ + method: 'post', + path: TIMELINE_URL, + body: mockBody, + }); + +export const getUpdateTimelinesRequest = (mockBody: rt.TypeOf) => + requestMock.create({ + method: 'patch', + path: TIMELINE_URL, + body: mockBody, + }); + export const getImportTimelinesRequestEnableOverwrite = (filename?: string) => requestMock.create({ method: 'post', diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.test.ts new file mode 100644 index 00000000000000..70ee1532395a5d --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.test.ts @@ -0,0 +1,272 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { SecurityPluginSetup } from '../../../../../../plugins/security/server'; + +import { + serverMock, + requestContextMock, + createMockConfig, +} from '../../detection_engine/routes/__mocks__'; + +import { + mockGetCurrentUser, + mockGetTimelineValue, + mockGetTemplateTimelineValue, +} from './__mocks__/import_timelines'; +import { + getCreateTimelinesRequest, + inputTimeline, + createTimelineWithoutTimelineId, + createTimelineWithTimelineId, + createTemplateTimelineWithoutTimelineId, + createTemplateTimelineWithTimelineId, +} from './__mocks__/request_responses'; +import { + CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, + CREATE_TIMELINE_ERROR_MESSAGE, +} from './utils/create_timelines'; + +describe('create timelines', () => { + let server: ReturnType; + let securitySetup: SecurityPluginSetup; + let { context } = requestContextMock.createTools(); + let mockGetTimeline: jest.Mock; + let mockPersistTimeline: jest.Mock; + let mockPersistPinnedEventOnTimeline: jest.Mock; + let mockPersistNote: jest.Mock; + + beforeEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + server = serverMock.create(); + context = requestContextMock.createTools().context; + + securitySetup = ({ + authc: { + getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), + }, + authz: {}, + } as unknown) as SecurityPluginSetup; + + mockGetTimeline = jest.fn(); + mockPersistTimeline = jest.fn(); + mockPersistPinnedEventOnTimeline = jest.fn(); + mockPersistNote = jest.fn(); + }); + + describe('Manipulate timeline', () => { + describe('Create a new timeline', () => { + beforeEach(async () => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(null), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: createTimelineWithTimelineId, + }), + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const createTimelinesRoute = jest.requireActual('./create_timelines_route') + .createTimelinesRoute; + createTimelinesRoute(server.router, createMockConfig(), securitySetup); + + const mockRequest = getCreateTimelinesRequest(createTimelineWithoutTimelineId); + await server.inject(mockRequest, context); + }); + + test('should Create a new timeline savedObject', async () => { + expect(mockPersistTimeline).toHaveBeenCalled(); + }); + + test('should Create a new timeline savedObject without timelineId', async () => { + expect(mockPersistTimeline.mock.calls[0][1]).toBeNull(); + }); + + test('should Create a new timeline savedObject without timeline version', async () => { + expect(mockPersistTimeline.mock.calls[0][2]).toBeNull(); + }); + + test('should Create a new timeline savedObject witn given timeline', async () => { + expect(mockPersistTimeline.mock.calls[0][3]).toEqual(inputTimeline); + }); + + test('should NOT Create new pinned events', async () => { + expect(mockPersistPinnedEventOnTimeline).not.toBeCalled(); + }); + + test('should NOT Create notes', async () => { + expect(mockPersistNote).not.toBeCalled(); + }); + + test('returns 200 when create timeline successfully', async () => { + const response = await server.inject( + getCreateTimelinesRequest(createTimelineWithoutTimelineId), + context + ); + expect(response.status).toEqual(200); + }); + }); + + describe('Import a timeline already exist', () => { + beforeEach(() => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(mockGetTimelineValue), + persistTimeline: mockPersistTimeline, + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const createTimelinesRoute = jest.requireActual('./create_timelines_route') + .createTimelinesRoute; + createTimelinesRoute(server.router, createMockConfig(), securitySetup); + }); + + test('returns error message', async () => { + const response = await server.inject( + getCreateTimelinesRequest(createTimelineWithTimelineId), + context + ); + expect(response.body).toEqual({ + message: CREATE_TIMELINE_ERROR_MESSAGE, + status_code: 405, + }); + }); + }); + }); + + describe('Manipulate template timeline', () => { + describe('Create a new template timeline', () => { + beforeEach(async () => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(null), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: createTemplateTimelineWithTimelineId, + }), + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const createTimelinesRoute = jest.requireActual('./create_timelines_route') + .createTimelinesRoute; + createTimelinesRoute(server.router, createMockConfig(), securitySetup); + + const mockRequest = getCreateTimelinesRequest(createTemplateTimelineWithoutTimelineId); + await server.inject(mockRequest, context); + }); + + test('should Create a new template timeline savedObject', async () => { + expect(mockPersistTimeline).toHaveBeenCalled(); + }); + + test('should Create a new template timeline savedObject without timelineId', async () => { + expect(mockPersistTimeline.mock.calls[0][1]).toBeNull(); + }); + + test('should Create a new template timeline savedObject without template timeline version', async () => { + expect(mockPersistTimeline.mock.calls[0][2]).toBeNull(); + }); + + test('should Create a new template timeline savedObject witn given template timeline', async () => { + expect(mockPersistTimeline.mock.calls[0][3]).toEqual( + createTemplateTimelineWithTimelineId.timeline + ); + }); + + test('should NOT Create new pinned events', async () => { + expect(mockPersistPinnedEventOnTimeline).not.toBeCalled(); + }); + + test('should NOT Create notes', async () => { + expect(mockPersistNote).not.toBeCalled(); + }); + + test('returns 200 when create timeline successfully', async () => { + const response = await server.inject( + getCreateTimelinesRequest(createTimelineWithoutTimelineId), + context + ); + expect(response.status).toEqual(200); + }); + }); + + describe('Import a template timeline already exist', () => { + beforeEach(() => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(mockGetTemplateTimelineValue), + persistTimeline: mockPersistTimeline, + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const createTimelinesRoute = jest.requireActual('./create_timelines_route') + .createTimelinesRoute; + createTimelinesRoute(server.router, createMockConfig(), securitySetup); + }); + + test('returns error message', async () => { + const response = await server.inject( + getCreateTimelinesRequest(createTemplateTimelineWithTimelineId), + context + ); + expect(response.body).toEqual({ + message: CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, + status_code: 405, + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.ts b/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.ts new file mode 100644 index 00000000000000..c456ae31fb7da6 --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/create_timelines_route.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IRouter } from '../../../../../../../src/core/server'; + +import { TIMELINE_URL } from '../../../../common/constants'; +import { TimelineType } from '../../../../common/types/timeline'; + +import { ConfigType } from '../../..'; +import { SetupPlugins } from '../../../plugin'; +import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; + +import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils'; + +import { createTimelineSchema } from './schemas/create_timelines_schema'; +import { buildFrameworkRequest } from './utils/common'; +import { + createTimelines, + getTimeline, + getTemplateTimeline, + CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, + CREATE_TIMELINE_ERROR_MESSAGE, +} from './utils/create_timelines'; + +export const createTimelinesRoute = ( + router: IRouter, + config: ConfigType, + security: SetupPlugins['security'] +) => { + router.post( + { + path: TIMELINE_URL, + validate: { + body: buildRouteValidation(createTimelineSchema), + }, + options: { + tags: ['access:siem'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + + const { timelineId, timeline, version } = request.body; + const { templateTimelineId, timelineType } = timeline; + const isHandlingTemplateTimeline = timelineType === TimelineType.template; + + const existTimeline = + timelineId != null ? await getTimeline(frameworkRequest, timelineId) : null; + const existTemplateTimeline = + templateTimelineId != null + ? await getTemplateTimeline(frameworkRequest, templateTimelineId) + : null; + + if ( + (!isHandlingTemplateTimeline && existTimeline != null) || + (isHandlingTemplateTimeline && (existTemplateTimeline != null || existTimeline != null)) + ) { + return siemResponse.error({ + body: isHandlingTemplateTimeline + ? CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE + : CREATE_TIMELINE_ERROR_MESSAGE, + statusCode: 405, + }); + } + + // Create timeline + const newTimeline = await createTimelines(frameworkRequest, timeline, null, version); + return response.ok({ + body: { + data: { + persistTimeline: newTimeline, + }, + }, + }); + } catch (err) { + const error = transformError(err); + + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts index 9f41943cfa27f7..56c152d02ae98d 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts @@ -6,13 +6,13 @@ import { getImportTimelinesRequest } from './__mocks__/request_responses'; import { - createMockConfig, serverMock, requestContextMock, requestMock, + createMockConfig, } from '../../detection_engine/routes/__mocks__'; import { TIMELINE_EXPORT_URL } from '../../../../common/constants'; -import { SecurityPluginSetup } from '../../../../../security/server'; +import { SecurityPluginSetup } from '../../../../../../plugins/security/server'; import { mockUniqueParsedObjects, @@ -24,7 +24,6 @@ import { } from './__mocks__/import_timelines'; describe('import timelines', () => { - let config: ReturnType; let server: ReturnType; let request: ReturnType; let securitySetup: SecurityPluginSetup; @@ -43,7 +42,6 @@ describe('import timelines', () => { server = serverMock.create(); context = requestContextMock.createTools().context; - config = createMockConfig(); securitySetup = ({ authc: { @@ -84,40 +82,28 @@ describe('import timelines', () => { beforeEach(() => { jest.doMock('../saved_object', () => { return { - Timeline: jest.fn().mockImplementation(() => { - return { - getTimeline: mockGetTimeline.mockReturnValue(null), - persistTimeline: mockPersistTimeline.mockReturnValue({ - timeline: { savedObjectId: newTimelineSavedObjectId, version: newTimelineVersion }, - }), - }; + getTimeline: mockGetTimeline.mockReturnValue(null), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: { savedObjectId: newTimelineSavedObjectId, version: newTimelineVersion }, }), }; }); jest.doMock('../../pinned_event/saved_object', () => { return { - PinnedEvent: jest.fn().mockImplementation(() => { - return { - persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, - }; - }), + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, }; }); jest.doMock('../../note/saved_object', () => { return { - Note: jest.fn().mockImplementation(() => { - return { - persistNote: mockPersistNote, - }; - }), + persistNote: mockPersistNote, }; }); const importTimelinesRoute = jest.requireActual('./import_timelines_route') .importTimelinesRoute; - importTimelinesRoute(server.router, config, securitySetup); + importTimelinesRoute(server.router, createMockConfig(), securitySetup); }); test('should use given timelineId to check if the timeline savedObject already exist', async () => { @@ -230,38 +216,26 @@ describe('import timelines', () => { beforeEach(() => { jest.doMock('../saved_object', () => { return { - Timeline: jest.fn().mockImplementation(() => { - return { - getTimeline: mockGetTimeline.mockReturnValue(mockGetTimelineValue), - persistTimeline: mockPersistTimeline, - }; - }), + getTimeline: mockGetTimeline.mockReturnValue(mockGetTimelineValue), + persistTimeline: mockPersistTimeline, }; }); jest.doMock('../../pinned_event/saved_object', () => { return { - PinnedEvent: jest.fn().mockImplementation(() => { - return { - persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, - }; - }), + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, }; }); jest.doMock('../../note/saved_object', () => { return { - Note: jest.fn().mockImplementation(() => { - return { - persistNote: mockPersistNote, - }; - }), + persistNote: mockPersistNote, }; }); const importTimelinesRoute = jest.requireActual('./import_timelines_route') .importTimelinesRoute; - importTimelinesRoute(server.router, config, securitySetup); + importTimelinesRoute(server.router, createMockConfig(), securitySetup); }); test('returns error message', async () => { @@ -286,36 +260,24 @@ describe('import timelines', () => { beforeEach(() => { jest.doMock('../saved_object', () => { return { - Timeline: jest.fn().mockImplementation(() => { - return { - getTimeline: mockGetTimeline.mockReturnValue(null), - persistTimeline: mockPersistTimeline.mockReturnValue({ - timeline: { savedObjectId: '79deb4c0-6bc1-11ea-9999-f5341fb7a189' }, - }), - }; + getTimeline: mockGetTimeline.mockReturnValue(null), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: { savedObjectId: '79deb4c0-6bc1-11ea-9999-f5341fb7a189' }, }), }; }); jest.doMock('../../pinned_event/saved_object', () => { return { - PinnedEvent: jest.fn().mockImplementation(() => { - return { - persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline.mockReturnValue( - new Error('Test error') - ), - }; - }), + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline.mockReturnValue( + new Error('Test error') + ), }; }); jest.doMock('../../note/saved_object', () => { return { - Note: jest.fn().mockImplementation(() => { - return { - persistNote: mockPersistNote, - }; - }), + persistNote: mockPersistNote, }; }); }); @@ -328,11 +290,14 @@ describe('import timelines', () => { const importTimelinesRoute = jest.requireActual('./import_timelines_route') .importTimelinesRoute; - importTimelinesRoute(server.router, config, securitySetup); + importTimelinesRoute(server.router, createMockConfig(), securitySetup); const result = server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "file",Invalid value "undefined" supplied to "file"' + [ + 'Invalid value "undefined" supplied to "file"', + 'Invalid value "undefined" supplied to "file"', + ].join(',') ); }); }); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts index 9d148abf82cddd..bff89bdf9b5b21 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts @@ -5,9 +5,19 @@ */ import { extname } from 'path'; -import { chunk, omit, set } from 'lodash/fp'; +import { chunk, omit } from 'lodash/fp'; + +import { createPromiseFromStreams } from '../../../../../../../src/legacy/utils'; +import { IRouter } from '../../../../../../../src/core/server'; import { TIMELINE_IMPORT_URL } from '../../../../common/constants'; + +import { SetupPlugins } from '../../../plugin'; +import { ConfigType } from '../../../config'; +import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; + +import { importRulesSchema } from '../../detection_engine/routes/schemas/response/import_rules_schema'; +import { validate } from '../../detection_engine/routes/rules/validate'; import { buildSiemResponse, createBulkErrorObject, @@ -16,32 +26,22 @@ import { } from '../../detection_engine/routes/utils'; import { createTimelinesStreamFromNdJson } from '../create_timelines_stream_from_ndjson'; -import { createPromiseFromStreams } from '../../../../../../../src/legacy/utils'; +import { ImportTimelinesPayloadSchemaRt } from './schemas/import_timelines_schema'; +import { buildFrameworkRequest } from './utils/common'; import { - createTimelines, getTupleDuplicateErrorsAndUniqueTimeline, isBulkError, isImportRegular, ImportTimelineResponse, ImportTimelinesSchema, PromiseFromStreams, + timelineSavedObjectOmittedFields, } from './utils/import_timelines'; +import { createTimelines, getTimeline } from './utils/create_timelines'; -import { IRouter } from '../../../../../../../src/core/server'; -import { SetupPlugins } from '../../../plugin'; -import { ImportTimelinesPayloadSchemaRt } from './schemas/import_timelines_schema'; -import { importRulesSchema } from '../../detection_engine/routes/schemas/response/import_rules_schema'; -import { ConfigType } from '../../../config'; - -import { Timeline } from '../saved_object'; -import { validate } from '../../detection_engine/routes/rules/validate'; -import { FrameworkRequest } from '../../framework'; -import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; const CHUNK_PARSED_OBJECT_SIZE = 10; -const timelineLib = new Timeline(); - export const importTimelinesRoute = ( router: IRouter, config: ConfigType, @@ -95,9 +95,7 @@ export const importTimelinesRoute = ( const chunkParseObjects = chunk(CHUNK_PARSED_OBJECT_SIZE, uniqueParsedObjects); let importTimelineResponse: ImportTimelineResponse[] = []; - const user = await security?.authc.getCurrentUser(request); - let frameworkRequest = set('context.core.savedObjects.client', savedObjectsClient, request); - frameworkRequest = set('user', user, frameworkRequest); + const frameworkRequest = await buildFrameworkRequest(context, security, request); while (chunkParseObjects.length) { const batchParseObjects = chunkParseObjects.shift() ?? []; @@ -125,32 +123,16 @@ export const importTimelinesRoute = ( eventNotes, } = parsedTimeline; const parsedTimelineObject = omit( - [ - 'globalNotes', - 'eventNotes', - 'pinnedEventIds', - 'version', - 'savedObjectId', - 'created', - 'createdBy', - 'updated', - 'updatedBy', - ], + timelineSavedObjectOmittedFields, parsedTimeline ); + let newTimeline = null; try { - let timeline = null; - try { - timeline = await timelineLib.getTimeline( - (frameworkRequest as unknown) as FrameworkRequest, - savedObjectId - ); - // eslint-disable-next-line no-empty - } catch (e) {} + const timeline = await getTimeline(frameworkRequest, savedObjectId); if (timeline == null) { - const newSavedObjectId = await createTimelines( - (frameworkRequest as unknown) as FrameworkRequest, + newTimeline = await createTimelines( + frameworkRequest, parsedTimelineObject, null, // timelineSavedObjectId null, // timelineVersion @@ -159,7 +141,10 @@ export const importTimelinesRoute = ( [] // existing note ids ); - resolve({ timeline_id: newSavedObjectId, status_code: 200 }); + resolve({ + timeline_id: newTimeline.timeline.savedObjectId, + status_code: 200, + }); } else { resolve( createBulkErrorObject({ diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/create_timelines_schema.ts b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/create_timelines_schema.ts new file mode 100644 index 00000000000000..241d266a14c78f --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/create_timelines_schema.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 * as rt from 'io-ts'; + +import { SavedTimelineRuntimeType } from '../../../../../common/types/timeline'; +import { unionWithNullType } from '../../../../../common/utility_types'; + +export const createTimelineSchema = rt.intersection([ + rt.type({ + timeline: SavedTimelineRuntimeType, + }), + rt.partial({ + timelineId: unionWithNullType(rt.string), + version: unionWithNullType(rt.string), + }), +]); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/import_timelines_schema.ts b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/import_timelines_schema.ts index 056fdaf0d2515f..3b340b1c15359f 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/import_timelines_schema.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/import_timelines_schema.ts @@ -7,14 +7,17 @@ import * as rt from 'io-ts'; import { Readable } from 'stream'; import { either } from 'fp-ts/lib/Either'; + +import { SavedTimelineRuntimeType } from '../../../../../common/types/timeline'; + import { eventNotes, globalNotes, pinnedEventIds } from './schemas'; -import { SavedTimelineRuntimeType } from '../../types'; +import { unionWithNullType } from '../../../../../common/utility_types'; export const ImportTimelinesSchemaRt = rt.intersection([ SavedTimelineRuntimeType, rt.type({ - savedObjectId: rt.string, - version: rt.string, + savedObjectId: unionWithNullType(rt.string), + version: unionWithNullType(rt.string), }), rt.type({ globalNotes, diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts index 71627363ef0f88..1fd3a3554dc155 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/schemas.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import * as runtimeTypes from 'io-ts'; -import { unionWithNullType } from '../../../framework'; -import { SavedNoteRuntimeType } from '../../../note/types'; +import { unionWithNullType } from '../../../../../common/utility_types'; +import { SavedNoteRuntimeType } from '../../../../../common/types/timeline/note'; -export const eventNotes = runtimeTypes.array(unionWithNullType(SavedNoteRuntimeType)); -export const globalNotes = runtimeTypes.array(unionWithNullType(SavedNoteRuntimeType)); -export const pinnedEventIds = runtimeTypes.array(unionWithNullType(runtimeTypes.string)); +export const eventNotes = unionWithNullType(runtimeTypes.array(SavedNoteRuntimeType)); +export const globalNotes = unionWithNullType(runtimeTypes.array(SavedNoteRuntimeType)); +export const pinnedEventIds = unionWithNullType(runtimeTypes.array(runtimeTypes.string)); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/schemas/update_timelines_schema.ts b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/update_timelines_schema.ts new file mode 100644 index 00000000000000..43f4208947aa50 --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/schemas/update_timelines_schema.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 * as rt from 'io-ts'; + +import { SavedTimelineRuntimeType } from '../../../../../common/types/timeline'; +import { unionWithNullType } from '../../../../../common/utility_types'; + +export const updateTimelineSchema = rt.type({ + timeline: SavedTimelineRuntimeType, + timelineId: unionWithNullType(rt.string), + version: unionWithNullType(rt.string), +}); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.test.ts new file mode 100644 index 00000000000000..9c47488d47159a --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.test.ts @@ -0,0 +1,288 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SecurityPluginSetup } from '../../../../../../plugins/security/server'; + +import { + serverMock, + requestContextMock, + createMockConfig, +} from '../../detection_engine/routes/__mocks__'; + +import { + getUpdateTimelinesRequest, + inputTimeline, + updateTimelineWithTimelineId, + updateTemplateTimelineWithTimelineId, +} from './__mocks__/request_responses'; +import { + mockGetCurrentUser, + mockGetTimelineValue, + mockGetTemplateTimelineValue, +} from './__mocks__/import_timelines'; +import { + UPDATE_TIMELINE_ERROR_MESSAGE, + UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, +} from './utils/update_timelines'; + +describe('update timelines', () => { + let server: ReturnType; + let securitySetup: SecurityPluginSetup; + let { context } = requestContextMock.createTools(); + let mockGetTimeline: jest.Mock; + let mockGetTemplateTimeline: jest.Mock; + let mockPersistTimeline: jest.Mock; + let mockPersistPinnedEventOnTimeline: jest.Mock; + let mockPersistNote: jest.Mock; + + beforeEach(() => { + jest.resetModules(); + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + server = serverMock.create(); + context = requestContextMock.createTools().context; + + securitySetup = ({ + authc: { + getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), + }, + authz: {}, + } as unknown) as SecurityPluginSetup; + + mockGetTimeline = jest.fn(); + mockGetTemplateTimeline = jest.fn(); + mockPersistTimeline = jest.fn(); + mockPersistPinnedEventOnTimeline = jest.fn(); + mockPersistNote = jest.fn(); + }); + + describe('Manipulate timeline', () => { + describe('Update an existing timeline', () => { + beforeEach(async () => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(mockGetTimelineValue), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: updateTimelineWithTimelineId.timeline, + }), + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const updateTimelinesRoute = jest.requireActual('./update_timelines_route') + .updateTimelinesRoute; + updateTimelinesRoute(server.router, createMockConfig(), securitySetup); + + const mockRequest = getUpdateTimelinesRequest(updateTimelineWithTimelineId); + await server.inject(mockRequest, context); + }); + + test('should Check a if given timeline id exist', async () => { + expect(mockGetTimeline.mock.calls[0][1]).toEqual(updateTimelineWithTimelineId.timelineId); + }); + + test('should Update existing timeline savedObject with timelineId', async () => { + expect(mockPersistTimeline.mock.calls[0][1]).toEqual( + updateTimelineWithTimelineId.timelineId + ); + }); + + test('should Update existing timeline savedObject with timeline version', async () => { + expect(mockPersistTimeline.mock.calls[0][2]).toEqual(updateTimelineWithTimelineId.version); + }); + + test('should Update existing timeline savedObject witn given timeline', async () => { + expect(mockPersistTimeline.mock.calls[0][3]).toEqual(inputTimeline); + }); + + test('should NOT Update new pinned events', async () => { + expect(mockPersistPinnedEventOnTimeline).not.toBeCalled(); + }); + + test('should NOT Update notes', async () => { + expect(mockPersistNote).not.toBeCalled(); + }); + + test('returns 200 when create timeline successfully', async () => { + const response = await server.inject( + getUpdateTimelinesRequest(updateTimelineWithTimelineId), + context + ); + expect(response.status).toEqual(200); + }); + }); + + describe("Update a timeline that doesn't exist", () => { + beforeEach(() => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(null), + getTimelineByTemplateTimelineId: mockGetTemplateTimeline.mockReturnValue(null), + persistTimeline: mockPersistTimeline, + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const updateTimelinesRoute = jest.requireActual('./update_timelines_route') + .updateTimelinesRoute; + updateTimelinesRoute(server.router, createMockConfig(), securitySetup); + }); + + test('returns error message', async () => { + const response = await server.inject( + getUpdateTimelinesRequest(updateTimelineWithTimelineId), + context + ); + expect(response.body).toEqual({ + message: UPDATE_TIMELINE_ERROR_MESSAGE, + status_code: 405, + }); + }); + }); + }); + + describe('Manipulate template timeline', () => { + describe('Update an existing template timeline', () => { + beforeEach(async () => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(mockGetTemplateTimelineValue), + getTimelineByTemplateTimelineId: mockGetTemplateTimeline.mockReturnValue({ + timeline: [mockGetTemplateTimelineValue], + }), + persistTimeline: mockPersistTimeline.mockReturnValue({ + timeline: updateTimelineWithTimelineId.timeline, + }), + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const updateTimelinesRoute = jest.requireActual('./update_timelines_route') + .updateTimelinesRoute; + updateTimelinesRoute(server.router, createMockConfig(), securitySetup); + + const mockRequest = getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId); + await server.inject(mockRequest, context); + }); + + test('should Check if given timeline id exist', async () => { + expect(mockGetTimeline.mock.calls[0][1]).toEqual( + updateTemplateTimelineWithTimelineId.timelineId + ); + }); + + test('should Update existing template timeline with template timelineId', async () => { + expect(mockGetTemplateTimeline.mock.calls[0][1]).toEqual( + updateTemplateTimelineWithTimelineId.timelineId + ); + }); + + test('should Update existing template timeline with timeline version', async () => { + expect(mockPersistTimeline.mock.calls[0][2]).toEqual( + updateTemplateTimelineWithTimelineId.version + ); + }); + + test('should Update existing template timeline witn given timeline', async () => { + expect(mockPersistTimeline.mock.calls[0][3]).toEqual( + updateTemplateTimelineWithTimelineId.timeline + ); + }); + + test('should NOT Update new pinned events', async () => { + expect(mockPersistPinnedEventOnTimeline).not.toBeCalled(); + }); + + test('should NOT Update notes', async () => { + expect(mockPersistNote).not.toBeCalled(); + }); + + test('returns 200 when create template timeline successfully', async () => { + const response = await server.inject( + getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId), + context + ); + expect(response.status).toEqual(200); + }); + }); + + describe("Update a template timeline that doesn't exist", () => { + beforeEach(() => { + jest.doMock('../saved_object', () => { + return { + getTimeline: mockGetTimeline.mockReturnValue(null), + getTimelineByTemplateTimelineId: mockGetTemplateTimeline.mockReturnValue({ + timeline: [], + }), + persistTimeline: mockPersistTimeline, + }; + }); + + jest.doMock('../../pinned_event/saved_object', () => { + return { + persistPinnedEventOnTimeline: mockPersistPinnedEventOnTimeline, + }; + }); + + jest.doMock('../../note/saved_object', () => { + return { + persistNote: mockPersistNote, + }; + }); + + const updateTimelinesRoute = jest.requireActual('./update_timelines_route') + .updateTimelinesRoute; + updateTimelinesRoute(server.router, createMockConfig(), securitySetup); + }); + + test('returns error message', async () => { + const response = await server.inject( + getUpdateTimelinesRequest(updateTemplateTimelineWithTimelineId), + context + ); + expect(response.body).toEqual({ + message: UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, + status_code: 405, + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.ts b/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.ts new file mode 100644 index 00000000000000..a0f3d11a1533de --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/update_timelines_route.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from '../../../../../../../src/core/server'; + +import { TIMELINE_URL } from '../../../../common/constants'; +import { TimelineType } from '../../../../common/types/timeline'; + +import { SetupPlugins } from '../../../plugin'; +import { buildRouteValidation } from '../../../utils/build_validation/route_validation'; +import { ConfigType } from '../../..'; + +import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils'; +import { FrameworkRequest } from '../../framework'; + +import { updateTimelineSchema } from './schemas/update_timelines_schema'; +import { buildFrameworkRequest } from './utils/common'; +import { createTimelines, getTimeline, getTemplateTimeline } from './utils/create_timelines'; +import { checkIsFailureCases } from './utils/update_timelines'; + +export const updateTimelinesRoute = ( + router: IRouter, + config: ConfigType, + security: SetupPlugins['security'] +) => { + router.patch( + { + path: TIMELINE_URL, + validate: { + body: buildRouteValidation(updateTimelineSchema), + }, + options: { + tags: ['access:siem'], + }, + }, + // eslint-disable-next-line complexity + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const frameworkRequest = await buildFrameworkRequest(context, security, request); + const { timelineId, timeline, version } = request.body; + const { templateTimelineId, templateTimelineVersion, timelineType } = timeline; + const isHandlingTemplateTimeline = timelineType === TimelineType.template; + const existTimeline = + timelineId != null ? await getTimeline(frameworkRequest, timelineId) : null; + + const existTemplateTimeline = + templateTimelineId != null + ? await getTemplateTimeline(frameworkRequest, templateTimelineId) + : null; + const errorObj = checkIsFailureCases( + isHandlingTemplateTimeline, + version, + templateTimelineVersion ?? null, + existTimeline, + existTemplateTimeline + ); + if (errorObj != null) { + return siemResponse.error(errorObj); + } + const updatedTimeline = await createTimelines( + (frameworkRequest as unknown) as FrameworkRequest, + timeline, + timelineId, + version + ); + return response.ok({ + body: { + data: { + persistTimeline: updatedTimeline, + }, + }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.ts new file mode 100644 index 00000000000000..1036a74b74a03c --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/common.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 { set } from 'lodash/fp'; + +import { SetupPlugins } from '../../../../plugin'; +import { KibanaRequest } from '../../../../../../../../src/core/server'; +import { RequestHandlerContext } from '../../../../../../../../target/types/core/server'; +import { FrameworkRequest } from '../../../framework'; + +export const buildFrameworkRequest = async ( + context: RequestHandlerContext, + security: SetupPlugins['security'], + request: KibanaRequest +): Promise => { + const savedObjectsClient = context.core.savedObjects.client; + const user = await security?.authc.getCurrentUser(request); + + return set( + 'user', + user, + set( + 'context.core.savedObjects.client', + savedObjectsClient, + request + ) + ); +}; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/create_timelines.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/create_timelines.ts new file mode 100644 index 00000000000000..2c67a514cdf979 --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/create_timelines.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 } from 'lodash/fp'; + +import * as timelineLib from '../../saved_object'; +import * as pinnedEventLib from '../../../pinned_event/saved_object'; +import * as noteLib from '../../../note/saved_object'; +import { FrameworkRequest } from '../../../framework'; +import { SavedTimeline, TimelineSavedObject } from '../../../../../common/types/timeline'; +import { SavedNote } from '../../../../../common/types/timeline/note'; +import { NoteResult, ResponseTimeline } from '../../../../graphql/types'; +export const CREATE_TIMELINE_ERROR_MESSAGE = + 'UPDATE timeline with POST is not allowed, please use PATCH instead'; +export const CREATE_TEMPLATE_TIMELINE_ERROR_MESSAGE = + 'UPDATE template timeline with POST is not allowed, please use PATCH instead'; + +export const saveTimelines = ( + frameworkRequest: FrameworkRequest, + timeline: SavedTimeline, + timelineSavedObjectId?: string | null, + timelineVersion?: string | null +): Promise => { + return timelineLib.persistTimeline( + frameworkRequest, + timelineSavedObjectId ?? null, + timelineVersion ?? null, + timeline + ); +}; + +export const savePinnedEvents = ( + frameworkRequest: FrameworkRequest, + timelineSavedObjectId: string, + pinnedEventIds: string[] +) => + Promise.all( + pinnedEventIds.map(eventId => + pinnedEventLib.persistPinnedEventOnTimeline( + frameworkRequest, + null, // pinnedEventSavedObjectId + eventId, + timelineSavedObjectId + ) + ) + ); + +export const saveNotes = ( + frameworkRequest: FrameworkRequest, + timelineSavedObjectId: string, + timelineVersion?: string | null, + existingNoteIds?: string[], + newNotes?: NoteResult[] +) => { + return Promise.all( + newNotes?.map(note => { + const newNote: SavedNote = { + eventId: note.eventId, + note: note.note, + timelineId: timelineSavedObjectId, + }; + + return noteLib.persistNote( + frameworkRequest, + existingNoteIds?.find(nId => nId === note.noteId) ?? null, + timelineVersion ?? null, + newNote + ); + }) ?? [] + ); +}; + +export const createTimelines = async ( + frameworkRequest: FrameworkRequest, + timeline: SavedTimeline, + timelineSavedObjectId?: string | null, + timelineVersion?: string | null, + pinnedEventIds?: string[] | null, + notes?: NoteResult[], + existingNoteIds?: string[] +): Promise => { + const responseTimeline = await saveTimelines( + frameworkRequest, + timeline, + timelineSavedObjectId, + timelineVersion + ); + const newTimelineSavedObjectId = responseTimeline.timeline.savedObjectId; + const newTimelineVersion = responseTimeline.timeline.version; + + let myPromises: unknown[] = []; + if (pinnedEventIds != null && !isEmpty(pinnedEventIds)) { + myPromises = [ + ...myPromises, + savePinnedEvents( + frameworkRequest, + timelineSavedObjectId ?? newTimelineSavedObjectId, + pinnedEventIds + ), + ]; + } + if (!isEmpty(notes)) { + myPromises = [ + ...myPromises, + saveNotes( + frameworkRequest, + timelineSavedObjectId ?? newTimelineSavedObjectId, + newTimelineVersion, + existingNoteIds, + notes + ), + ]; + } + + if (myPromises.length > 0) { + await Promise.all(myPromises); + } + + return responseTimeline; +}; + +export const getTimeline = async ( + frameworkRequest: FrameworkRequest, + savedObjectId: string +): Promise => { + let timeline = null; + try { + timeline = await timelineLib.getTimeline(frameworkRequest, savedObjectId); + // eslint-disable-next-line no-empty + } catch (e) {} + return timeline; +}; + +export const getTemplateTimeline = async ( + frameworkRequest: FrameworkRequest, + templateTimelineId: string +): Promise => { + let templateTimeline = null; + try { + templateTimeline = await timelineLib.getTimelineByTemplateTimelineId( + frameworkRequest, + templateTimelineId + ); + } catch (e) { + return null; + } + return templateTimeline.timeline[0]; +}; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/export_timelines.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/export_timelines.ts index 677891fa16c024..ea9a5fab66805c 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/utils/export_timelines.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/export_timelines.ts @@ -4,13 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { NoteSavedObject } from '../../../note/types'; -import { PinnedEventSavedObject } from '../../../pinned_event/types'; -import { convertSavedObjectToSavedTimeline } from '../../convert_saved_object_to_savedtimeline'; - -import { convertSavedObjectToSavedPinnedEvent } from '../../../pinned_event/saved_object'; -import { convertSavedObjectToSavedNote } from '../../../note/saved_object'; - import { SavedObjectsClient, SavedObjectsFindOptions, @@ -22,11 +15,20 @@ import { ExportTimelineSavedObjectsClient, ExportedNotes, TimelineSavedObject, -} from '../../types'; + ExportTimelineNotFoundError, +} from '../../../../../common/types/timeline'; +import { NoteSavedObject } from '../../../../../common/types/timeline/note'; +import { PinnedEventSavedObject } from '../../../../../common/types/timeline/pinned_event'; + import { transformDataToNdjson } from '../../../../utils/read_stream/create_stream_from_ndjson'; + +import { convertSavedObjectToSavedPinnedEvent } from '../../../pinned_event/saved_object'; +import { convertSavedObjectToSavedNote } from '../../../note/saved_object'; import { pinnedEventSavedObjectType } from '../../../pinned_event/saved_object_mappings'; import { noteSavedObjectType } from '../../../note/saved_object_mappings'; + import { timelineSavedObjectType } from '../../saved_object_mappings'; +import { convertSavedObjectToSavedTimeline } from '../../convert_saved_object_to_savedtimeline'; export type TimelineSavedObjectsClient = Pick< SavedObjectsClient, @@ -126,12 +128,23 @@ const getTimelines = async ( ) ); - const timelineObjects: TimelineSavedObject[] | undefined = - savedObjects != null - ? savedObjects.saved_objects.map((savedObject: unknown) => { - return convertSavedObjectToSavedTimeline(savedObject); - }) - : []; + const timelineObjects: { + timelines: TimelineSavedObject[]; + errors: ExportTimelineNotFoundError[]; + } = savedObjects.saved_objects.reduce( + (acc, savedObject) => { + return savedObject.error == null + ? { + errors: acc.errors, + timelines: [...acc.timelines, convertSavedObjectToSavedTimeline(savedObject)], + } + : { errors: [...acc.errors, savedObject.error], timelines: acc.timelines }; + }, + { + timelines: [] as TimelineSavedObject[], + errors: [] as ExportTimelineNotFoundError[], + } + ); return timelineObjects; }; @@ -139,12 +152,8 @@ const getTimelines = async ( const getTimelinesFromObjects = async ( savedObjectsClient: ExportTimelineSavedObjectsClient, ids: string[] -): Promise => { - const timelines: TimelineSavedObject[] = await getTimelines(savedObjectsClient, ids); - // To Do for feature freeze - // if (timelines.length !== request.body.ids.length) { - // //figure out which is missing to tell user - // } +): Promise> => { + const { timelines, errors } = await getTimelines(savedObjectsClient, ids); const [notes, pinnedEventIds] = await Promise.all([ Promise.all(ids.map(timelineId => getNotesByTimelineId(savedObjectsClient, timelineId))), @@ -178,7 +187,7 @@ const getTimelinesFromObjects = async ( return acc; }, []); - return myResponse ?? []; + return [...myResponse, ...errors] ?? []; }; export const getExportTimelineByObjectIds = async ({ diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/import_timelines.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/import_timelines.ts index f69a715f9b2c99..9e120cdc023dc1 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/utils/import_timelines.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/import_timelines.ts @@ -7,20 +7,10 @@ import uuid from 'uuid'; import { has } from 'lodash/fp'; import { createBulkErrorObject, BulkError } from '../../../detection_engine/routes/utils'; -import { PinnedEvent } from '../../../pinned_event/saved_object'; -import { Note } from '../../../note/saved_object'; - -import { Timeline } from '../../saved_object'; -import { SavedTimeline } from '../../types'; -import { FrameworkRequest } from '../../../framework'; -import { SavedNote } from '../../../note/types'; +import { SavedTimeline } from '../../../../../common/types/timeline'; import { NoteResult } from '../../../../graphql/types'; import { HapiReadableStream } from '../../../detection_engine/rules/types'; -const pinnedEventLib = new PinnedEvent(); -const timelineLib = new Timeline(); -const noteLib = new Note(); - export interface ImportTimelinesSchema { success: boolean; success_count: number; @@ -84,100 +74,6 @@ export const getTupleDuplicateErrorsAndUniqueTimeline = ( return [Array.from(errors.values()), Array.from(timelinesAcc.values())]; }; -export const saveTimelines = async ( - frameworkRequest: FrameworkRequest, - timeline: SavedTimeline, - timelineSavedObjectId?: string | null, - timelineVersion?: string | null -) => { - const newTimelineRes = await timelineLib.persistTimeline( - frameworkRequest, - timelineSavedObjectId ?? null, - timelineVersion ?? null, - timeline - ); - - return { - newTimelineSavedObjectId: newTimelineRes?.timeline?.savedObjectId ?? null, - newTimelineVersion: newTimelineRes?.timeline?.version ?? null, - }; -}; - -export const savePinnedEvents = ( - frameworkRequest: FrameworkRequest, - timelineSavedObjectId: string, - pinnedEventIds?: string[] | null -) => { - return ( - pinnedEventIds?.map(eventId => { - return pinnedEventLib.persistPinnedEventOnTimeline( - frameworkRequest, - null, // pinnedEventSavedObjectId - eventId, - timelineSavedObjectId - ); - }) ?? [] - ); -}; - -export const saveNotes = ( - frameworkRequest: FrameworkRequest, - timelineSavedObjectId: string, - timelineVersion?: string | null, - existingNoteIds?: string[], - newNotes?: NoteResult[] -) => { - return Promise.all( - newNotes?.map(note => { - const newNote: SavedNote = { - eventId: note.eventId, - note: note.note, - timelineId: timelineSavedObjectId, - }; - - return noteLib.persistNote( - frameworkRequest, - existingNoteIds?.find(nId => nId === note.noteId) ?? null, - timelineVersion ?? null, - newNote - ); - }) ?? [] - ); -}; - -export const createTimelines = async ( - frameworkRequest: FrameworkRequest, - timeline: SavedTimeline, - timelineSavedObjectId?: string | null, - timelineVersion?: string | null, - pinnedEventIds?: string[] | null, - notes?: NoteResult[], - existingNoteIds?: string[] -) => { - const { newTimelineSavedObjectId, newTimelineVersion } = await saveTimelines( - frameworkRequest, - timeline, - timelineSavedObjectId, - timelineVersion - ); - await Promise.all([ - savePinnedEvents( - frameworkRequest, - timelineSavedObjectId ?? newTimelineSavedObjectId, - pinnedEventIds - ), - saveNotes( - frameworkRequest, - timelineSavedObjectId ?? newTimelineSavedObjectId, - newTimelineVersion, - existingNoteIds, - notes - ), - ]); - - return newTimelineSavedObjectId; -}; - export const isImportRegular = ( importTimelineResponse: ImportTimelineResponse ): importTimelineResponse is ImportRegular => { @@ -189,3 +85,15 @@ export const isBulkError = ( ): importRuleResponse is BulkError => { return has('error', importRuleResponse); }; + +export const timelineSavedObjectOmittedFields = [ + 'globalNotes', + 'eventNotes', + 'pinnedEventIds', + 'version', + 'savedObjectId', + 'created', + 'createdBy', + 'updated', + 'updatedBy', +]; diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/utils/update_timelines.ts b/x-pack/plugins/siem/server/lib/timeline/routes/utils/update_timelines.ts new file mode 100644 index 00000000000000..6a25d8def91160 --- /dev/null +++ b/x-pack/plugins/siem/server/lib/timeline/routes/utils/update_timelines.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 { TimelineSavedObject } from '../../../../../common/types/timeline'; + +export const UPDATE_TIMELINE_ERROR_MESSAGE = + 'CREATE timeline with PATCH is not allowed, please use POST instead'; +export const UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE = + 'CREATE template timeline with PATCH is not allowed, please use POST instead'; +export const NO_MATCH_VERSION_ERROR_MESSAGE = + 'TimelineVersion conflict: The given version doesn not match with existing timeline'; +export const NO_MATCH_ID_ERROR_MESSAGE = + "Timeline id doesn't match with existing template timeline"; +export const OLDER_VERSION_ERROR_MESSAGE = + 'Template timelineVersion conflict: The given version is older then existing version'; + +export const checkIsFailureCases = ( + isHandlingTemplateTimeline: boolean, + version: string | null, + templateTimelineVersion: number | null, + existTimeline: TimelineSavedObject | null, + existTemplateTimeline: TimelineSavedObject | null +) => { + if (!isHandlingTemplateTimeline && existTimeline == null) { + return { + body: UPDATE_TIMELINE_ERROR_MESSAGE, + statusCode: 405, + }; + } else if (isHandlingTemplateTimeline && existTemplateTimeline == null) { + // Throw error to create template timeline in patch + return { + body: UPDATE_TEMPLATE_TIMELINE_ERROR_MESSAGE, + statusCode: 405, + }; + } else if ( + isHandlingTemplateTimeline && + existTimeline != null && + existTemplateTimeline != null && + existTimeline.savedObjectId !== existTemplateTimeline.savedObjectId + ) { + // Throw error you can not have a no matching between your timeline and your template timeline during an update + return { + body: NO_MATCH_ID_ERROR_MESSAGE, + statusCode: 409, + }; + } else if (!isHandlingTemplateTimeline && existTimeline?.version !== version) { + // throw error 409 conflict timeline + return { + body: NO_MATCH_VERSION_ERROR_MESSAGE, + statusCode: 409, + }; + } else if ( + isHandlingTemplateTimeline && + existTemplateTimeline != null && + existTemplateTimeline.templateTimelineVersion == null && + existTemplateTimeline.version !== version + ) { + // throw error 409 conflict timeline + return { + body: NO_MATCH_VERSION_ERROR_MESSAGE, + statusCode: 409, + }; + } else if ( + isHandlingTemplateTimeline && + templateTimelineVersion != null && + existTemplateTimeline != null && + existTemplateTimeline.templateTimelineVersion != null && + existTemplateTimeline.templateTimelineVersion >= templateTimelineVersion + ) { + // Throw error you can not update a template timeline version with an old version + return { + body: OLDER_VERSION_ERROR_MESSAGE, + statusCode: 409, + }; + } else { + return null; + } +}; diff --git a/x-pack/plugins/siem/server/lib/timeline/saved_object.ts b/x-pack/plugins/siem/server/lib/timeline/saved_object.ts index e8cd27947589fc..d2df7589f3c4a9 100644 --- a/x-pack/plugins/siem/server/lib/timeline/saved_object.ts +++ b/x-pack/plugins/siem/server/lib/timeline/saved_object.ts @@ -8,260 +8,308 @@ import { getOr } from 'lodash/fp'; import { SavedObjectsFindOptions } from '../../../../../../src/core/server'; import { UNAUTHENTICATED_USER } from '../../../common/constants'; +import { NoteSavedObject } from '../../../common/types/timeline/note'; +import { PinnedEventSavedObject } from '../../../common/types/timeline/pinned_event'; +import { SavedTimeline, TimelineSavedObject, TimelineType } from '../../../common/types/timeline'; import { ResponseTimeline, PageInfoTimeline, SortTimeline, ResponseFavoriteTimeline, TimelineResult, + Maybe, } from '../../graphql/types'; import { FrameworkRequest } from '../framework'; -import { Note } from '../note/saved_object'; -import { NoteSavedObject } from '../note/types'; -import { PinnedEventSavedObject } from '../pinned_event/types'; -import { PinnedEvent } from '../pinned_event/saved_object'; +import * as note from '../note/saved_object'; +import * as pinnedEvent from '../pinned_event/saved_object'; import { convertSavedObjectToSavedTimeline } from './convert_saved_object_to_savedtimeline'; import { pickSavedTimeline } from './pick_saved_timeline'; import { timelineSavedObjectType } from './saved_object_mappings'; -import { SavedTimeline, TimelineSavedObject } from './types'; interface ResponseTimelines { timeline: TimelineSavedObject[]; totalCount: number; } -export class Timeline { - private readonly note = new Note(); - private readonly pinnedEvent = new PinnedEvent(); +export interface ResponseTemplateTimeline { + code?: Maybe; - public async getTimeline( - request: FrameworkRequest, - timelineId: string - ): Promise { - return this.getSavedTimeline(request, timelineId); - } + message?: Maybe; + + templateTimeline: TimelineResult; +} - public async getAllTimeline( +export interface Timeline { + getTimeline: (request: FrameworkRequest, timelineId: string) => Promise; + + getAllTimeline: ( request: FrameworkRequest, onlyUserFavorite: boolean | null, pageInfo: PageInfoTimeline | null, search: string | null, sort: SortTimeline | null - ): Promise { - const options: SavedObjectsFindOptions = { - type: timelineSavedObjectType, - perPage: pageInfo != null ? pageInfo.pageSize : undefined, - page: pageInfo != null ? pageInfo.pageIndex : undefined, - search: search != null ? search : undefined, - searchFields: onlyUserFavorite - ? ['title', 'description', 'favorite.keySearch'] - : ['title', 'description'], - sortField: sort != null ? sort.sortField : undefined, - sortOrder: sort != null ? sort.sortOrder : undefined, - }; - - return this.getAllSavedTimeline(request, options); - } + ) => Promise; - public async persistFavorite( + persistFavorite: ( request: FrameworkRequest, timelineId: string | null - ): Promise { - const userName = request.user?.username ?? UNAUTHENTICATED_USER; - const fullName = request.user?.full_name ?? ''; - try { - let timeline: SavedTimeline = {}; - if (timelineId != null) { - const { - eventIdToNoteIds, - notes, - noteIds, - pinnedEventIds, - pinnedEventsSaveObject, - savedObjectId, - version, - ...savedTimeline - } = await this.getBasicSavedTimeline(request, timelineId); - timelineId = savedObjectId; // eslint-disable-line no-param-reassign - timeline = savedTimeline; - } + ) => Promise; - const userFavoriteTimeline = { - keySearch: userName != null ? convertStringToBase64(userName) : null, - favoriteDate: new Date().valueOf(), - fullName, - userName, - }; - if (timeline.favorite != null) { - const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( - user => user.userName === userName - ); - - timeline.favorite = - alreadyExistsTimelineFavoriteByUser > -1 - ? [ - ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), - ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), - ] - : [...timeline.favorite, userFavoriteTimeline]; - } else if (timeline.favorite == null) { - timeline.favorite = [userFavoriteTimeline]; - } + persistTimeline: ( + request: FrameworkRequest, + timelineId: string | null, + version: string | null, + timeline: SavedTimeline, + timelineType?: TimelineType | null + ) => Promise; - const persistResponse = await this.persistTimeline(request, timelineId, null, timeline); + deleteTimeline: (request: FrameworkRequest, timelineIds: string[]) => Promise; + convertStringToBase64: (text: string) => string; + timelineWithReduxProperties: ( + notes: NoteSavedObject[], + pinnedEvents: PinnedEventSavedObject[], + timeline: TimelineSavedObject, + userName: string + ) => TimelineSavedObject; +} + +export const getTimeline = async ( + request: FrameworkRequest, + timelineId: string +): Promise => { + return getSavedTimeline(request, timelineId); +}; + +export const getTimelineByTemplateTimelineId = async ( + request: FrameworkRequest, + templateTimelineId: string +): Promise<{ + totalCount: number; + timeline: TimelineSavedObject[]; +}> => { + const options: SavedObjectsFindOptions = { + type: timelineSavedObjectType, + filter: `siem-ui-timeline.attributes.templateTimelineId: ${templateTimelineId}`, + }; + return getAllSavedTimeline(request, options); +}; + +export const getAllTimeline = async ( + request: FrameworkRequest, + onlyUserFavorite: boolean | null, + pageInfo: PageInfoTimeline | null, + search: string | null, + sort: SortTimeline | null +): Promise => { + const options: SavedObjectsFindOptions = { + type: timelineSavedObjectType, + perPage: pageInfo != null ? pageInfo.pageSize : undefined, + page: pageInfo != null ? pageInfo.pageIndex : undefined, + search: search != null ? search : undefined, + searchFields: onlyUserFavorite + ? ['title', 'description', 'favorite.keySearch'] + : ['title', 'description'], + sortField: sort != null ? sort.sortField : undefined, + sortOrder: sort != null ? sort.sortOrder : undefined, + }; + return getAllSavedTimeline(request, options); +}; + +export const persistFavorite = async ( + request: FrameworkRequest, + timelineId: string | null +): Promise => { + const userName = request.user?.username ?? UNAUTHENTICATED_USER; + const fullName = request.user?.full_name ?? ''; + try { + let timeline: SavedTimeline = {}; + if (timelineId != null) { + const { + eventIdToNoteIds, + notes, + noteIds, + pinnedEventIds, + pinnedEventsSaveObject, + savedObjectId, + version, + ...savedTimeline + } = await getBasicSavedTimeline(request, timelineId); + timelineId = savedObjectId; // eslint-disable-line no-param-reassign + timeline = savedTimeline; + } + + const userFavoriteTimeline = { + keySearch: userName != null ? convertStringToBase64(userName) : null, + favoriteDate: new Date().valueOf(), + fullName, + userName, + }; + if (timeline.favorite != null) { + const alreadyExistsTimelineFavoriteByUser = timeline.favorite.findIndex( + user => user.userName === userName + ); + + timeline.favorite = + alreadyExistsTimelineFavoriteByUser > -1 + ? [ + ...timeline.favorite.slice(0, alreadyExistsTimelineFavoriteByUser), + ...timeline.favorite.slice(alreadyExistsTimelineFavoriteByUser + 1), + ] + : [...timeline.favorite, userFavoriteTimeline]; + } else if (timeline.favorite == null) { + timeline.favorite = [userFavoriteTimeline]; + } + + const persistResponse = await persistTimeline(request, timelineId, null, timeline); + return { + savedObjectId: persistResponse.timeline.savedObjectId, + version: persistResponse.timeline.version, + favorite: + persistResponse.timeline.favorite != null + ? persistResponse.timeline.favorite.filter(fav => fav.userName === userName) + : [], + }; + } catch (err) { + if (getOr(null, 'output.statusCode', err) === 403) { return { - savedObjectId: persistResponse.timeline.savedObjectId, - version: persistResponse.timeline.version, - favorite: - persistResponse.timeline.favorite != null - ? persistResponse.timeline.favorite.filter(fav => fav.userName === userName) - : [], + savedObjectId: '', + version: '', + favorite: [], + code: 403, + message: err.message, }; - } catch (err) { - if (getOr(null, 'output.statusCode', err) === 403) { - return { - savedObjectId: '', - version: '', - favorite: [], - code: 403, - message: err.message, - }; - } - throw err; } + throw err; } +}; - public async persistTimeline( - request: FrameworkRequest, - timelineId: string | null, - version: string | null, - timeline: SavedTimeline - ): Promise { - const savedObjectsClient = request.context.core.savedObjects.client; - try { - if (timelineId == null) { - // Create new timeline - const newTimeline = convertSavedObjectToSavedTimeline( - await savedObjectsClient.create( - timelineSavedObjectType, - pickSavedTimeline(timelineId, timeline, request.user) - ) - ); - return { - code: 200, - message: 'success', - timeline: newTimeline, - }; - } - // Update Timeline - await savedObjectsClient.update( - timelineSavedObjectType, - timelineId, - pickSavedTimeline(timelineId, timeline, request.user), - { - version: version || undefined, - } +export const persistTimeline = async ( + request: FrameworkRequest, + timelineId: string | null, + version: string | null, + timeline: SavedTimeline +): Promise => { + const savedObjectsClient = request.context.core.savedObjects.client; + try { + if (timelineId == null) { + // Create new timeline + const newTimeline = convertSavedObjectToSavedTimeline( + await savedObjectsClient.create( + timelineSavedObjectType, + pickSavedTimeline(timelineId, timeline, request.user) + ) ); - return { code: 200, message: 'success', - timeline: await this.getSavedTimeline(request, timelineId), + timeline: newTimeline, }; - } catch (err) { - if (timelineId != null && savedObjectsClient.errors.isConflictError(err)) { - return { - code: 409, - message: err.message, - timeline: await this.getSavedTimeline(request, timelineId), - }; - } else if (getOr(null, 'output.statusCode', err) === 403) { - const timelineToReturn: TimelineResult = { - ...timeline, - savedObjectId: '', - version: '', - }; - return { - code: 403, - message: err.message, - timeline: timelineToReturn, - }; + } + // Update Timeline + await savedObjectsClient.update( + timelineSavedObjectType, + timelineId, + pickSavedTimeline(timelineId, timeline, request.user), + { + version: version || undefined, } - throw err; + ); + + return { + code: 200, + message: 'success', + timeline: await getSavedTimeline(request, timelineId), + }; + } catch (err) { + if (timelineId != null && savedObjectsClient.errors.isConflictError(err)) { + return { + code: 409, + message: err.message, + timeline: await getSavedTimeline(request, timelineId), + }; + } else if (getOr(null, 'output.statusCode', err) === 403) { + const timelineToReturn: TimelineResult = { + ...timeline, + savedObjectId: '', + version: '', + }; + return { + code: 403, + message: err.message, + timeline: timelineToReturn, + }; } + throw err; } +}; - public async deleteTimeline(request: FrameworkRequest, timelineIds: string[]) { - const savedObjectsClient = request.context.core.savedObjects.client; - - await Promise.all( - timelineIds.map(timelineId => - Promise.all([ - savedObjectsClient.delete(timelineSavedObjectType, timelineId), - this.note.deleteNoteByTimelineId(request, timelineId), - this.pinnedEvent.deleteAllPinnedEventsOnTimeline(request, timelineId), - ]) - ) - ); - } +export const deleteTimeline = async (request: FrameworkRequest, timelineIds: string[]) => { + const savedObjectsClient = request.context.core.savedObjects.client; - private async getBasicSavedTimeline(request: FrameworkRequest, timelineId: string) { - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObject = await savedObjectsClient.get(timelineSavedObjectType, timelineId); + await Promise.all( + timelineIds.map(timelineId => + Promise.all([ + savedObjectsClient.delete(timelineSavedObjectType, timelineId), + note.deleteNoteByTimelineId(request, timelineId), + pinnedEvent.deleteAllPinnedEventsOnTimeline(request, timelineId), + ]) + ) + ); +}; - return convertSavedObjectToSavedTimeline(savedObject); - } +const getBasicSavedTimeline = async (request: FrameworkRequest, timelineId: string) => { + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObject = await savedObjectsClient.get(timelineSavedObjectType, timelineId); + + return convertSavedObjectToSavedTimeline(savedObject); +}; - private async getSavedTimeline(request: FrameworkRequest, timelineId: string) { - const userName = request.user?.username ?? UNAUTHENTICATED_USER; +const getSavedTimeline = async (request: FrameworkRequest, timelineId: string) => { + const userName = request.user?.username ?? UNAUTHENTICATED_USER; - const savedObjectsClient = request.context.core.savedObjects.client; - const savedObject = await savedObjectsClient.get(timelineSavedObjectType, timelineId); - const timelineSaveObject = convertSavedObjectToSavedTimeline(savedObject); - const timelineWithNotesAndPinnedEvents = await Promise.all([ - this.note.getNotesByTimelineId(request, timelineSaveObject.savedObjectId), - this.pinnedEvent.getAllPinnedEventsByTimelineId(request, timelineSaveObject.savedObjectId), - Promise.resolve(timelineSaveObject), - ]); + const savedObjectsClient = request.context.core.savedObjects.client; + const savedObject = await savedObjectsClient.get(timelineSavedObjectType, timelineId); + const timelineSaveObject = convertSavedObjectToSavedTimeline(savedObject); + const timelineWithNotesAndPinnedEvents = await Promise.all([ + note.getNotesByTimelineId(request, timelineSaveObject.savedObjectId), + pinnedEvent.getAllPinnedEventsByTimelineId(request, timelineSaveObject.savedObjectId), + Promise.resolve(timelineSaveObject), + ]); - const [notes, pinnedEvents, timeline] = timelineWithNotesAndPinnedEvents; + const [notes, pinnedEvents, timeline] = timelineWithNotesAndPinnedEvents; - return timelineWithReduxProperties(notes, pinnedEvents, timeline, userName); + return timelineWithReduxProperties(notes, pinnedEvents, timeline, userName); +}; + +const getAllSavedTimeline = async (request: FrameworkRequest, options: SavedObjectsFindOptions) => { + const userName = request.user?.username ?? UNAUTHENTICATED_USER; + const savedObjectsClient = request.context.core.savedObjects.client; + if (options.searchFields != null && options.searchFields.includes('favorite.keySearch')) { + options.search = `${options.search != null ? options.search : ''} ${ + userName != null ? convertStringToBase64(userName) : null + }`; } - private async getAllSavedTimeline(request: FrameworkRequest, options: SavedObjectsFindOptions) { - const userName = request.user?.username ?? UNAUTHENTICATED_USER; - const savedObjectsClient = request.context.core.savedObjects.client; - if (options.searchFields != null && options.searchFields.includes('favorite.keySearch')) { - options.search = `${options.search != null ? options.search : ''} ${ - userName != null ? convertStringToBase64(userName) : null - }`; - } + const savedObjects = await savedObjectsClient.find(options); - const savedObjects = await savedObjectsClient.find(options); - - const timelinesWithNotesAndPinnedEvents = await Promise.all( - savedObjects.saved_objects.map(async savedObject => { - const timelineSaveObject = convertSavedObjectToSavedTimeline(savedObject); - return Promise.all([ - this.note.getNotesByTimelineId(request, timelineSaveObject.savedObjectId), - this.pinnedEvent.getAllPinnedEventsByTimelineId( - request, - timelineSaveObject.savedObjectId - ), - Promise.resolve(timelineSaveObject), - ]); - }) - ); + const timelinesWithNotesAndPinnedEvents = await Promise.all( + savedObjects.saved_objects.map(async savedObject => { + const timelineSaveObject = convertSavedObjectToSavedTimeline(savedObject); + return Promise.all([ + note.getNotesByTimelineId(request, timelineSaveObject.savedObjectId), + pinnedEvent.getAllPinnedEventsByTimelineId(request, timelineSaveObject.savedObjectId), + Promise.resolve(timelineSaveObject), + ]); + }) + ); - return { - totalCount: savedObjects.total, - timeline: timelinesWithNotesAndPinnedEvents.map(([notes, pinnedEvents, timeline]) => - timelineWithReduxProperties(notes, pinnedEvents, timeline, userName) - ), - }; - } -} + return { + totalCount: savedObjects.total, + timeline: timelinesWithNotesAndPinnedEvents.map(([notes, pinnedEvents, timeline]) => + timelineWithReduxProperties(notes, pinnedEvents, timeline, userName) + ), + }; +}; export const convertStringToBase64 = (text: string): string => Buffer.from(text).toString('base64'); @@ -283,11 +331,9 @@ export const timelineWithReduxProperties = ( timeline.favorite != null && userName != null ? timeline.favorite.filter(fav => fav.userName === userName) : [], - eventIdToNoteIds: notes.filter(note => note.eventId != null), - noteIds: notes - .filter(note => note.eventId == null && note.noteId != null) - .map(note => note.noteId), + eventIdToNoteIds: notes.filter(n => n.eventId != null), + noteIds: notes.filter(n => n.eventId == null && n.noteId != null).map(n => n.noteId), notes, - pinnedEventIds: pinnedEvents.map(pinnedEvent => pinnedEvent.eventId), + pinnedEventIds: pinnedEvents.map(e => e.eventId), pinnedEventsSaveObject: pinnedEvents, }); diff --git a/x-pack/plugins/siem/server/lib/timeline/saved_object_mappings.ts b/x-pack/plugins/siem/server/lib/timeline/saved_object_mappings.ts index b956e0f98fcb62..1cab24d0879ff6 100644 --- a/x-pack/plugins/siem/server/lib/timeline/saved_object_mappings.ts +++ b/x-pack/plugins/siem/server/lib/timeline/saved_object_mappings.ts @@ -231,6 +231,15 @@ export const timelineSavedObjectMappings = { title: { type: 'text', }, + templateTimelineId: { + type: 'text', + }, + templateTimelineVersion: { + type: 'integer', + }, + timelineType: { + type: 'keyword', + }, dateRange: { properties: { start: { diff --git a/x-pack/plugins/siem/server/routes/index.ts b/x-pack/plugins/siem/server/routes/index.ts index 1c03823e85fd7a..ffad86a09cee72 100644 --- a/x-pack/plugins/siem/server/routes/index.ts +++ b/x-pack/plugins/siem/server/routes/index.ts @@ -30,6 +30,8 @@ import { findRulesStatusesRoute } from '../lib/detection_engine/routes/rules/fin import { getPrepackagedRulesStatusRoute } from '../lib/detection_engine/routes/rules/get_prepackaged_rules_status_route'; import { importTimelinesRoute } from '../lib/timeline/routes/import_timelines_route'; import { exportTimelinesRoute } from '../lib/timeline/routes/export_timelines_route'; +import { createTimelinesRoute } from '../lib/timeline/routes/create_timelines_route'; +import { updateTimelinesRoute } from '../lib/timeline/routes/update_timelines_route'; import { SetupPlugins } from '../plugin'; import { ConfigType } from '../config'; @@ -55,6 +57,8 @@ export const initRoutes = ( patchRulesBulkRoute(router); deleteRulesBulkRoute(router); + createTimelinesRoute(router, config, security); + updateTimelinesRoute(router, config, security); importRulesRoute(router, config); exportRulesRoute(router, config); diff --git a/x-pack/plugins/siem/server/utils/typed_resolvers.ts b/x-pack/plugins/siem/server/utils/typed_resolvers.ts index da38e8a1e1bf28..4f19bd54b01f0c 100644 --- a/x-pack/plugins/siem/server/utils/typed_resolvers.ts +++ b/x-pack/plugins/siem/server/utils/typed_resolvers.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as runtimeTypes from 'io-ts'; import { GraphQLResolveInfo } from 'graphql'; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -106,6 +105,3 @@ export type ChildResolverOf = ResolverWithParent< Resolver_, ResultOf >; - -export const unionWithNullType = (type: T) => - runtimeTypes.union([type, runtimeTypes.null]); diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts index b063404f68e4fb..65a810ff94a1f5 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts @@ -6,7 +6,7 @@ import { SavedObjectMigrationFn } from 'src/core/server'; -export const migrateToKibana660: SavedObjectMigrationFn = doc => { +export const migrateToKibana660: SavedObjectMigrationFn = doc => { if (!doc.attributes.hasOwnProperty('disabledFeatures')) { doc.attributes.disabledFeatures = []; } diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index a70fbdb18c30b6..0f6e3fc31d96d6 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -12,11 +12,10 @@ import { TaskManager } from './task_manager'; import { createTaskManager } from './create_task_manager'; import { TaskManagerConfig } from './config'; import { Middleware } from './lib/middleware'; +import { setupSavedObjects } from './saved_objects'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginLegacyDependencies {} export type TaskManagerSetupContract = { - registerLegacyAPI: (legacyDependencies: PluginLegacyDependencies) => Promise; + registerLegacyAPI: () => Promise; } & Pick; export type TaskManagerStartContract = Pick< @@ -35,12 +34,18 @@ export class TaskManagerPlugin this.currentConfig = {} as TaskManagerConfig; } - public setup(core: CoreSetup, plugins: unknown): TaskManagerSetupContract { + public async setup(core: CoreSetup, plugins: unknown): Promise { const logger = this.initContext.logger.get('taskManager'); - const config$ = this.initContext.config.create(); + const config = await this.initContext.config + .create() + .pipe(first()) + .toPromise(); + + setupSavedObjects(core.savedObjects, config); + return { - registerLegacyAPI: once((__LEGACY: PluginLegacyDependencies) => { - config$.subscribe(async config => { + registerLegacyAPI: once(() => { + (async () => { const [{ savedObjects, elasticsearch }] = await core.getStartServices(); const savedObjectsRepository = savedObjects.createInternalRepository(['task']); this.legacyTaskManager$.next( @@ -53,7 +58,7 @@ export class TaskManagerPlugin }) ); this.legacyTaskManager$.complete(); - }); + })(); return this.taskManager; }), addMiddleware: (middleware: Middleware) => { diff --git a/x-pack/plugins/task_manager/server/saved_objects/index.ts b/x-pack/plugins/task_manager/server/saved_objects/index.ts new file mode 100644 index 00000000000000..0ad9021cd7f39c --- /dev/null +++ b/x-pack/plugins/task_manager/server/saved_objects/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsServiceSetup } from 'kibana/server'; +import mappings from './mappings.json'; +import { TaskManagerConfig } from '../config.js'; + +export function setupSavedObjects( + savedObjects: SavedObjectsServiceSetup, + config: TaskManagerConfig +) { + savedObjects.registerType({ + name: 'task', + namespaceType: 'agnostic', + hidden: true, + convertToAliasScript: `ctx._id = ctx._source.type + ':' + ctx._id`, + mappings: mappings.task, + indexPattern: config.index, + }); +} diff --git a/x-pack/legacy/plugins/task_manager/server/mappings.json b/x-pack/plugins/task_manager/server/saved_objects/mappings.json similarity index 100% rename from x-pack/legacy/plugins/task_manager/server/mappings.json rename to x-pack/plugins/task_manager/server/saved_objects/mappings.json diff --git a/x-pack/legacy/plugins/task_manager/server/migrations.ts b/x-pack/plugins/task_manager/server/saved_objects/migrations.ts similarity index 100% rename from x-pack/legacy/plugins/task_manager/server/migrations.ts rename to x-pack/plugins/task_manager/server/saved_objects/migrations.ts diff --git a/x-pack/plugins/task_manager/server/task_manager.ts b/x-pack/plugins/task_manager/server/task_manager.ts index 24ceea0fe71ef2..2a45a599120ddc 100644 --- a/x-pack/plugins/task_manager/server/task_manager.ts +++ b/x-pack/plugins/task_manager/server/task_manager.ts @@ -240,7 +240,7 @@ export class TaskManager { * @param taskDefinitions - The Kibana task definitions dictionary */ public registerTaskDefinitions(taskDefinitions: TaskDictionary) { - this.assertUninitialized('register task definitions'); + this.assertUninitialized('register task definitions', Object.keys(taskDefinitions).join(', ')); const duplicate = Object.keys(taskDefinitions).find(k => !!this.definitions[k]); if (duplicate) { throw new Error(`Task ${duplicate} is already defined!`); @@ -360,9 +360,11 @@ export class TaskManager { * @param {string} message shown if task manager is already initialized * @returns void */ - private assertUninitialized(message: string) { + private assertUninitialized(message: string, context?: string) { if (this.isStarted) { - throw new Error(`Cannot ${message} after the task manager is initialized!`); + throw new Error( + `${context ? `[${context}] ` : ''}Cannot ${message} after the task manager is initialized` + ); } } } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4f690ee398af99..17d5c8b38f871a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2579,7 +2579,6 @@ "telemetry.welcomeBanner.enableButtonLabel": "有効にする", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "遠隔測定に関するプライバシーステートメント", "telemetry.welcomeBanner.title": "Elastic Stack の改善にご協力ください", - "tileMap.baseMapsVisualization.childShouldImplementMethodErrorMessage": "子は data-update に対応できるようこのメソドを導入する必要があります", "tileMap.function.help": "タイルマップのビジュアライゼーションです", "tileMap.geohashLayer.mapTitle": "{mapType} マップタイプが認識されません", "tileMap.tooltipFormatter.latitudeLabel": "緯度", @@ -2601,25 +2600,6 @@ "tileMap.visParams.desaturateTilesLabel": "タイルを不飽和化", "tileMap.visParams.mapTypeLabel": "マップタイプ", "tileMap.visParams.reduceVibrancyOfTileColorsTip": "色の鮮明度を下げます。この機能は Internet Explorer ではバージョンにかかわらず利用できません。", - "tileMap.wmsOptions.attributionStringTip": "右下角の属性文字列", - "tileMap.wmsOptions.baseLayerSettingsTitle": "ベースレイヤー設定", - "tileMap.wmsOptions.imageFormatToUseTip": "通常画像/png または画像/jpeg です。サーバーが透明レイヤーを返す場合は png を使用します。", - "tileMap.wmsOptions.layersLabel": "レイヤー", - "tileMap.wmsOptions.listOfLayersToUseTip": "使用するレイヤーのコンマ区切りのリストです。", - "tileMap.wmsOptions.mapLoadFailDescription": "このパラメーターが正しくないと、マップが正常に読み込まれません。", - "tileMap.wmsOptions.urlOfWMSWebServiceTip": "WMS web サービスの URL です。", - "tileMap.wmsOptions.useWMSCompliantMapTileServerTip": "WMS 対応のマップタイルサーバーを使用します。上級者向けです。", - "tileMap.wmsOptions.versionOfWMSserverSupportsTip": "サーバーがサポートしている WMS のバージョンです。", - "tileMap.wmsOptions.wmsAttributionLabel": "WMS 属性", - "tileMap.wmsOptions.wmsDescription": "WMS は、マップイメージサービスの {wmsLink} です。", - "tileMap.wmsOptions.wmsFormatLabel": "WMS フォーマット", - "tileMap.wmsOptions.wmsLayersLabel": "WMS レイヤー", - "tileMap.wmsOptions.wmsLinkText": "OGC スタンダード", - "tileMap.wmsOptions.wmsMapServerLabel": "WMS マップサーバー", - "tileMap.wmsOptions.wmsServerSupportedStylesListTip": "WMS サーバーがサポートしている使用スタイルのコンマ区切りのリストです。大抵は空白のままです。", - "tileMap.wmsOptions.wmsStylesLabel": "WMS スタイル", - "tileMap.wmsOptions.wmsUrlLabel": "WMS URL", - "tileMap.wmsOptions.wmsVersionLabel": "WMS バージョン", "timelion.badge.readOnly.text": "読み込み専用", "timelion.badge.readOnly.tooltip": "Timelion シートを保存できません", "timelion.breadcrumbs.create": "作成", @@ -8241,11 +8221,8 @@ "xpack.ingestManager.agentConfigList.addButton": "エージェント構成を作成", "xpack.ingestManager.agentConfigList.agentsColumnTitle": "エージェント", "xpack.ingestManager.agentConfigList.clearFiltersLinkText": "フィルターを消去", - "xpack.ingestManager.agentConfigList.copyConfigActionText": "構成をコピー", "xpack.ingestManager.agentConfigList.createDatasourceActionText": "データソースを作成", "xpack.ingestManager.agentConfigList.datasourcesCountColumnTitle": "データソース", - "xpack.ingestManager.agentConfigList.deleteButton": "{count, plural, one {# エージェント設定} other {# エージェント設定}}を削除", - "xpack.ingestManager.agentConfigList.deleteConfigActionText": "構成の削除", "xpack.ingestManager.agentConfigList.descriptionColumnTitle": "説明", "xpack.ingestManager.agentConfigList.loadingAgentConfigsMessage": "エージェント構成を読み込み中...", "xpack.ingestManager.agentConfigList.nameColumnTitle": "名前", @@ -8321,13 +8298,6 @@ "xpack.ingestManager.agentListStatus.offlineLabel": "オフライン", "xpack.ingestManager.agentListStatus.onlineLabel": "オンライン", "xpack.ingestManager.agentListStatus.totalLabel": "エージェント", - "xpack.ingestManager.apiKeysForm.configLabel": "構成", - "xpack.ingestManager.apiKeysForm.nameLabel": "キー名", - "xpack.ingestManager.apiKeysForm.saveButton": "保存", - "xpack.ingestManager.apiKeysList.apiKeyColumnTitle": "API キー", - "xpack.ingestManager.apiKeysList.configColumnTitle": "構成", - "xpack.ingestManager.apiKeysList.emptyEnrollmentKeysMessage": "API キーがありません", - "xpack.ingestManager.apiKeysList.nameColumnTitle": "名前", "xpack.ingestManager.appNavigation.configurationsLinkText": "構成", "xpack.ingestManager.appNavigation.fleetLinkText": "フリート", "xpack.ingestManager.appNavigation.overviewLinkText": "概要", @@ -8336,7 +8306,6 @@ "xpack.ingestManager.configDetails.configDetailsTitle": "構成「{id}」", "xpack.ingestManager.configDetails.configNotFoundErrorTitle": "構成「{id}」が見つかりません", "xpack.ingestManager.configDetails.datasourcesTable.actionsColumnTitle": "アクション", - "xpack.ingestManager.configDetails.datasourcesTable.copyActionTitle": "データソースをコピー", "xpack.ingestManager.configDetails.datasourcesTable.deleteActionTitle": "データソースを削除", "xpack.ingestManager.configDetails.datasourcesTable.descriptionColumnTitle": "説明", "xpack.ingestManager.configDetails.datasourcesTable.editActionTitle": "データソースを編集", @@ -8344,7 +8313,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "名前空間", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "パッケージ", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "ストリーム", - "xpack.ingestManager.configDetails.datasourcesTable.viewActionTitle": "データソースを表示", "xpack.ingestManager.configDetails.subTabs.datasouces": "データソース", "xpack.ingestManager.configDetails.subTabs.settings": "設定", "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML ファイル", @@ -8406,8 +8374,6 @@ "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "{count} 件のエージェント構成を削除しました", "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "エージェント構成「{id}」を削除しました", "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "キャンセル", - "xpack.ingestManager.deleteApiKeys.confirmModal.confirmButtonLabel": "削除", - "xpack.ingestManager.deleteApiKeys.confirmModal.title": "API キーを削除: {apiKeyId}", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "{agentConfigName} が一部のエージェントで既に使用されていることをフリートが検出しました。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "このアクションは {agentsCount} {agentsCount, plural, one {# エージェント} other {# エージェント}}に影響します", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "キャンセル", @@ -8430,9 +8396,7 @@ "xpack.ingestManager.editConfig.successNotificationTitle": "エージェント構成「{name}」を更新しました", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "名前を選択", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "新規キーを作成", - "xpack.ingestManager.enrollmentApiKeyList.hideTableButton": "を非表示", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "既存のキーを使用", - "xpack.ingestManager.enrollmentApiKeyList.viewTableButton": "表示", "xpack.ingestManager.epm.addDatasourceButtonText": "データソースを作成", "xpack.ingestManager.epm.pageSubtitle": "人気のアプリやサービスのパッケージを参照する", "xpack.ingestManager.epm.pageTitle": "Elastic Package Manager", @@ -10532,10 +10496,9 @@ "xpack.ml.overview.feedbackSectionTitle": "フィードバック", "xpack.ml.overview.gettingStartedSectionCreateJob": "新規ジョブを作成中", "xpack.ml.overview.gettingStartedSectionDocs": "ドキュメンテーション", - "xpack.ml.overview.gettingStartedSectionText": "機械学習へようこそ。はじめに{docs}や{createJob}をご参照ください。Elastic Stackの機械学習の詳細については、{whatIsMachineLearning}をご覧ください。{transforms}を使用して、分析ジョブの機能インデックスを作成することをお勧めします。", + "xpack.ml.overview.gettingStartedSectionText": "機械学習へようこそ。はじめに{docs}や{createJob}をご参照ください。{transforms}を使用して、分析ジョブの機能インデックスを作成することをお勧めします。", "xpack.ml.overview.gettingStartedSectionTitle": "はじめて使う", "xpack.ml.overview.gettingStartedSectionTransforms": "Elasticsearchの変換", - "xpack.ml.overview.gettingStartedSectionWhatIsMachineLearning": "こちら", "xpack.ml.overview.overviewLabel": "概要", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失敗", "xpack.ml.overview.statsBar.runningAnalyticsLabel": "実行中", @@ -12647,7 +12610,6 @@ "xpack.security.loginPage.esUnavailableTitle": "Elasticsearch クラスターに接続できません", "xpack.security.loginPage.loginProviderDescription": "{providerType}/{providerName} でログイン", "xpack.security.loginPage.loginSelectorErrorMessage": "ログインを実行できませんでした。", - "xpack.security.loginPage.loginSelectorOR": "OR", "xpack.security.loginPage.noLoginMethodsAvailableMessage": "システム管理者にお問い合わせください。", "xpack.security.loginPage.noLoginMethodsAvailableTitle": "ログインが無効です。", "xpack.security.loginPage.requiresSecureConnectionMessage": "システム管理者にお問い合わせください。", @@ -16140,7 +16102,6 @@ "xpack.uptime.emptyStateError.notAuthorized": "アップタイムデータの表示が承認されていません。システム管理者にお問い合わせください。", "xpack.uptime.emptyStateError.notFoundPage": "ページが見つかりません", "xpack.uptime.emptyStateError.title": "エラー", - "xpack.uptime.featureCatalogueDescription": "エンドポイントヘルスチェックとアップタイム監視を行います。", "xpack.uptime.featureRegistry.uptimeFeatureName": "アップタイム", "xpack.uptime.filterBar.ariaLabel": "概要ページのインプットフィルター基準", "xpack.uptime.filterBar.filterDownLabel": "ダウン", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 98292d5bc9160b..ece2de973afe48 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2580,7 +2580,6 @@ "telemetry.welcomeBanner.enableButtonLabel": "启用", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "遥测隐私声明", "telemetry.welcomeBanner.title": "帮助我们改进 Elastic Stack", - "tileMap.baseMapsVisualization.childShouldImplementMethodErrorMessage": "子函数应实现此方法以响应数据更新", "tileMap.function.help": "磁贴地图可视化", "tileMap.geohashLayer.mapTitle": "{mapType} 地图类型无法识别", "tileMap.tooltipFormatter.latitudeLabel": "纬度", @@ -2602,25 +2601,6 @@ "tileMap.visParams.desaturateTilesLabel": "降低平铺地图饱和度", "tileMap.visParams.mapTypeLabel": "地图类型", "tileMap.visParams.reduceVibrancyOfTileColorsTip": "降低平铺地图颜色的亮度。此设置在任何版本的 IE 浏览器中均不起作用。", - "tileMap.wmsOptions.attributionStringTip": "右下角的属性字符串。", - "tileMap.wmsOptions.baseLayerSettingsTitle": "基础图层设置", - "tileMap.wmsOptions.imageFormatToUseTip": "通常为 image/png 或 image/jpeg。如果服务器返回透明图层,则使用 png。", - "tileMap.wmsOptions.layersLabel": "图层", - "tileMap.wmsOptions.listOfLayersToUseTip": "要使用的图层逗号分隔列表。", - "tileMap.wmsOptions.mapLoadFailDescription": "如果此参数不正确,将无法加载地图。", - "tileMap.wmsOptions.urlOfWMSWebServiceTip": "WMS Web 服务的 URL。", - "tileMap.wmsOptions.useWMSCompliantMapTileServerTip": "使用符合 WMS 规范的平铺地图服务器。仅适用于高级用户。", - "tileMap.wmsOptions.versionOfWMSserverSupportsTip": "服务器支持的 WMS 版本。", - "tileMap.wmsOptions.wmsAttributionLabel": "WMS 属性", - "tileMap.wmsOptions.wmsDescription": "WMS 是用于地图图像服务的 {wmsLink}。", - "tileMap.wmsOptions.wmsFormatLabel": "WMS 格式", - "tileMap.wmsOptions.wmsLayersLabel": "WMS 图层", - "tileMap.wmsOptions.wmsLinkText": "OGC 标准", - "tileMap.wmsOptions.wmsMapServerLabel": "WMS 地图服务器", - "tileMap.wmsOptions.wmsServerSupportedStylesListTip": "要使用的以逗号分隔的 WMS 服务器支持的样式列表。在大部分情况下为空。", - "tileMap.wmsOptions.wmsStylesLabel": "WMS 样式", - "tileMap.wmsOptions.wmsUrlLabel": "WMS url", - "tileMap.wmsOptions.wmsVersionLabel": "WMS 版本", "timelion.badge.readOnly.text": "只读", "timelion.badge.readOnly.tooltip": "无法保存 Timelion 工作表", "timelion.breadcrumbs.create": "创建", @@ -8244,11 +8224,8 @@ "xpack.ingestManager.agentConfigList.addButton": "创建代理配置", "xpack.ingestManager.agentConfigList.agentsColumnTitle": "代理", "xpack.ingestManager.agentConfigList.clearFiltersLinkText": "清除筛选", - "xpack.ingestManager.agentConfigList.copyConfigActionText": "复制配置", "xpack.ingestManager.agentConfigList.createDatasourceActionText": "创建数据源", "xpack.ingestManager.agentConfigList.datasourcesCountColumnTitle": "数据源", - "xpack.ingestManager.agentConfigList.deleteButton": "删除 {count, plural, one {# 个代理配置} other {# 个代理配置}}", - "xpack.ingestManager.agentConfigList.deleteConfigActionText": "删除配置", "xpack.ingestManager.agentConfigList.descriptionColumnTitle": "描述", "xpack.ingestManager.agentConfigList.loadingAgentConfigsMessage": "正在加载代理配置……", "xpack.ingestManager.agentConfigList.nameColumnTitle": "名称", @@ -8324,13 +8301,6 @@ "xpack.ingestManager.agentListStatus.offlineLabel": "脱机", "xpack.ingestManager.agentListStatus.onlineLabel": "联机", "xpack.ingestManager.agentListStatus.totalLabel": "代理", - "xpack.ingestManager.apiKeysForm.configLabel": "配置", - "xpack.ingestManager.apiKeysForm.nameLabel": "密钥名称", - "xpack.ingestManager.apiKeysForm.saveButton": "保存", - "xpack.ingestManager.apiKeysList.apiKeyColumnTitle": "API 密钥", - "xpack.ingestManager.apiKeysList.configColumnTitle": "配置", - "xpack.ingestManager.apiKeysList.emptyEnrollmentKeysMessage": "无 API 密钥", - "xpack.ingestManager.apiKeysList.nameColumnTitle": "名称", "xpack.ingestManager.appNavigation.configurationsLinkText": "配置", "xpack.ingestManager.appNavigation.fleetLinkText": "Fleet", "xpack.ingestManager.appNavigation.overviewLinkText": "概览", @@ -8339,7 +8309,6 @@ "xpack.ingestManager.configDetails.configDetailsTitle": "配置“{id}”", "xpack.ingestManager.configDetails.configNotFoundErrorTitle": "未找到配置“{id}”", "xpack.ingestManager.configDetails.datasourcesTable.actionsColumnTitle": "操作", - "xpack.ingestManager.configDetails.datasourcesTable.copyActionTitle": "复制数据源", "xpack.ingestManager.configDetails.datasourcesTable.deleteActionTitle": "删除数据源", "xpack.ingestManager.configDetails.datasourcesTable.descriptionColumnTitle": "描述", "xpack.ingestManager.configDetails.datasourcesTable.editActionTitle": "编辑数据源", @@ -8347,7 +8316,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "命名空间", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "软件包", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "流计数", - "xpack.ingestManager.configDetails.datasourcesTable.viewActionTitle": "查看数据源", "xpack.ingestManager.configDetails.subTabs.datasouces": "数据源", "xpack.ingestManager.configDetails.subTabs.settings": "设置", "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML 文件", @@ -8409,8 +8377,6 @@ "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "已删除 {count} 个代理配置", "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "已删除代理配置“{id}”", "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "取消", - "xpack.ingestManager.deleteApiKeys.confirmModal.confirmButtonLabel": "删除", - "xpack.ingestManager.deleteApiKeys.confirmModal.title": "删除 api 密钥:{apiKeyId}", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "Fleet 已检测到 {agentConfigName} 已由您的部分代理使用。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "此操作将影响 {agentsCount} 个 {agentsCount, plural, one {代理} other {代理}}。", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "取消", @@ -8433,9 +8399,7 @@ "xpack.ingestManager.editConfig.successNotificationTitle": "代理配置“{name}”已更新", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "选择名称", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "创建新密钥", - "xpack.ingestManager.enrollmentApiKeyList.hideTableButton": "隐藏", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "使用现有密钥", - "xpack.ingestManager.enrollmentApiKeyList.viewTableButton": "查看", "xpack.ingestManager.epm.addDatasourceButtonText": "创建数据源", "xpack.ingestManager.epm.pageSubtitle": "浏览热门应用和服务的软件。", "xpack.ingestManager.epm.pageTitle": "Elastic Package Manager", @@ -10535,10 +10499,9 @@ "xpack.ml.overview.feedbackSectionTitle": "反馈", "xpack.ml.overview.gettingStartedSectionCreateJob": "创建新作业", "xpack.ml.overview.gettingStartedSectionDocs": "文档", - "xpack.ml.overview.gettingStartedSectionText": "欢迎使用 Machine Learning。首先阅读我们的{docs}或{createJob}。有关 Elastic Stack 中的机器学习的详情,请参阅{whatIsMachineLearning}。建议使用 {transforms}为分析作业创建功能索引。", + "xpack.ml.overview.gettingStartedSectionText": "欢迎使用 Machine Learning。首先阅读我们的{docs}或{createJob}。建议使用 {transforms}为分析作业创建功能索引。", "xpack.ml.overview.gettingStartedSectionTitle": "入门", "xpack.ml.overview.gettingStartedSectionTransforms": "Elasticsearch 的转换", - "xpack.ml.overview.gettingStartedSectionWhatIsMachineLearning": "此处", "xpack.ml.overview.overviewLabel": "概览", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失败", "xpack.ml.overview.statsBar.runningAnalyticsLabel": "正在运行", @@ -12651,7 +12614,6 @@ "xpack.security.loginPage.esUnavailableTitle": "无法连接到 Elasticsearch 集群", "xpack.security.loginPage.loginProviderDescription": "使用 {providerType}/{providerName} 登录", "xpack.security.loginPage.loginSelectorErrorMessage": "无法执行登录。", - "xpack.security.loginPage.loginSelectorOR": "或", "xpack.security.loginPage.noLoginMethodsAvailableMessage": "请联系您的管理员。", "xpack.security.loginPage.noLoginMethodsAvailableTitle": "登录已禁用。", "xpack.security.loginPage.requiresSecureConnectionMessage": "请联系您的管理员。", @@ -16145,7 +16107,6 @@ "xpack.uptime.emptyStateError.notAuthorized": "您无权查看 Uptime 数据,请联系系统管理员。", "xpack.uptime.emptyStateError.notFoundPage": "未找到页面", "xpack.uptime.emptyStateError.title": "错误", - "xpack.uptime.featureCatalogueDescription": "执行终端节点运行状况检查和运行时间监测。", "xpack.uptime.featureRegistry.uptimeFeatureName": "运行时间", "xpack.uptime.filterBar.ariaLabel": "概览页面的输入筛选条件", "xpack.uptime.filterBar.filterDownLabel": "关闭", diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index bdca506cc73385..0cdf1ca05feac2 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -25,6 +25,8 @@ import { registerClusterCheckupRoutes } from './routes/cluster_checkup'; import { registerDeprecationLoggingRoutes } from './routes/deprecation_logging'; import { registerReindexIndicesRoutes, createReindexWorker } from './routes/reindex_indices'; import { registerTelemetryRoutes } from './routes/telemetry'; +import { telemetrySavedObjectType, reindexOperationSavedObjectType } from './saved_object_types'; + import { RouteDependencies } from './types'; interface PluginsSetup { @@ -57,11 +59,14 @@ export class UpgradeAssistantServerPlugin implements Plugin { } setup( - { http, getStartServices, capabilities }: CoreSetup, + { http, getStartServices, capabilities, savedObjects }: CoreSetup, { usageCollection, cloud, licensing }: PluginsSetup ) { this.licensing = licensing; + savedObjects.registerType(reindexOperationSavedObjectType); + savedObjects.registerType(telemetrySavedObjectType); + const router = http.createRouter(); const dependencies: RouteDependencies = { @@ -85,8 +90,12 @@ export class UpgradeAssistantServerPlugin implements Plugin { registerTelemetryRoutes(dependencies); if (usageCollection) { - getStartServices().then(([{ savedObjects, elasticsearch }]) => { - registerUpgradeAssistantUsageCollector({ elasticsearch, usageCollection, savedObjects }); + getStartServices().then(([{ savedObjects: savedObjectsService, elasticsearch }]) => { + registerUpgradeAssistantUsageCollector({ + elasticsearch, + usageCollection, + savedObjects: savedObjectsService, + }); }); } } diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/index.ts new file mode 100644 index 00000000000000..dee0a74d8994bb --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/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 { reindexOperationSavedObjectType } from './reindex_operation_saved_object_type'; +export { telemetrySavedObjectType } from './telemetry_saved_object_type'; diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/reindex_operation_saved_object_type.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/reindex_operation_saved_object_type.ts new file mode 100644 index 00000000000000..ba661fbeceb267 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/reindex_operation_saved_object_type.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectsType } from 'src/core/server'; + +import { REINDEX_OP_TYPE } from '../../common/types'; + +export const reindexOperationSavedObjectType: SavedObjectsType = { + name: REINDEX_OP_TYPE, + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + reindexTaskId: { + type: 'keyword', + }, + indexName: { + type: 'keyword', + }, + newIndexName: { + type: 'keyword', + }, + status: { + type: 'integer', + }, + locked: { + type: 'date', + }, + lastCompletedStep: { + type: 'integer', + }, + errorMessage: { + type: 'keyword', + }, + reindexTaskPercComplete: { + type: 'float', + }, + runningReindexCount: { + type: 'integer', + }, + reindexOptions: { + properties: { + openAndClose: { + type: 'boolean', + }, + queueSettings: { + properties: { + queuedAt: { + type: 'long', + }, + startedAt: { + type: 'long', + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.ts new file mode 100644 index 00000000000000..b1321e634c0f1c --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/saved_object_types/telemetry_saved_object_type.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 { SavedObjectsType } from 'src/core/server'; + +import { UPGRADE_ASSISTANT_TYPE } from '../../common/types'; + +export const telemetrySavedObjectType: SavedObjectsType = { + name: UPGRADE_ASSISTANT_TYPE, + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + ui_open: { + properties: { + overview: { + type: 'long', + null_value: 0, + }, + cluster: { + type: 'long', + null_value: 0, + }, + indices: { + type: 'long', + null_value: 0, + }, + }, + }, + ui_reindex: { + properties: { + close: { + type: 'long', + null_value: 0, + }, + open: { + type: 'long', + null_value: 0, + }, + start: { + type: 'long', + null_value: 0, + }, + stop: { + type: 'long', + null_value: 0, + }, + }, + }, + features: { + properties: { + deprecation_logging: { + properties: { + enabled: { + type: 'boolean', + null_value: true, + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/legacy/plugins/uptime/README.md b/x-pack/plugins/uptime/README.md similarity index 100% rename from x-pack/legacy/plugins/uptime/README.md rename to x-pack/plugins/uptime/README.md diff --git a/x-pack/legacy/plugins/uptime/common/constants/alerts.ts b/x-pack/plugins/uptime/common/constants/alerts.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/alerts.ts rename to x-pack/plugins/uptime/common/constants/alerts.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/capabilities.ts b/x-pack/plugins/uptime/common/constants/capabilities.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/capabilities.ts rename to x-pack/plugins/uptime/common/constants/capabilities.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts b/x-pack/plugins/uptime/common/constants/chart_format_limits.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts rename to x-pack/plugins/uptime/common/constants/chart_format_limits.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts b/x-pack/plugins/uptime/common/constants/client_defaults.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/client_defaults.ts rename to x-pack/plugins/uptime/common/constants/client_defaults.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts b/x-pack/plugins/uptime/common/constants/context_defaults.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts rename to x-pack/plugins/uptime/common/constants/context_defaults.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/index.ts b/x-pack/plugins/uptime/common/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/index.ts rename to x-pack/plugins/uptime/common/constants/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/plugin.ts b/x-pack/plugins/uptime/common/constants/plugin.ts similarity index 64% rename from x-pack/legacy/plugins/uptime/common/constants/plugin.ts rename to x-pack/plugins/uptime/common/constants/plugin.ts index 00781726941d5c..6064524872a0ac 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/plugin.ts +++ b/x-pack/plugins/uptime/common/constants/plugin.ts @@ -8,12 +8,17 @@ import { i18n } from '@kbn/i18n'; export const PLUGIN = { APP_ROOT_ID: 'react-uptime-root', - DESCRIPTION: 'Uptime monitoring', + DESCRIPTION: i18n.translate('xpack.uptime.pluginDescription', { + defaultMessage: 'Uptime monitoring', + description: 'The description text that will appear in the feature catalogue.', + }), ID: 'uptime', LOCAL_STORAGE_KEY: 'xpack.uptime', NAME: i18n.translate('xpack.uptime.featureRegistry.uptimeFeatureName', { defaultMessage: 'Uptime', }), ROUTER_BASE_NAME: '/app/uptime#', - TITLE: 'uptime', + TITLE: i18n.translate('xpack.uptime.uptimeFeatureCatalogueTitle', { + defaultMessage: 'Uptime', + }), }; diff --git a/x-pack/legacy/plugins/uptime/common/constants/query.ts b/x-pack/plugins/uptime/common/constants/query.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/query.ts rename to x-pack/plugins/uptime/common/constants/query.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts b/x-pack/plugins/uptime/common/constants/rest_api.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/rest_api.ts rename to x-pack/plugins/uptime/common/constants/rest_api.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/settings_defaults.ts b/x-pack/plugins/uptime/common/constants/settings_defaults.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/settings_defaults.ts rename to x-pack/plugins/uptime/common/constants/settings_defaults.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/constants/ui.ts rename to x-pack/plugins/uptime/common/constants/ui.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/alerts/index.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/alerts/index.ts rename to x-pack/plugins/uptime/common/runtime_types/alerts/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/alerts/status_check.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/alerts/status_check.ts rename to x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/certs.ts b/x-pack/plugins/uptime/common/runtime_types/certs.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/certs.ts rename to x-pack/plugins/uptime/common/runtime_types/certs.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts b/x-pack/plugins/uptime/common/runtime_types/common.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/common.ts rename to x-pack/plugins/uptime/common/runtime_types/common.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts b/x-pack/plugins/uptime/common/runtime_types/dynamic_settings.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/dynamic_settings.ts rename to x-pack/plugins/uptime/common/runtime_types/dynamic_settings.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/index.ts b/x-pack/plugins/uptime/common/runtime_types/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/index.ts rename to x-pack/plugins/uptime/common/runtime_types/index.ts index 78aab3806ae049..e80471bf8b56f9 100644 --- a/x-pack/legacy/plugins/uptime/common/runtime_types/index.ts +++ b/x-pack/plugins/uptime/common/runtime_types/index.ts @@ -7,8 +7,8 @@ export * from './alerts'; export * from './certs'; export * from './common'; +export * from './dynamic_settings'; export * from './monitor'; export * from './overview_filters'; export * from './ping'; export * from './snapshot'; -export * from './dynamic_settings'; diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/details.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/details.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/monitor/details.ts rename to x-pack/plugins/uptime/common/runtime_types/monitor/details.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/index.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/monitor/index.ts rename to x-pack/plugins/uptime/common/runtime_types/monitor/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/monitor/locations.ts rename to x-pack/plugins/uptime/common/runtime_types/monitor/locations.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/monitor/state.ts b/x-pack/plugins/uptime/common/runtime_types/monitor/state.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/monitor/state.ts rename to x-pack/plugins/uptime/common/runtime_types/monitor/state.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/overview_filters/index.ts b/x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/overview_filters/index.ts rename to x-pack/plugins/uptime/common/runtime_types/overview_filters/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts b/x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts rename to x-pack/plugins/uptime/common/runtime_types/overview_filters/overview_filters.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/ping/histogram.ts b/x-pack/plugins/uptime/common/runtime_types/ping/histogram.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/ping/histogram.ts rename to x-pack/plugins/uptime/common/runtime_types/ping/histogram.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/ping/index.ts b/x-pack/plugins/uptime/common/runtime_types/ping/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/ping/index.ts rename to x-pack/plugins/uptime/common/runtime_types/ping/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/ping/ping.ts rename to x-pack/plugins/uptime/common/runtime_types/ping/ping.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/snapshot/index.ts b/x-pack/plugins/uptime/common/runtime_types/snapshot/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/snapshot/index.ts rename to x-pack/plugins/uptime/common/runtime_types/snapshot/index.ts diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/snapshot/snapshot_count.ts b/x-pack/plugins/uptime/common/runtime_types/snapshot/snapshot_count.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/common/runtime_types/snapshot/snapshot_count.ts rename to x-pack/plugins/uptime/common/runtime_types/snapshot/snapshot_count.ts diff --git a/x-pack/legacy/plugins/uptime/common/types/index.ts b/x-pack/plugins/uptime/common/types/index.ts similarity index 69% rename from x-pack/legacy/plugins/uptime/common/types/index.ts rename to x-pack/plugins/uptime/common/types/index.ts index a32eabd49a3e5e..71ccd54dd3cdcd 100644 --- a/x-pack/legacy/plugins/uptime/common/types/index.ts +++ b/x-pack/plugins/uptime/common/types/index.ts @@ -4,18 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -/** Represents a bucket of monitor status information. */ -export interface StatusData { - /** The timeseries point for this status data. */ - x: number; - /** The value of up counts for this point. */ - up?: number | null; - /** The value for down counts for this point. */ - down?: number | null; - /** The total down counts for this point. */ - total?: number | null; -} - /** Represents the average monitor duration ms at a point in time. */ export interface MonitorDurationAveragePoint { /** The timeseries value for this point. */ diff --git a/x-pack/plugins/uptime/kibana.json b/x-pack/plugins/uptime/kibana.json index 6ec8a755ebea09..ce8b64ce07254a 100644 --- a/x-pack/plugins/uptime/kibana.json +++ b/x-pack/plugins/uptime/kibana.json @@ -2,8 +2,16 @@ "configPath": ["xpack", "uptime"], "id": "uptime", "kibanaVersion": "kibana", - "requiredPlugins": ["alerting", "features", "licensing", "usageCollection"], + "optionalPlugins": ["capabilities", "data", "home"], + "requiredPlugins": [ + "alerting", + "embeddable", + "features", + "licensing", + "triggers_actions_ui", + "usageCollection" + ], "server": true, - "ui": false, + "ui": true, "version": "8.0.0" } diff --git a/x-pack/legacy/plugins/uptime/public/app.ts b/x-pack/plugins/uptime/public/app.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/app.ts rename to x-pack/plugins/uptime/public/app.ts diff --git a/x-pack/legacy/plugins/remote_clusters/common/index.ts b/x-pack/plugins/uptime/public/apps/index.ts similarity index 82% rename from x-pack/legacy/plugins/remote_clusters/common/index.ts rename to x-pack/plugins/uptime/public/apps/index.ts index baad348d7a1365..65b80d08d4f20f 100644 --- a/x-pack/legacy/plugins/remote_clusters/common/index.ts +++ b/x-pack/plugins/uptime/public/apps/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PLUGIN = { - ID: 'remoteClusters', -}; +export { UptimePlugin } from './plugin'; diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts new file mode 100644 index 00000000000000..719dac022dada8 --- /dev/null +++ b/x-pack/plugins/uptime/public/apps/plugin.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 { LegacyCoreStart, AppMountParameters } from 'src/core/public'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; +import { UMFrontendLibs } from '../lib/lib'; +import { PLUGIN } from '../../common/constants'; +import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; +import { getKibanaFrameworkAdapter } from '../lib/adapters/framework/new_platform_adapter'; +import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; +import { EmbeddableStart } from '../../../../../src/plugins/embeddable/public'; +import { TriggersAndActionsUIPublicPluginSetup } from '../../../triggers_actions_ui/public'; +import { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; + +export interface StartObject { + core: LegacyCoreStart; + plugins: any; +} + +export interface ClientPluginsSetup { + data: DataPublicPluginSetup; + home: HomePublicPluginSetup; + triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; +} + +export interface ClientPluginsStart { + embeddable: EmbeddableStart; +} + +export class UptimePlugin implements Plugin { + constructor(_context: PluginInitializerContext) {} + + public async setup( + core: CoreSetup, + plugins: ClientPluginsSetup + ): Promise { + if (plugins.home) { + plugins.home.featureCatalogue.register({ + id: PLUGIN.ID, + title: PLUGIN.TITLE, + description: PLUGIN.DESCRIPTION, + icon: 'uptimeApp', + path: '/app/uptime#/', + showOnHomePage: true, + category: FeatureCatalogueCategory.DATA, + }); + } + + core.application.register({ + appRoute: '/app/uptime#/', + id: PLUGIN.ID, + euiIconType: 'uptimeApp', + order: 8900, + title: PLUGIN.TITLE, + async mount(params: AppMountParameters) { + const [coreStart, corePlugins] = await core.getStartServices(); + const { element } = params; + const libs: UMFrontendLibs = { + framework: getKibanaFrameworkAdapter(coreStart, plugins, corePlugins), + }; + libs.framework.render(element); + return () => {}; + }, + }); + } + + public start(_start: CoreStart, _plugins: {}): void {} + + public stop(): void {} +} diff --git a/x-pack/legacy/plugins/uptime/public/apps/template.html b/x-pack/plugins/uptime/public/apps/template.html similarity index 100% rename from x-pack/legacy/plugins/uptime/public/apps/template.html rename to x-pack/plugins/uptime/public/apps/template.html diff --git a/x-pack/legacy/plugins/uptime/public/badge.ts b/x-pack/plugins/uptime/public/badge.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/badge.ts rename to x-pack/plugins/uptime/public/badge.ts diff --git a/x-pack/legacy/plugins/uptime/public/breadcrumbs.ts b/x-pack/plugins/uptime/public/breadcrumbs.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/breadcrumbs.ts rename to x-pack/plugins/uptime/public/breadcrumbs.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/__tests__/location_link.test.tsx b/x-pack/plugins/uptime/public/components/common/__tests__/location_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/__tests__/location_link.test.tsx rename to x-pack/plugins/uptime/public/components/common/__tests__/location_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx b/x-pack/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx rename to x-pack/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/chart_empty_state.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/chart_empty_state.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/chart_empty_state.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/chart_empty_state.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/chart_wrapper.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/chart_wrapper.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/chart_wrapper.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/chart_wrapper.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend_row.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend_row.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend_row.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend_row.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/duration_charts.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/duration_charts.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/duration_charts.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/duration_charts.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts b/x-pack/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx rename to x-pack/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx b/x-pack/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx rename to x-pack/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_empty_state.tsx b/x-pack/plugins/uptime/public/components/common/charts/chart_empty_state.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/chart_empty_state.tsx rename to x-pack/plugins/uptime/public/components/common/charts/chart_empty_state.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx rename to x-pack/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts b/x-pack/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts rename to x-pack/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart.tsx rename to x-pack/plugins/uptime/public/components/common/charts/donut_chart.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx rename to x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx rename to x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx rename to x-pack/plugins/uptime/public/components/common/charts/duration_chart.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx similarity index 94% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx rename to x-pack/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx index a31a143b71fd24..ceb1e700f293e2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx @@ -9,11 +9,11 @@ import moment from 'moment'; import { AnnotationTooltipFormatter, RectAnnotation } from '@elastic/charts'; import { RectAnnotationDatum } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs'; import { AnnotationTooltip } from './annotation_tooltip'; -import { ANOMALY_SEVERITY } from '../../../../../../../plugins/ml/common/constants/anomalies'; +import { ANOMALY_SEVERITY } from '../../../../../../plugins/ml/common/constants/anomalies'; import { getSeverityColor, getSeverityType, -} from '../../../../../../../plugins/ml/common/util/anomaly_utils'; +} from '../../../../../../plugins/ml/common/util/anomaly_utils'; interface Props { anomalies: any; diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx b/x-pack/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx rename to x-pack/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/get_tick_format.ts b/x-pack/plugins/uptime/public/components/common/charts/get_tick_format.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/get_tick_format.ts rename to x-pack/plugins/uptime/public/components/common/charts/get_tick_format.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/index.ts b/x-pack/plugins/uptime/public/components/common/charts/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/index.ts rename to x-pack/plugins/uptime/public/components/common/charts/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx b/x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx rename to x-pack/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/charts/ping_histogram.tsx rename to x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx b/x-pack/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx rename to x-pack/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/higher_order/index.ts b/x-pack/plugins/uptime/public/components/common/higher_order/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/higher_order/index.ts rename to x-pack/plugins/uptime/public/components/common/higher_order/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx b/x-pack/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx rename to x-pack/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/location_link.tsx b/x-pack/plugins/uptime/public/components/common/location_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/location_link.tsx rename to x-pack/plugins/uptime/public/components/common/location_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/uptime_date_picker.tsx b/x-pack/plugins/uptime/public/components/common/uptime_date_picker.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/common/uptime_date_picker.tsx rename to x-pack/plugins/uptime/public/components/common/uptime_date_picker.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx b/x-pack/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/index.ts b/x-pack/plugins/uptime/public/components/monitor/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/index.ts rename to x-pack/plugins/uptime/public/components/monitor/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx similarity index 85% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx index 85d0b1b593704a..06cdb07bd8bcdb 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx @@ -7,21 +7,17 @@ import React, { useEffect, useState, useContext, useRef } from 'react'; import uuid from 'uuid'; import styled from 'styled-components'; -import { npStart } from 'ui/new_platform'; - -import { - ViewMode, - EmbeddableOutput, - ErrorEmbeddable, - isErrorEmbeddable, -} from '../../../../../../../../../src/plugins/embeddable/public'; +import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../../legacy/plugins/maps/public'; import * as i18n from './translations'; -import { MapEmbeddable, MapEmbeddableInput } from '../../../../../../maps/public'; -import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../../../plugins/maps/public'; import { Location } from '../../../../../common/runtime_types'; - import { getLayerList } from './map_config'; -import { UptimeThemeContext } from '../../../../contexts'; +import { UptimeThemeContext, UptimeStartupPluginsContext } from '../../../../contexts'; +import { + isErrorEmbeddable, + ViewMode, + ErrorEmbeddable, +} from '../../../../../../../../src/plugins/embeddable/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/public'; export interface EmbeddedMapProps { upPoints: LocationPoint[]; @@ -52,11 +48,11 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp const { colors } = useContext(UptimeThemeContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = useRef(null); - const factory = npStart.plugins.embeddable.getEmbeddableFactory< - MapEmbeddableInput, - EmbeddableOutput, - MapEmbeddable - >(MAP_SAVED_OBJECT_TYPE); + const { embeddable: embeddablePlugin } = useContext(UptimeStartupPluginsContext); + if (!embeddablePlugin) { + throw new Error('Embeddable start plugin not found'); + } + const factory: any = embeddablePlugin.getEmbeddableFactory(MAP_SAVED_OBJECT_TYPE); const input: MapEmbeddableInput = { id: uuid.v4(), @@ -88,7 +84,7 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp if (!factory) { throw new Error('Map embeddable not found.'); } - const embeddableObject = await factory.create({ + const embeddableObject: any = await factory.create({ ...input, title: i18n.MAP_TITLE, }); diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts b/x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts rename to x-pack/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/index.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/index.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_map.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/location_map.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_map.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/location_map.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_missing.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/location_missing.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_missing.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/location_missing.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx b/x-pack/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx rename to x-pack/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx index c0b02181dcce1a..31cdcfac9feef3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx @@ -9,7 +9,7 @@ import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { MLFlyoutView } from '../ml_flyout'; import { UptimeSettingsContext } from '../../../../contexts'; import { CLIENT_DEFAULTS } from '../../../../../common/constants'; -import { License } from '../../../../../../../../plugins/licensing/common/license'; +import { License } from '../../../../../../../plugins/licensing/common/license'; const expiredLicense = new License({ signature: 'test signature', diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/index.ts b/x-pack/plugins/uptime/public/components/monitor/ml/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/index.ts rename to x-pack/plugins/uptime/public/components/monitor/ml/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/license_info.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/license_info.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/license_info.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx index c3e8579ca48370..6eec30d405f769 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx @@ -19,7 +19,7 @@ import * as labels from './translations'; import { useKibana, KibanaReactNotifications, -} from '../../../../../../../../src/plugins/kibana_react/public'; +} from '../../../../../../../src/plugins/kibana_react/public'; import { MLFlyoutView } from './ml_flyout'; import { ML_JOB_ID } from '../../../../common/constants'; import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx index 4963a901f0ecc5..7f19885c15406e 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx @@ -18,9 +18,9 @@ import { ConfirmJobDeletion } from './confirm_delete'; import { UptimeRefreshContext } from '../../../contexts'; import { getMLJobId } from '../../../state/api/ml_anomaly'; import * as labels from './translations'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ManageMLJobComponent } from './manage_ml_job'; -import { JobStat } from '../../../../../../../plugins/ml/common/types/data_recognizer'; +import { JobStat } from '../../../../../../plugins/ml/common/types/data_recognizer'; import { useMonitorId } from '../../../hooks'; export const MLIntegrationComponent = () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ml/translations.tsx b/x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ml/translations.tsx rename to x-pack/plugins/uptime/public/components/monitor/ml/translations.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_charts.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_charts.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_charts.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_charts.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts rename to x-pack/plugins/uptime/public/components/monitor/monitor_duration/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx index 7e39b977f12710..52d4f620f84b3f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx @@ -20,7 +20,7 @@ import { } from '../../../state/selectors'; import { UptimeRefreshContext } from '../../../contexts'; import { getMLJobId } from '../../../state/api/ml_anomaly'; -import { JobStat } from '../../../../../../../plugins/ml/common/types/data_recognizer'; +import { JobStat } from '../../../../../ml/common/types/data_recognizer'; import { MonitorDurationComponent } from './monitor_duration'; import { MonitorIdParam } from '../../../../common/types'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/status_by_location.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/status_by_location.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/status_by_location.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/status_by_location.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/status_by_location.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/index.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/index.ts rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/index.ts b/x-pack/plugins/uptime/public/components/monitor/ping_histogram/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/index.ts rename to x-pack/plugins/uptime/public/components/monitor/ping_histogram/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/index.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/index.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/location_name.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/location_name.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/location_name.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/location_name.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx rename to x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx b/x-pack/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx rename to x-pack/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx b/x-pack/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx rename to x-pack/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx b/x-pack/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx rename to x-pack/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts rename to x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/index.ts rename to x-pack/plugins/uptime/public/components/overview/alerts/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index 04dfe4b3e3509a..92fc015a276d3f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } f import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; interface Props { setAlertFlyoutVisible: (value: boolean) => void; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx similarity index 83% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx index 09e6dc72b7f981..262e1552e3660d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx @@ -5,8 +5,8 @@ */ import React from 'react'; -import { AlertsContextProvider } from '../../../../../../../plugins/triggers_actions_ui/public'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { AlertsContextProvider } from '../../../../../../plugins/triggers_actions_ui/public'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; export const UptimeAlertsContextProvider: React.FC = ({ children }) => { const { diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx similarity index 90% rename from x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx rename to x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index 13705e7d192933..9b1d3a73dc6614 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { AlertAdd } from '../../../../../../../plugins/triggers_actions_ui/public'; +import { AlertAdd } from '../../../../../../plugins/triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx index 651103a34bf212..d38f203739ceac 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state.tsx +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state.tsx @@ -10,7 +10,7 @@ import { EmptyStateError } from './empty_state_error'; import { EmptyStateLoading } from './empty_state_loading'; import { DataOrIndexMissing } from './data_or_index_missing'; import { DynamicSettings, StatesIndexStatus } from '../../../../common/runtime_types'; -import { IHttpFetchError } from '../../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; interface EmptyStateProps { children: JSX.Element[] | JSX.Element; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx index 1135b969018a11..aa4040e319e0f2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx @@ -7,7 +7,7 @@ import { EuiEmptyPrompt, EuiPanel, EuiTitle, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { IHttpFetchError } from '../../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; interface EmptyStateErrorProps { errors: IHttpFetchError[]; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx b/x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx rename to x-pack/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts b/x-pack/plugins/uptime/public/components/overview/empty_state/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts rename to x-pack/plugins/uptime/public/components/overview/empty_state/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts rename to x-pack/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/index.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/index.ts rename to x-pack/plugins/uptime/public/components/overview/filter_group/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts rename to x-pack/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts b/x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts rename to x-pack/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx rename to x-pack/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/index.ts b/x-pack/plugins/uptime/public/components/overview/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/index.ts rename to x-pack/plugins/uptime/public/components/overview/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts b/x-pack/plugins/uptime/public/components/overview/kuery_bar/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx index 63aceed2be6362..792fff4c7cdca2 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx @@ -16,7 +16,7 @@ import { IIndexPattern, QuerySuggestion, DataPublicPluginSetup, -} from '../../../../../../../../src/plugins/data/public'; +} from '../../../../../../../src/plugins/data/public'; const Container = styled.div` margin-bottom: 10px; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx b/x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js index 2b5ad9b59e39f8..936eae04ffa641 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js @@ -14,7 +14,7 @@ import { units, fontSizes, unit, -} from '../../../../../../apm/public/style/variables'; +} from '../../../../../../../legacy/plugins/apm/public/style/variables'; import { tint } from 'polished'; import theme from '@elastic/eui/dist/eui_theme_light.json'; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js rename to x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js index 7fbabe71bcdb52..ac6832050b9d3c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import { isEmpty } from 'lodash'; import Suggestion from './suggestion'; -import { units, px, unit } from '../../../../../../apm/public/style/variables'; +import { units, px, unit } from '../../../../../../../legacy/plugins/apm/public/style/variables'; import { tint } from 'polished'; import theme from '@elastic/eui/dist/eui_theme_light.json'; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/index.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/index.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx index 18e2e2437e1477..7e9536689470e6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx @@ -202,13 +202,7 @@ export const MonitorListComponent: React.FC = ({ itemId="monitor_id" itemIdToExpandedRowMap={getExpandedRowMap()} items={items} - // TODO: not needed without sorting and pagination - // onChange={onChange} noItemsMessage={!!filters ? labels.NO_MONITOR_ITEM_SELECTED : labels.NO_DATA_MESSAGE} - // TODO: reintegrate pagination in future release - // pagination={pagination} - // TODO: reintegrate sorting in future release - // sorting={sorting} columns={columns} /> diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx index 5bfe6ff0c5b4f1..6fb880e28c7347 100644 --- a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx @@ -9,7 +9,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { getMonitorList } from '../../../state/actions'; import { FetchMonitorStatesQueryArgs } from '../../../../common/runtime_types'; import { monitorListSelector } from '../../../state/selectors'; -import { MonitorListComponent } from './index'; +import { MonitorListComponent } from './monitor_list'; export interface MonitorListProps { filters?: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx rename to x-pack/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/translations.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/translations.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/types.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/types.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/types.ts rename to x-pack/plugins/uptime/public/components/overview/monitor_list/types.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/overview_container.tsx b/x-pack/plugins/uptime/public/components/overview/overview_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/overview_container.tsx rename to x-pack/plugins/uptime/public/components/overview/overview_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/parsing_error_callout.tsx b/x-pack/plugins/uptime/public/components/overview/parsing_error_callout.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/parsing_error_callout.tsx rename to x-pack/plugins/uptime/public/components/overview/parsing_error_callout.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/snapshot/index.ts b/x-pack/plugins/uptime/public/components/overview/snapshot/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/snapshot/index.ts rename to x-pack/plugins/uptime/public/components/overview/snapshot/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot.tsx b/x-pack/plugins/uptime/public/components/overview/snapshot/snapshot.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot.tsx rename to x-pack/plugins/uptime/public/components/overview/snapshot/snapshot.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_container.tsx b/x-pack/plugins/uptime/public/components/overview/snapshot/snapshot_container.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_container.tsx rename to x-pack/plugins/uptime/public/components/overview/snapshot/snapshot_container.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx b/x-pack/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx rename to x-pack/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/status_panel.tsx b/x-pack/plugins/uptime/public/components/overview/status_panel.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/overview/status_panel.tsx rename to x-pack/plugins/uptime/public/components/overview/status_panel.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap b/x-pack/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap rename to x-pack/plugins/uptime/public/components/settings/__tests__/__snapshots__/certificate_form.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap b/x-pack/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap rename to x-pack/plugins/uptime/public/components/settings/__tests__/__snapshots__/indices_form.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx b/x-pack/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx rename to x-pack/plugins/uptime/public/components/settings/__tests__/certificate_form.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx b/x-pack/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx rename to x-pack/plugins/uptime/public/components/settings/__tests__/indices_form.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/certificate_form.tsx b/x-pack/plugins/uptime/public/components/settings/certificate_form.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/certificate_form.tsx rename to x-pack/plugins/uptime/public/components/settings/certificate_form.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/settings/indices_form.tsx b/x-pack/plugins/uptime/public/components/settings/indices_form.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/settings/indices_form.tsx rename to x-pack/plugins/uptime/public/components/settings/indices_form.tsx diff --git a/x-pack/legacy/plugins/uptime/public/contexts/index.ts b/x-pack/plugins/uptime/public/contexts/index.ts similarity index 82% rename from x-pack/legacy/plugins/uptime/public/contexts/index.ts rename to x-pack/plugins/uptime/public/contexts/index.ts index 2b27fcfe907abd..243a25c26901a1 100644 --- a/x-pack/legacy/plugins/uptime/public/contexts/index.ts +++ b/x-pack/plugins/uptime/public/contexts/index.ts @@ -11,3 +11,7 @@ export { UptimeSettingsContextProvider, } from './uptime_settings_context'; export { UptimeThemeContextProvider, UptimeThemeContext } from './uptime_theme_context'; +export { + UptimeStartupPluginsContext, + UptimeStartupPluginsContextProvider, +} from './uptime_startup_plugins_context'; diff --git a/x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_refresh_context.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx rename to x-pack/plugins/uptime/public/contexts/uptime_refresh_context.tsx diff --git a/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx rename to x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx index 137846de103b4d..4fabf3f2ed4972 100644 --- a/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx +++ b/x-pack/plugins/uptime/public/contexts/uptime_settings_context.tsx @@ -9,7 +9,7 @@ import { UptimeAppProps } from '../uptime_app'; import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../common/constants'; import { CommonlyUsedRange } from '../components/common/uptime_date_picker'; import { useGetUrlParams } from '../hooks'; -import { ILicense } from '../../../../../plugins/licensing/common/types'; +import { ILicense } from '../../../../plugins/licensing/common/types'; export interface UptimeSettingsContextValues { basePath: string; diff --git a/x-pack/plugins/uptime/public/contexts/uptime_startup_plugins_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_startup_plugins_context.tsx new file mode 100644 index 00000000000000..e516ff44aa12ab --- /dev/null +++ b/x-pack/plugins/uptime/public/contexts/uptime_startup_plugins_context.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { createContext } from 'react'; +import { ClientPluginsStart } from '../apps/plugin'; + +export const UptimeStartupPluginsContext = createContext>({}); + +export const UptimeStartupPluginsContextProvider: React.FC> = ({ + children, + ...props +}) => ; diff --git a/x-pack/legacy/plugins/uptime/public/contexts/uptime_theme_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/contexts/uptime_theme_context.tsx rename to x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx diff --git a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap b/x-pack/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap rename to x-pack/plugins/uptime/public/hooks/__tests__/__snapshots__/use_url_params.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx b/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx rename to x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx index 1ce00fe7ce3af5..306919015fcb1d 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx +++ b/x-pack/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Route } from 'react-router-dom'; import { mountWithRouter } from '../../lib'; import { OVERVIEW_ROUTE } from '../../../common/constants'; -import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { UptimeUrlParams, getSupportedUrlParams } from '../../lib/helper'; import { makeBaseBreadcrumb, useBreadcrumbs } from '../use_breadcrumbs'; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_url_params.test.tsx b/x-pack/plugins/uptime/public/hooks/__tests__/use_url_params.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_url_params.test.tsx rename to x-pack/plugins/uptime/public/hooks/__tests__/use_url_params.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/hooks/index.ts b/x-pack/plugins/uptime/public/hooks/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/index.ts rename to x-pack/plugins/uptime/public/hooks/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts b/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts similarity index 95% rename from x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts rename to x-pack/plugins/uptime/public/hooks/update_kuery_string.ts index ab4d6f75849e88..492d2bab5bb806 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/update_kuery_string.ts +++ b/x-pack/plugins/uptime/public/hooks/update_kuery_string.ts @@ -5,7 +5,7 @@ */ import { combineFiltersAndUserSearch, stringifyKueries } from '../lib/helper'; -import { esKuery, IIndexPattern } from '../../../../../../src/plugins/data/public'; +import { esKuery, IIndexPattern } from '../../../../../src/plugins/data/public'; const getKueryString = (urlFilters: string): string => { let kueryString = ''; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_breadcrumbs.ts b/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts similarity index 94% rename from x-pack/legacy/plugins/uptime/public/hooks/use_breadcrumbs.ts rename to x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts index d1cc8e18973868..182c6b01141284 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_breadcrumbs.ts +++ b/x-pack/plugins/uptime/public/hooks/use_breadcrumbs.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useEffect } from 'react'; import { UptimeUrlParams } from '../lib/helper'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { useUrlParams } from '.'; export const makeBaseBreadcrumb = (params?: UptimeUrlParams): ChromeBreadcrumb => { diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_monitor.ts b/x-pack/plugins/uptime/public/hooks/use_monitor.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/use_monitor.ts rename to x-pack/plugins/uptime/public/hooks/use_monitor.ts diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts rename to x-pack/plugins/uptime/public/hooks/use_telemetry.ts diff --git a/x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/plugins/uptime/public/hooks/use_url_params.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/hooks/use_url_params.ts rename to x-pack/plugins/uptime/public/hooks/use_url_params.ts diff --git a/x-pack/legacy/plugins/uptime/public/icons/heartbeat_white.svg b/x-pack/plugins/uptime/public/icons/heartbeat_white.svg similarity index 100% rename from x-pack/legacy/plugins/uptime/public/icons/heartbeat_white.svg rename to x-pack/plugins/uptime/public/icons/heartbeat_white.svg diff --git a/x-pack/plugins/uptime/public/index.ts b/x-pack/plugins/uptime/public/index.ts new file mode 100644 index 00000000000000..48cf2c90ad07b1 --- /dev/null +++ b/x-pack/plugins/uptime/public/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. + */ + +import { PluginInitializerContext } from 'kibana/public'; +import { UptimePlugin } from './apps'; + +export const plugin = (initializerContext: PluginInitializerContext) => + new UptimePlugin(initializerContext); diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/capabilities_adapter.ts b/x-pack/plugins/uptime/public/lib/adapters/framework/capabilities_adapter.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/adapters/framework/capabilities_adapter.ts rename to x-pack/plugins/uptime/public/lib/adapters/framework/capabilities_adapter.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx b/x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx similarity index 86% rename from x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx rename to x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx index 71c73bf5ba5d4f..f7f9e056f41af5 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx +++ b/x-pack/plugins/uptime/public/lib/adapters/framework/new_platform_adapter.tsx @@ -9,7 +9,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { get } from 'lodash'; import { i18n as i18nFormatter } from '@kbn/i18n'; -import { PluginsSetup } from 'ui/new_platform/new_platform'; import { alertTypeInitializers } from '../../alert_types'; import { UptimeApp, UptimeAppProps } from '../../../uptime_app'; import { getIntegratedAppAvailability } from './capabilities_adapter'; @@ -20,10 +19,12 @@ import { DEFAULT_TIMEPICKER_QUICK_RANGES, } from '../../../../common/constants'; import { UMFrameworkAdapter } from '../../lib'; +import { ClientPluginsStart, ClientPluginsSetup } from '../../../apps/plugin'; export const getKibanaFrameworkAdapter = ( core: CoreStart, - plugins: PluginsSetup + plugins: ClientPluginsSetup, + startPlugins: ClientPluginsStart ): UMFrameworkAdapter => { const { application: { capabilities }, @@ -35,14 +36,15 @@ export const getKibanaFrameworkAdapter = ( const { data: { autocomplete }, - // TODO: after NP migration we can likely fix this typing problem - // @ts-ignore we don't control this type triggers_actions_ui, } = plugins; - alertTypeInitializers.forEach(init => - triggers_actions_ui.alertTypeRegistry.register(init({ autocomplete })) - ); + alertTypeInitializers.forEach(init => { + const alertInitializer = init({ autocomplete }); + if (!triggers_actions_ui.alertTypeRegistry.has(alertInitializer.id)) { + triggers_actions_ui.alertTypeRegistry.register(init({ autocomplete })); + } + }); let breadcrumbs: ChromeBreadcrumb[] = []; core.chrome.getBreadcrumbs$().subscribe((nextBreadcrumbs?: ChromeBreadcrumb[]) => { @@ -68,6 +70,7 @@ export const getKibanaFrameworkAdapter = ( isLogsAvailable: logs, kibanaBreadcrumbs: breadcrumbs, plugins, + startPlugins, renderGlobalHelpControls: () => setHelpExtension({ appName: i18nFormatter.translate('xpack.uptime.header.appName', { diff --git a/x-pack/legacy/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts b/x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts rename to x-pack/plugins/uptime/public/lib/alert_types/__tests__/monitor_status.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts b/x-pack/plugins/uptime/public/lib/alert_types/index.ts similarity index 67% rename from x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts rename to x-pack/plugins/uptime/public/lib/alert_types/index.ts index 74160577cb0b13..f7ab254ffe675f 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/index.ts @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// TODO: after NP migration is complete we should be able to remove this lint ignore comment -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../../../plugins/triggers_actions_ui/public'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { initMonitorStatusAlertType } from './monitor_status'; export type AlertTypeInitializer = (dependenies: { autocomplete: any }) => AlertTypeModel; diff --git a/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx similarity index 88% rename from x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx rename to x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index 0624d20b197c0e..e7695fb1cbb56c 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -8,17 +8,12 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import React from 'react'; import DateMath from '@elastic/datemath'; import { isRight } from 'fp-ts/lib/Either'; -import { - AlertTypeModel, - ValidationResult, - // TODO: this typing issue should be resolved after NP migration - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/triggers_actions_ui/public/types'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { AlertTypeInitializer } from '.'; import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; import { AlertMonitorStatus } from '../../components/overview/alerts/alerts_containers'; -export const validate = (alertParams: any): ValidationResult => { +export const validate = (alertParams: any) => { const errors: Record = {}; const decoded = StatusCheckExecutorParamsType.decode(alertParams); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_kueries.test.ts.snap b/x-pack/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_kueries.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_kueries.test.ts.snap rename to x-pack/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_kueries.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap b/x-pack/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap rename to x-pack/plugins/uptime/public/lib/helper/__tests__/__snapshots__/stringify_url_params.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/combine_filters_and_user_search.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/combine_filters_and_user_search.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/combine_filters_and_user_search.test.ts rename to x-pack/plugins/uptime/public/lib/helper/__tests__/combine_filters_and_user_search.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/convert_measurements.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/convert_measurements.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/convert_measurements.test.ts rename to x-pack/plugins/uptime/public/lib/helper/__tests__/convert_measurements.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/series_has_down_values.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/series_has_down_values.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/series_has_down_values.test.ts rename to x-pack/plugins/uptime/public/lib/helper/__tests__/series_has_down_values.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/stringify_kueries.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_kueries.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/stringify_kueries.test.ts rename to x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_kueries.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts b/x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts rename to x-pack/plugins/uptime/public/lib/helper/__tests__/stringify_url_params.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/__tests__/get_label_format.test.ts b/x-pack/plugins/uptime/public/lib/helper/charts/__tests__/get_label_format.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/__tests__/get_label_format.test.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/__tests__/get_label_format.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/__tests__/is_within_current_date.test.ts b/x-pack/plugins/uptime/public/lib/helper/charts/__tests__/is_within_current_date.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/__tests__/is_within_current_date.test.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/__tests__/is_within_current_date.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts b/x-pack/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts b/x-pack/plugins/uptime/public/lib/helper/charts/get_label_format.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/get_label_format.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/index.ts b/x-pack/plugins/uptime/public/lib/helper/charts/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/index.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/is_within_current_date.ts b/x-pack/plugins/uptime/public/lib/helper/charts/is_within_current_date.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/charts/is_within_current_date.ts rename to x-pack/plugins/uptime/public/lib/helper/charts/is_within_current_date.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/combine_filters_and_user_search.ts b/x-pack/plugins/uptime/public/lib/helper/combine_filters_and_user_search.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/combine_filters_and_user_search.ts rename to x-pack/plugins/uptime/public/lib/helper/combine_filters_and_user_search.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts b/x-pack/plugins/uptime/public/lib/helper/convert_measurements.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts rename to x-pack/plugins/uptime/public/lib/helper/convert_measurements.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/get_title.ts b/x-pack/plugins/uptime/public/lib/helper/get_title.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/get_title.ts rename to x-pack/plugins/uptime/public/lib/helper/get_title.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/helper_with_router.tsx b/x-pack/plugins/uptime/public/lib/helper/helper_with_router.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/helper_with_router.tsx rename to x-pack/plugins/uptime/public/lib/helper/helper_with_router.tsx diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/index.ts b/x-pack/plugins/uptime/public/lib/helper/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/index.ts rename to x-pack/plugins/uptime/public/lib/helper/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_apm_href.test.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_apm_href.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_apm_href.test.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_apm_href.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_infra_href.test.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_infra_href.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_infra_href.test.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_infra_href.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_logging_href.test.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_logging_href.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_logging_href.test.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/__tests__/get_logging_href.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/add_base_path.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/add_base_path.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/add_base_path.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/add_base_path.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/build_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/build_href.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/build_href.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/get_apm_href.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/get_infra_href.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/get_logging_href.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/index.ts b/x-pack/plugins/uptime/public/lib/helper/observability_integration/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/observability_integration/index.ts rename to x-pack/plugins/uptime/public/lib/helper/observability_integration/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/series_has_down_values.ts b/x-pack/plugins/uptime/public/lib/helper/series_has_down_values.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/series_has_down_values.ts rename to x-pack/plugins/uptime/public/lib/helper/series_has_down_values.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_kueries.ts b/x-pack/plugins/uptime/public/lib/helper/stringify_kueries.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/stringify_kueries.ts rename to x-pack/plugins/uptime/public/lib/helper/stringify_kueries.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts b/x-pack/plugins/uptime/public/lib/helper/stringify_url_params.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/stringify_url_params.ts rename to x-pack/plugins/uptime/public/lib/helper/stringify_url_params.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap rename to x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/__snapshots__/get_supported_url_params.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/get_supported_url_params.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_url_int.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_url_int.test.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_is_paused.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_is_paused.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_is_paused.test.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_is_paused.test.ts diff --git a/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_url_int.test.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_url_int.test.ts new file mode 100644 index 00000000000000..a5c2168378089d --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/helper/url_params/__tests__/parse_url_int.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 { parseUrlInt } from '../parse_url_int'; + +describe('parseUrlInt', () => { + it('parses a number', () => { + const result = parseUrlInt('23', 50); + expect(result).toBe(23); + }); + + it('returns default value for empty string', () => { + const result = parseUrlInt('', 50); + expect(result).toBe(50); + }); + + it('returns default value for non-numeric string', () => { + const result = parseUrlInt('abc', 50); + expect(result).toBe(50); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/get_supported_url_params.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/index.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/index.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/parse_absolute_date.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_is_paused.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_is_paused.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_is_paused.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/parse_is_paused.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts similarity index 87% rename from x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts rename to x-pack/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts index b1a4d0a2aba0d8..0e5363527b5163 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts +++ b/x-pack/plugins/uptime/public/lib/helper/url_params/parse_url_int.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// TODO: add a comment explaining the purpose of this function export const parseUrlInt = (value: string | undefined, defaultValue: number): number => { const parsed = parseInt(value || '', 10); return isNaN(parsed) ? defaultValue : parsed; diff --git a/x-pack/legacy/plugins/uptime/public/lib/index.ts b/x-pack/plugins/uptime/public/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/index.ts rename to x-pack/plugins/uptime/public/lib/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/lib/lib.ts b/x-pack/plugins/uptime/public/lib/lib.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/lib/lib.ts rename to x-pack/plugins/uptime/public/lib/lib.ts diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap rename to x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/not_found.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/not_found.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/not_found.test.tsx.snap rename to x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/not_found.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap rename to x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/overview.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap rename to x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/page_header.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/monitor.test.tsx b/x-pack/plugins/uptime/public/pages/__tests__/monitor.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/monitor.test.tsx rename to x-pack/plugins/uptime/public/pages/__tests__/monitor.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/not_found.test.tsx b/x-pack/plugins/uptime/public/pages/__tests__/not_found.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/not_found.test.tsx rename to x-pack/plugins/uptime/public/pages/__tests__/not_found.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/overview.test.tsx b/x-pack/plugins/uptime/public/pages/__tests__/overview.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/overview.test.tsx rename to x-pack/plugins/uptime/public/pages/__tests__/overview.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/page_header.test.tsx b/x-pack/plugins/uptime/public/pages/__tests__/page_header.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/__tests__/page_header.test.tsx rename to x-pack/plugins/uptime/public/pages/__tests__/page_header.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/index.ts b/x-pack/plugins/uptime/public/pages/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/index.ts rename to x-pack/plugins/uptime/public/pages/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/plugins/uptime/public/pages/monitor.tsx similarity index 87% rename from x-pack/legacy/plugins/uptime/public/pages/monitor.tsx rename to x-pack/plugins/uptime/public/pages/monitor.tsx index 4495be9b24dc1f..8a309db75acd23 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/plugins/uptime/public/pages/monitor.tsx @@ -7,14 +7,13 @@ import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { useSelector } from 'react-redux'; -import { useTrackPageview } from '../../../../../plugins/observability/public'; import { monitorStatusSelector } from '../state/selectors'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; +import { useTrackPageview } from '../../../observability/public'; import { useMonitorId, useUptimeTelemetry, UptimePage } from '../hooks'; import { MonitorCharts } from '../components/monitor'; -import { MonitorStatusDetails } from '../components/monitor'; -import { PingList } from '../components/monitor'; +import { MonitorStatusDetails, PingList } from '../components/monitor'; export const MonitorPage: React.FC = () => { const monitorId = useMonitorId(); diff --git a/x-pack/legacy/plugins/uptime/public/pages/not_found.tsx b/x-pack/plugins/uptime/public/pages/not_found.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/not_found.tsx rename to x-pack/plugins/uptime/public/pages/not_found.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/plugins/uptime/public/pages/overview.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/pages/overview.tsx rename to x-pack/plugins/uptime/public/pages/overview.tsx index adc36efa6f7db1..fefd804cbfabf0 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/plugins/uptime/public/pages/overview.tsx @@ -10,11 +10,11 @@ import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { useUptimeTelemetry, UptimePage, useGetUrlParams } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; -import { useTrackPageview } from '../../../../../plugins/observability/public'; -import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; -import { useUpdateKueryString } from '../hooks'; import { PageHeader } from './page_header'; +import { DataPublicPluginSetup, IIndexPattern } from '../../../../../src/plugins/data/public'; +import { useUpdateKueryString } from '../hooks'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; +import { useTrackPageview } from '../../../observability/public'; import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; import { EmptyState, FilterGroup, KueryBar, ParsingErrorCallout } from '../components/overview'; import { StatusPanel } from '../components/overview/status_panel'; diff --git a/x-pack/legacy/plugins/uptime/public/pages/page_header.tsx b/x-pack/plugins/uptime/public/pages/page_header.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/page_header.tsx rename to x-pack/plugins/uptime/public/pages/page_header.tsx diff --git a/x-pack/legacy/plugins/uptime/public/pages/settings.tsx b/x-pack/plugins/uptime/public/pages/settings.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/pages/settings.tsx rename to x-pack/plugins/uptime/public/pages/settings.tsx index d8c2a780928541..52096a49435d72 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/settings.tsx +++ b/x-pack/plugins/uptime/public/pages/settings.tsx @@ -24,7 +24,7 @@ import { getDynamicSettings, setDynamicSettings } from '../state/actions/dynamic import { DynamicSettings } from '../../common/runtime_types'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { OVERVIEW_ROUTE } from '../../common/constants'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { UptimePage, useUptimeTelemetry } from '../hooks'; import { IndicesForm } from '../components/settings/indices_form'; import { diff --git a/x-pack/legacy/plugins/uptime/public/pages/translations.ts b/x-pack/plugins/uptime/public/pages/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/pages/translations.ts rename to x-pack/plugins/uptime/public/pages/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/routes.tsx rename to x-pack/plugins/uptime/public/routes.tsx index b5e20ef8a70a9b..eb0587c0417a2b 100644 --- a/x-pack/legacy/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; import { Route, Switch } from 'react-router-dom'; -import { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; +import { DataPublicPluginSetup } from '../../../../src/plugins/data/public'; import { OverviewPage } from './components/overview/overview_container'; import { MONITOR_ROUTE, OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../common/constants'; import { MonitorPage, NotFoundPage, SettingsPage } from './pages'; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/__tests__/__snapshots__/overview_filters.test.ts.snap b/x-pack/plugins/uptime/public/state/actions/__tests__/__snapshots__/overview_filters.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/__tests__/__snapshots__/overview_filters.test.ts.snap rename to x-pack/plugins/uptime/public/state/actions/__tests__/__snapshots__/overview_filters.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/__tests__/overview_filters.test.ts b/x-pack/plugins/uptime/public/state/actions/__tests__/overview_filters.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/__tests__/overview_filters.test.ts rename to x-pack/plugins/uptime/public/state/actions/__tests__/overview_filters.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/dynamic_settings.ts b/x-pack/plugins/uptime/public/state/actions/dynamic_settings.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/dynamic_settings.ts rename to x-pack/plugins/uptime/public/state/actions/dynamic_settings.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index.ts b/x-pack/plugins/uptime/public/state/actions/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/index.ts rename to x-pack/plugins/uptime/public/state/actions/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index_patternts.ts b/x-pack/plugins/uptime/public/state/actions/index_patternts.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/index_patternts.ts rename to x-pack/plugins/uptime/public/state/actions/index_patternts.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts b/x-pack/plugins/uptime/public/state/actions/index_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/index_status.ts rename to x-pack/plugins/uptime/public/state/actions/index_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/actions/ml_anomaly.ts similarity index 82% rename from x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts rename to x-pack/plugins/uptime/public/state/actions/ml_anomaly.ts index 67c75314a7305b..441a3cefdf204d 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/actions/ml_anomaly.ts @@ -6,15 +6,15 @@ import { createAction } from 'redux-actions'; import { createAsyncAction } from './utils'; -import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; -import { AnomaliesTableRecord } from '../../../../../../plugins/ml/common/types/anomalies'; +import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/capabilities'; +import { AnomaliesTableRecord } from '../../../../../plugins/ml/common/types/anomalies'; import { CreateMLJobSuccess, DeleteJobResults, MonitorIdParam, HeartbeatIndicesParam, } from './types'; -import { JobExistResult } from '../../../../../../plugins/ml/common/types/data_recognizer'; +import { JobExistResult } from '../../../../../plugins/ml/common/types/data_recognizer'; export const resetMLState = createAction('RESET_ML_STATE'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts b/x-pack/plugins/uptime/public/state/actions/monitor.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts rename to x-pack/plugins/uptime/public/state/actions/monitor.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_duration.ts b/x-pack/plugins/uptime/public/state/actions/monitor_duration.ts similarity index 90% rename from x-pack/legacy/plugins/uptime/public/state/actions/monitor_duration.ts rename to x-pack/plugins/uptime/public/state/actions/monitor_duration.ts index 9a2db5be60b12a..524044f873687a 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_duration.ts +++ b/x-pack/plugins/uptime/public/state/actions/monitor_duration.ts @@ -7,7 +7,7 @@ import { createAction } from 'redux-actions'; import { QueryParams } from './types'; import { MonitorDurationResult } from '../../../common/types'; -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; type MonitorQueryParams = QueryParams & { monitorId: string }; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_list.ts b/x-pack/plugins/uptime/public/state/actions/monitor_list.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/monitor_list.ts rename to x-pack/plugins/uptime/public/state/actions/monitor_list.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts b/x-pack/plugins/uptime/public/state/actions/monitor_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts rename to x-pack/plugins/uptime/public/state/actions/monitor_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/overview_filters.ts b/x-pack/plugins/uptime/public/state/actions/overview_filters.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/overview_filters.ts rename to x-pack/plugins/uptime/public/state/actions/overview_filters.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/ping.ts b/x-pack/plugins/uptime/public/state/actions/ping.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/ping.ts rename to x-pack/plugins/uptime/public/state/actions/ping.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts b/x-pack/plugins/uptime/public/state/actions/snapshot.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts rename to x-pack/plugins/uptime/public/state/actions/snapshot.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts b/x-pack/plugins/uptime/public/state/actions/types.ts similarity index 94% rename from x-pack/legacy/plugins/uptime/public/state/actions/types.ts rename to x-pack/plugins/uptime/public/state/actions/types.ts index 41381afd31453f..dee2df77707d28 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/plugins/uptime/public/state/actions/types.ts @@ -5,7 +5,7 @@ */ import { Action } from 'redux-actions'; -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; export interface AsyncAction { get: (payload: Payload) => Action; diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/ui.ts b/x-pack/plugins/uptime/public/state/actions/ui.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/actions/ui.ts rename to x-pack/plugins/uptime/public/state/actions/ui.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts b/x-pack/plugins/uptime/public/state/actions/utils.ts similarity index 90% rename from x-pack/legacy/plugins/uptime/public/state/actions/utils.ts rename to x-pack/plugins/uptime/public/state/actions/utils.ts index a5adb96731f330..8ce4cf011406be 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/utils.ts +++ b/x-pack/plugins/uptime/public/state/actions/utils.ts @@ -6,7 +6,7 @@ import { createAction } from 'redux-actions'; import { AsyncAction, AsyncAction1 } from './types'; -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; export function createAsyncAction( actionStr: string diff --git a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap b/x-pack/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap rename to x-pack/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap diff --git a/x-pack/plugins/uptime/public/state/api/__tests__/ml_anomaly.test.ts b/x-pack/plugins/uptime/public/state/api/__tests__/ml_anomaly.test.ts new file mode 100644 index 00000000000000..838e5b8246b4b5 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/api/__tests__/ml_anomaly.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getMLJobId } from '../ml_anomaly'; + +describe('ML Anomaly API', () => { + it('it generates a lowercase job id', async () => { + const monitorId = 'ABC1334haa'; + + const jobId = getMLJobId(monitorId); + + expect(jobId).toEqual(jobId.toLowerCase()); + }); + + it('should truncate long monitor IDs', () => { + const longAndWeirdMonitorId = + 'https://auto-mmmmxhhhhhccclongAndWeirdMonitorId123yyyyyrereauto-xcmpa-1345555454646'; + + expect(getMLJobId(longAndWeirdMonitorId)).toHaveLength(64); + }); + + it('should remove special characters and replace them with underscore', () => { + const monIdSpecialChars = '/ ? , " < > | * a'; + + const jobId = getMLJobId(monIdSpecialChars); + + const format = /[/?,"<>|*]+/; + + expect(format.test(jobId)).toBe(false); + }); +}); diff --git a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts b/x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts rename to x-pack/plugins/uptime/public/state/api/__tests__/snapshot.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/dynamic_settings.ts b/x-pack/plugins/uptime/public/state/api/dynamic_settings.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/dynamic_settings.ts rename to x-pack/plugins/uptime/public/state/api/dynamic_settings.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index.ts b/x-pack/plugins/uptime/public/state/api/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/index.ts rename to x-pack/plugins/uptime/public/state/api/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts b/x-pack/plugins/uptime/public/state/api/index_pattern.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts rename to x-pack/plugins/uptime/public/state/api/index_pattern.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts b/x-pack/plugins/uptime/public/state/api/index_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/index_status.ts rename to x-pack/plugins/uptime/public/state/api/index_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts similarity index 71% rename from x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts rename to x-pack/plugins/uptime/public/state/api/ml_anomaly.ts index 16b90e99214284..c4ecb769abefce 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts @@ -8,17 +8,35 @@ import moment from 'moment'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; import { API_URLS, ML_JOB_ID, ML_MODULE_ID } from '../../../common/constants'; -import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; +import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/capabilities'; import { CreateMLJobSuccess, DeleteJobResults, MonitorIdParam, HeartbeatIndicesParam, } from '../actions/types'; -import { DataRecognizerConfigResponse } from '../../../../../../plugins/ml/common/types/modules'; -import { JobExistResult } from '../../../../../../plugins/ml/common/types/data_recognizer'; +import { DataRecognizerConfigResponse } from '../../../../../plugins/ml/common/types/modules'; +import { JobExistResult } from '../../../../../plugins/ml/common/types/data_recognizer'; -export const getMLJobId = (monitorId: string) => `${monitorId}_${ML_JOB_ID}`.toLowerCase(); +const getJobPrefix = (monitorId: string) => { + // ML App doesn't support upper case characters in job name + // Also Spaces and the characters / ? , " < > | * are not allowed + // so we will replace all special chars with _ + + const prefix = monitorId.replace(/[^A-Z0-9]+/gi, '_').toLowerCase(); + + // ML Job ID can't be greater than 64 length, so will be substring it, and hope + // At such big length, there is minimum chance of having duplicate monitor id + // Subtracting ML_JOB_ID constant as well + const postfix = '_' + ML_JOB_ID; + + if ((prefix + postfix).length > 64) { + return prefix.substring(0, 64 - postfix.length) + '_'; + } + return prefix + '_'; +}; + +export const getMLJobId = (monitorId: string) => `${getJobPrefix(monitorId)}${ML_JOB_ID}`; export const getMLCapabilities = async (): Promise => { return await apiService.get(API_URLS.ML_CAPABILITIES); @@ -34,11 +52,8 @@ export const createMLJob = async ({ }: MonitorIdParam & HeartbeatIndicesParam): Promise => { const url = API_URLS.ML_SETUP_MODULE + ML_MODULE_ID; - // ML App doesn't support upper case characters in job name - const lowerCaseMonitorId = monitorId.toLowerCase(); - const data = { - prefix: `${lowerCaseMonitorId}_`, + prefix: `${getJobPrefix(monitorId)}`, useDedicatedIndex: false, startDatafeed: true, start: moment() @@ -48,7 +63,7 @@ export const createMLJob = async ({ query: { bool: { filter: [ - { term: { 'monitor.id': lowerCaseMonitorId } }, + { term: { 'monitor.id': monitorId } }, { range: { 'monitor.duration.us': { gt: 0 } } }, ], }, diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts b/x-pack/plugins/uptime/public/state/api/monitor.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/monitor.ts rename to x-pack/plugins/uptime/public/state/api/monitor.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts b/x-pack/plugins/uptime/public/state/api/monitor_duration.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts rename to x-pack/plugins/uptime/public/state/api/monitor_duration.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_list.ts b/x-pack/plugins/uptime/public/state/api/monitor_list.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/monitor_list.ts rename to x-pack/plugins/uptime/public/state/api/monitor_list.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts b/x-pack/plugins/uptime/public/state/api/monitor_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts rename to x-pack/plugins/uptime/public/state/api/monitor_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts b/x-pack/plugins/uptime/public/state/api/overview_filters.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts rename to x-pack/plugins/uptime/public/state/api/overview_filters.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/plugins/uptime/public/state/api/ping.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/ping.ts rename to x-pack/plugins/uptime/public/state/api/ping.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts b/x-pack/plugins/uptime/public/state/api/snapshot.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts rename to x-pack/plugins/uptime/public/state/api/snapshot.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/types.ts b/x-pack/plugins/uptime/public/state/api/types.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/api/types.ts rename to x-pack/plugins/uptime/public/state/api/types.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts similarity index 95% rename from x-pack/legacy/plugins/uptime/public/state/api/utils.ts rename to x-pack/plugins/uptime/public/state/api/utils.ts index e67efa8570c110..acd9bec5a74bcb 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/utils.ts +++ b/x-pack/plugins/uptime/public/state/api/utils.ts @@ -6,7 +6,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import { isRight } from 'fp-ts/lib/Either'; -import { HttpFetchQuery, HttpSetup } from '../../../../../../../target/types/core/public'; +import { HttpFetchQuery, HttpSetup } from '../../../../../../target/types/core/public'; class ApiService { private static instance: ApiService; diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/__tests__/fetch_effect.test.ts b/x-pack/plugins/uptime/public/state/effects/__tests__/fetch_effect.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/__tests__/fetch_effect.test.ts rename to x-pack/plugins/uptime/public/state/effects/__tests__/fetch_effect.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/dynamic_settings.ts b/x-pack/plugins/uptime/public/state/effects/dynamic_settings.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/dynamic_settings.ts rename to x-pack/plugins/uptime/public/state/effects/dynamic_settings.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts similarity index 94% rename from x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts rename to x-pack/plugins/uptime/public/state/effects/fetch_effect.ts index b0734cb5ccabbc..0aa85609fe4f06 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/plugins/uptime/public/state/effects/fetch_effect.ts @@ -6,7 +6,7 @@ import { call, put } from 'redux-saga/effects'; import { Action } from 'redux-actions'; -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; /** * Factory function for a fetch effect. It expects three action creators, diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index.ts b/x-pack/plugins/uptime/public/state/effects/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/index.ts rename to x-pack/plugins/uptime/public/state/effects/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index_pattern.ts b/x-pack/plugins/uptime/public/state/effects/index_pattern.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/index_pattern.ts rename to x-pack/plugins/uptime/public/state/effects/index_pattern.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts b/x-pack/plugins/uptime/public/state/effects/index_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/index_status.ts rename to x-pack/plugins/uptime/public/state/effects/index_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/ml_anomaly.ts rename to x-pack/plugins/uptime/public/state/effects/ml_anomaly.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts b/x-pack/plugins/uptime/public/state/effects/monitor.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts rename to x-pack/plugins/uptime/public/state/effects/monitor.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_duration.ts b/x-pack/plugins/uptime/public/state/effects/monitor_duration.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/monitor_duration.ts rename to x-pack/plugins/uptime/public/state/effects/monitor_duration.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_list.ts b/x-pack/plugins/uptime/public/state/effects/monitor_list.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/monitor_list.ts rename to x-pack/plugins/uptime/public/state/effects/monitor_list.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts b/x-pack/plugins/uptime/public/state/effects/monitor_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts rename to x-pack/plugins/uptime/public/state/effects/monitor_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/overview_filters.ts b/x-pack/plugins/uptime/public/state/effects/overview_filters.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/overview_filters.ts rename to x-pack/plugins/uptime/public/state/effects/overview_filters.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/ping.ts b/x-pack/plugins/uptime/public/state/effects/ping.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/ping.ts rename to x-pack/plugins/uptime/public/state/effects/ping.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts b/x-pack/plugins/uptime/public/state/effects/snapshot.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts rename to x-pack/plugins/uptime/public/state/effects/snapshot.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/index.ts b/x-pack/plugins/uptime/public/state/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/index.ts rename to x-pack/plugins/uptime/public/state/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts b/x-pack/plugins/uptime/public/state/kibana_service.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/kibana_service.ts rename to x-pack/plugins/uptime/public/state/kibana_service.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/__snapshots__/snapshot.test.ts.snap b/x-pack/plugins/uptime/public/state/reducers/__tests__/__snapshots__/snapshot.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/__snapshots__/snapshot.test.ts.snap rename to x-pack/plugins/uptime/public/state/reducers/__tests__/__snapshots__/snapshot.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/__snapshots__/ui.test.ts.snap b/x-pack/plugins/uptime/public/state/reducers/__tests__/__snapshots__/ui.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/__snapshots__/ui.test.ts.snap rename to x-pack/plugins/uptime/public/state/reducers/__tests__/__snapshots__/ui.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts b/x-pack/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts rename to x-pack/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/ui.test.ts b/x-pack/plugins/uptime/public/state/reducers/__tests__/ui.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/ui.test.ts rename to x-pack/plugins/uptime/public/state/reducers/__tests__/ui.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/dynamic_settings.ts b/x-pack/plugins/uptime/public/state/reducers/dynamic_settings.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/dynamic_settings.ts rename to x-pack/plugins/uptime/public/state/reducers/dynamic_settings.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index.ts b/x-pack/plugins/uptime/public/state/reducers/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/index.ts rename to x-pack/plugins/uptime/public/state/reducers/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts b/x-pack/plugins/uptime/public/state/reducers/index_pattern.ts similarity index 92% rename from x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts rename to x-pack/plugins/uptime/public/state/reducers/index_pattern.ts index bc482e2f35c451..b357f5a904ea63 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/index_pattern.ts +++ b/x-pack/plugins/uptime/public/state/reducers/index_pattern.ts @@ -5,7 +5,7 @@ */ import { handleActions, Action } from 'redux-actions'; import { getIndexPattern, getIndexPatternSuccess, getIndexPatternFail } from '../actions'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; +import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; export interface IndexPatternState { index_pattern: IIndexPattern | null; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts b/x-pack/plugins/uptime/public/state/reducers/index_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/index_status.ts rename to x-pack/plugins/uptime/public/state/reducers/index_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts similarity index 88% rename from x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts rename to x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts index df5e825c3488b9..61e03a95929211 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts @@ -15,11 +15,11 @@ import { getMLCapabilitiesAction, } from '../actions'; import { getAsyncInitialState, handleAsyncAction } from './utils'; -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; import { AsyncInitialState } from './types'; -import { MlCapabilitiesResponse } from '../../../../../../plugins/ml/common/types/capabilities'; +import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/capabilities'; import { CreateMLJobSuccess, DeleteJobResults } from '../actions/types'; -import { JobExistResult } from '../../../../../../plugins/ml/common/types/data_recognizer'; +import { JobExistResult } from '../../../../../plugins/ml/common/types/data_recognizer'; export interface MLJobState { mlJob: AsyncInitialState; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts b/x-pack/plugins/uptime/public/state/reducers/monitor.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts rename to x-pack/plugins/uptime/public/state/reducers/monitor.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_duration.ts b/x-pack/plugins/uptime/public/state/reducers/monitor_duration.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/monitor_duration.ts rename to x-pack/plugins/uptime/public/state/reducers/monitor_duration.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_list.ts b/x-pack/plugins/uptime/public/state/reducers/monitor_list.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/monitor_list.ts rename to x-pack/plugins/uptime/public/state/reducers/monitor_list.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts b/x-pack/plugins/uptime/public/state/reducers/monitor_status.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts rename to x-pack/plugins/uptime/public/state/reducers/monitor_status.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts b/x-pack/plugins/uptime/public/state/reducers/overview_filters.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts rename to x-pack/plugins/uptime/public/state/reducers/overview_filters.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ping.ts b/x-pack/plugins/uptime/public/state/reducers/ping.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/ping.ts rename to x-pack/plugins/uptime/public/state/reducers/ping.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ping_list.ts b/x-pack/plugins/uptime/public/state/reducers/ping_list.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/ping_list.ts rename to x-pack/plugins/uptime/public/state/reducers/ping_list.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts b/x-pack/plugins/uptime/public/state/reducers/snapshot.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts rename to x-pack/plugins/uptime/public/state/reducers/snapshot.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts b/x-pack/plugins/uptime/public/state/reducers/types.ts similarity index 81% rename from x-pack/legacy/plugins/uptime/public/state/reducers/types.ts rename to x-pack/plugins/uptime/public/state/reducers/types.ts index 88995a2f5dd70c..c81ee6875f305a 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/types.ts +++ b/x-pack/plugins/uptime/public/state/reducers/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IHttpFetchError } from '../../../../../../../target/types/core/public/http'; +import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; export interface AsyncInitialState { data: ReduceStateType | null; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts b/x-pack/plugins/uptime/public/state/reducers/ui.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts rename to x-pack/plugins/uptime/public/state/reducers/ui.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts b/x-pack/plugins/uptime/public/state/reducers/utils.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/reducers/utils.ts rename to x-pack/plugins/uptime/public/state/reducers/utils.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts rename to x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/state/selectors/index.ts rename to x-pack/plugins/uptime/public/state/selectors/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/plugins/uptime/public/uptime_app.tsx similarity index 75% rename from x-pack/legacy/plugins/uptime/public/uptime_app.tsx rename to x-pack/plugins/uptime/public/uptime_app.tsx index 92775a26638633..0d18f959230d11 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/uptime_app.tsx @@ -10,13 +10,14 @@ import React, { useEffect } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; import { BrowserRouter as Router } from 'react-router-dom'; import { I18nStart, ChromeBreadcrumb, CoreStart } from 'src/core/public'; -import { PluginsSetup } from 'ui/new_platform/new_platform'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; +import { ClientPluginsSetup, ClientPluginsStart } from './apps/plugin'; import { UMUpdateBadge } from './lib/lib'; import { UptimeRefreshContextProvider, UptimeSettingsContextProvider, UptimeThemeContextProvider, + UptimeStartupPluginsContextProvider, } from './contexts'; import { CommonlyUsedRange } from './components/common/uptime_date_picker'; import { store } from './state'; @@ -47,7 +48,8 @@ export interface UptimeAppProps { isInfraAvailable: boolean; isLogsAvailable: boolean; kibanaBreadcrumbs: ChromeBreadcrumb[]; - plugins: PluginsSetup; + plugins: ClientPluginsSetup; + startPlugins: ClientPluginsStart; routerBasename: string; setBadge: UMUpdateBadge; renderGlobalHelpControls(): void; @@ -66,6 +68,7 @@ const Application = (props: UptimeAppProps) => { renderGlobalHelpControls, routerBasename, setBadge, + startPlugins, } = props; useEffect(() => { @@ -87,7 +90,6 @@ const Application = (props: UptimeAppProps) => { kibanaService.core = core; - // @ts-ignore store.dispatch(setBasePath(basePath)); return ( @@ -99,17 +101,19 @@ const Application = (props: UptimeAppProps) => { - - -
- - -
-
-
+ + + +
+ + +
+
+
+
diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts index 725b53aeca02db..d68bbabe82b861 100644 --- a/x-pack/plugins/uptime/server/kibana.index.ts +++ b/x-pack/plugins/uptime/server/kibana.index.ts @@ -5,7 +5,7 @@ */ import { Request, Server } from 'hapi'; -import { PLUGIN } from '../../../legacy/plugins/uptime/common/constants'; +import { PLUGIN } from '../common/constants'; import { compose } from './lib/compose/kibana'; import { initUptimeServer } from './uptime_server'; import { UptimeCorePlugins, UptimeCoreSetup } from './lib/adapters/framework'; diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts index 98c6be5aa3c8e8..f4d1c72770494d 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/adapter_types.ts @@ -13,7 +13,7 @@ import { } from 'src/core/server'; import { UMKibanaRoute } from '../../../rest_api'; import { PluginSetupContract } from '../../../../../features/server'; -import { DynamicSettings } from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { DynamicSettings } from '../../../../common/runtime_types'; export type APICaller = ( endpoint: string, @@ -31,7 +31,7 @@ export type UMSavedObjectsQueryFn = ( ) => Promise | T; export interface UptimeCoreSetup { - route: IRouter; + router: IRouter; } export interface UptimeCorePlugins { diff --git a/x-pack/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts index 0176471aec1be2..46f46720d4c04a 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -21,10 +21,10 @@ export class UMKibanaBackendFrameworkAdapter implements UMBackendFrameworkAdapte }; switch (method) { case 'GET': - this.server.route.get(routeDefinition, handler); + this.server.router.get(routeDefinition, handler); break; case 'POST': - this.server.route.post(routeDefinition, handler); + this.server.router.post(routeDefinition, handler); break; default: throw new Error(`Handler for method ${method} is not defined`); diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 4f4c6e3011ad14..24da3f3fa4d067 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -16,7 +16,7 @@ import { AlertType } from '../../../../../alerting/server'; import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCoreSetup } from '../../adapters'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mocks'; /** @@ -27,10 +27,10 @@ import { alertsMock, AlertServicesMock } from '../../../../../alerting/server/mo * so we don't have to mock them all for each test. */ const bootstrapDependencies = (customRequests?: any) => { - const route: IRouter = {} as IRouter; + const router: IRouter = {} as IRouter; // these server/libs parameters don't have any functionality, which is fine // because we aren't testing them here - const server: UptimeCoreSetup = { route }; + const server: UptimeCoreSetup = { router }; const libs: UMServerLibs = { requests: {} } as UMServerLibs; libs.requests = { ...libs.requests, ...customRequests }; return { server, libs }; diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 829e6f92d37020..f9df559a3977ba 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -9,14 +9,14 @@ import { isRight } from 'fp-ts/lib/Either'; import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; import { i18n } from '@kbn/i18n'; import { AlertExecutorOptions } from '../../../../alerting/server'; -import { ACTION_GROUP_DEFINITIONS } from '../../../../../legacy/plugins/uptime/common/constants'; import { UptimeAlertTypeFactory } from './types'; import { GetMonitorStatusResult } from '../requests'; import { StatusCheckExecutorParamsType, StatusCheckAlertStateType, StatusCheckAlertState, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; +} from '../../../common/runtime_types'; +import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants'; import { savedObjectsAdapter } from '../saved_objects'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; diff --git a/x-pack/plugins/uptime/server/lib/helper/get_histogram_interval.ts b/x-pack/plugins/uptime/server/lib/helper/get_histogram_interval.ts index fb44f5727aab38..26515fb4b4c63b 100644 --- a/x-pack/plugins/uptime/server/lib/helper/get_histogram_interval.ts +++ b/x-pack/plugins/uptime/server/lib/helper/get_histogram_interval.ts @@ -5,7 +5,7 @@ */ import DateMath from '@elastic/datemath'; -import { QUERY } from '../../../../../legacy/plugins/uptime/common/constants'; +import { QUERY } from '../../../common/constants'; export const parseRelativeDate = (dateStr: string, options = {}) => { // We need this this parsing because if user selects This week or this date diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts index b49a6b22ff9768..894e2316dc927f 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts @@ -5,7 +5,7 @@ */ import { getCerts } from '../get_certs'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; describe('getCerts', () => { let mockHits: any; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index 03e2bc7a44bd04..75bf5096bd9978 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -5,7 +5,7 @@ */ import { getLatestMonitor } from '../get_latest_monitor'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; describe('getLatestMonitor', () => { let expectedGetLatestSearchParams: any; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts index 5d3f9ce8d4ad9d..45be1df3e8d3bf 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_charts.test.ts @@ -7,7 +7,7 @@ import { set } from 'lodash'; import mockChartsData from './monitor_charts_mock.json'; import { getMonitorDurationChart } from '../get_monitor_duration'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; describe('ElasticsearchMonitorsAdapter', () => { it('getMonitorChartsData will provide expected filters', async () => { diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts index e47be617d7c994..82e624221c301c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_monitor_status.test.ts @@ -6,8 +6,8 @@ import { elasticsearchServiceMock } from '../../../../../../../src/core/server/mocks'; import { getMonitorStatus } from '../get_monitor_status'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; import { ScopedClusterClient } from 'src/core/server'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; interface BucketItemCriteria { monitor_id: string; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts index 4de7d3ffd2a7d5..e456670a5e68de 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_ping_histogram.test.ts @@ -5,7 +5,7 @@ */ import { getPingHistogram } from '../get_ping_histogram'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; describe('getPingHistogram', () => { const standardMockResponse: any = { diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts index abd3655cc64028..fd890a30cf7428 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts @@ -6,7 +6,7 @@ import { getPings } from '../get_pings'; import { set } from 'lodash'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../common/constants'; describe('getAll', () => { let mockEsSearchResult: any; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 4f99fbe94d54c7..b427e7cae1a7e8 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { Cert, GetCertsParams } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { Cert, GetCertsParams } from '../../../common/runtime_types'; export const getCerts: UMElasticsearchQueryFn = async ({ callES, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts b/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts index 95d23ddcbf466e..dbe71cf689214f 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_filter_bar.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { OverviewFilters } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { OverviewFilters } from '../../../common/runtime_types'; import { generateFilterAggs } from './generate_filter_aggs'; export interface GetFilterBarParams { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts index 6f7854d35b308b..7688f04f1acd95 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_index_status.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { StatesIndexStatus } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { StatesIndexStatus } from '../../../common/runtime_types'; export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = async ({ callES, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index a8e9ccb875a08c..98ce449002f217 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -5,7 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { Ping } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { Ping } from '../../../common/runtime_types'; export interface GetLatestMonitorParams { /** @member dateRangeStart timestamp bounds */ diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts index 4ce7176b57b199..cf4ffa339ddfc5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_details.ts @@ -5,10 +5,7 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { - MonitorDetails, - MonitorError, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { MonitorDetails, MonitorError } from '../../../common/runtime_types'; export interface GetMonitorDetailsParams { monitorId: string; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts index e9c745b0a87133..ea2a7e790652bd 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_duration.ts @@ -5,11 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { QUERY } from '../../../../../legacy/plugins/uptime/common/constants'; -import { - LocationDurationLine, - MonitorDurationResult, -} from '../../../../../legacy/plugins/uptime/common/types'; +import { LocationDurationLine, MonitorDurationResult } from '../../../common/types'; +import { QUERY } from '../../../common/constants'; export interface GetMonitorChartsParams { /** @member monitorId ID value for the selected monitor */ diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts index f49e404ffb084b..c8d3ca043edc50 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_locations.ts @@ -5,11 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { UNNAMED_LOCATION } from '../../../../../legacy/plugins/uptime/common/constants'; -import { - MonitorLocations, - MonitorLocation, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { MonitorLocations, MonitorLocation } from '../../../common/runtime_types'; +import { UNNAMED_LOCATION } from '../../../common/constants'; /** * Fetch data for the monitor page title. diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts index 4b40943a857051..b1791dd04861c4 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_states.ts @@ -4,15 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { CONTEXT_DEFAULTS } from '../../../common/constants'; import { fetchPage } from './search'; import { UMElasticsearchQueryFn } from '../adapters'; -import { - SortOrder, - CursorDirection, - MonitorSummary, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; - +import { MonitorSummary, SortOrder, CursorDirection } from '../../../common/runtime_types'; import { QueryContext } from './search'; export interface CursorPagination { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts index 5a8927764ea5c2..299913c8dff086 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_ping_histogram.ts @@ -5,12 +5,9 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { QUERY } from '../../../../../legacy/plugins/uptime/common/constants'; import { getFilterClause } from '../helper'; -import { - HistogramResult, - HistogramQueryResult, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { HistogramResult, HistogramQueryResult } from '../../../common/runtime_types'; +import { QUERY } from '../../../common/constants'; export interface GetPingHistogramParams { /** @member dateRangeStart timestamp bounds */ diff --git a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts index 6eccfdb13cef74..a6a0e3c3d6542e 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts @@ -10,7 +10,7 @@ import { HttpResponseBody, PingsResponse, Ping, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; +} from '../../../common/runtime_types'; const DEFAULT_PAGE_SIZE = 25; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts index 01f2ad88161cf0..b57bc87d454183 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_snapshot_counts.ts @@ -5,8 +5,8 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { Snapshot } from '../../../../../legacy/plugins/uptime/common/runtime_types'; -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { CONTEXT_DEFAULTS } from '../../../common/constants'; +import { Snapshot } from '../../../common/runtime_types'; import { QueryContext } from './search'; export interface GetSnapshotCountParams { diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/fetch_page.test.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/fetch_page.test.ts index 2a8f681ab34536..d4ad80c85ec3d5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/fetch_page.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/fetch_page.test.ts @@ -12,7 +12,7 @@ import { MonitorGroupsPage, } from '../fetch_page'; import { QueryContext } from '../query_context'; -import { MonitorSummary } from '../../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { MonitorSummary } from '../../../../../common/runtime_types'; import { nextPagination, prevPagination, simpleQueryContext } from './test_helpers'; const simpleFixture: MonitorGroups[] = [ diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts index 84774cdeed8567..e53fff429dd8df 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/query_context.test.ts @@ -6,10 +6,7 @@ import { QueryContext } from '../query_context'; import { CursorPagination } from '../types'; -import { - CursorDirection, - SortOrder, -} from '../../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection, SortOrder } from '../../../../../common/runtime_types'; describe(QueryContext, () => { // 10 minute range diff --git a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts index 47034c21301164..40775bde1c7f5a 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/__tests__/test_helpers.ts @@ -5,10 +5,7 @@ */ import { CursorPagination } from '../types'; -import { - CursorDirection, - SortOrder, -} from '../../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection, SortOrder } from '../../../../../common/runtime_types'; import { QueryContext } from '../query_context'; export const prevPagination = (key: any): CursorPagination => { diff --git a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts index 4739c804d24e75..d21259fad77a66 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts @@ -6,14 +6,14 @@ import { get, sortBy } from 'lodash'; import { QueryContext } from './query_context'; -import { QUERY, STATES } from '../../../../../../legacy/plugins/uptime/common/constants'; +import { QUERY, STATES } from '../../../../common/constants'; import { Check, Histogram, MonitorSummary, CursorDirection, SortOrder, -} from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +} from '../../../../common/runtime_types'; import { MonitorEnricher } from './fetch_page'; export const enrichMonitorGroups: MonitorEnricher = async ( diff --git a/x-pack/plugins/uptime/server/lib/requests/search/fetch_page.ts b/x-pack/plugins/uptime/server/lib/requests/search/fetch_page.ts index 84167840d5d9ba..bef8106ad1896c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/fetch_page.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/fetch_page.ts @@ -7,12 +7,8 @@ import { flatten } from 'lodash'; import { CursorPagination } from './types'; import { QueryContext } from './query_context'; -import { QUERY } from '../../../../../../legacy/plugins/uptime/common/constants'; -import { - CursorDirection, - MonitorSummary, - SortOrder, -} from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { QUERY } from '../../../../common/constants'; +import { CursorDirection, MonitorSummary, SortOrder } from '../../../../common/runtime_types'; import { enrichMonitorGroups } from './enrich_monitor_groups'; import { MonitorGroupIterator } from './monitor_group_iterator'; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts index 3449febfa5b053..e60c52660915a1 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/find_potential_matches.ts @@ -5,7 +5,7 @@ */ import { get, set } from 'lodash'; -import { CursorDirection } from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection } from '../../../../common/runtime_types'; import { QueryContext } from './query_context'; // This is the first phase of the query. In it, we find the most recent check groups that matched the given query. diff --git a/x-pack/plugins/uptime/server/lib/requests/search/monitor_group_iterator.ts b/x-pack/plugins/uptime/server/lib/requests/search/monitor_group_iterator.ts index 31d9166eb1e73b..2fb95620282583 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/monitor_group_iterator.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/monitor_group_iterator.ts @@ -6,7 +6,7 @@ import { QueryContext } from './query_context'; import { fetchChunk } from './fetch_chunk'; -import { CursorDirection } from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection } from '../../../../common/runtime_types'; import { MonitorGroups } from './fetch_page'; import { CursorPagination } from './types'; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts index 43fc54fb258089..977c32ad1f984b 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts @@ -5,7 +5,7 @@ */ import { QueryContext } from './query_context'; -import { CursorDirection } from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection } from '../../../../common/runtime_types'; import { MonitorGroups, MonitorLocCheckGroup } from './fetch_page'; /** diff --git a/x-pack/plugins/uptime/server/lib/requests/search/types.ts b/x-pack/plugins/uptime/server/lib/requests/search/types.ts index 2ec52d400b5972..35e9647196454e 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/types.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/types.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - CursorDirection, - SortOrder, -} from '../../../../../../legacy/plugins/uptime/common/runtime_types'; +import { CursorDirection, SortOrder } from '../../../../common/runtime_types'; export interface CursorPagination { cursorKey?: any; diff --git a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts index 84154429b91888..69507d2950cd84 100644 --- a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -8,10 +8,17 @@ import { UMElasticsearchQueryFn } from '../adapters'; import { HistogramResult, Ping, - PingsResponse as PingResults, + PingsResponse, GetCertsParams, GetPingsParams, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; + Cert, + OverviewFilters, + MonitorDetails, + MonitorLocations, + Snapshot, + StatesIndexStatus, +} from '../../../common/runtime_types'; +import { MonitorDurationResult } from '../../../common/types'; import { GetFilterBarParams, GetLatestMonitorParams, @@ -23,17 +30,8 @@ import { GetMonitorStatusParams, GetMonitorStatusResult, } from '.'; -import { - OverviewFilters, - MonitorDetails, - MonitorLocations, - Snapshot, - StatesIndexStatus, - Cert, -} from '../../../../../legacy/plugins/uptime/common/runtime_types'; import { GetMonitorStatesResult } from './get_monitor_states'; import { GetSnapshotCountParams } from './get_snapshot_counts'; -import { MonitorDurationResult } from '../../../../../legacy/plugins/uptime/common/types'; type ESQ = UMElasticsearchQueryFn; @@ -47,7 +45,7 @@ export interface UptimeRequests { getMonitorLocations: ESQ; getMonitorStates: ESQ; getMonitorStatus: ESQ; - getPings: ESQ; + getPings: ESQ; getPingHistogram: ESQ; getSnapshotCount: ESQ; getIndexStatus: ESQ<{}, StatesIndexStatus>; diff --git a/x-pack/plugins/uptime/server/lib/saved_objects.ts b/x-pack/plugins/uptime/server/lib/saved_objects.ts index d849fbd8ce0a8f..28b9eaad2cf6f0 100644 --- a/x-pack/plugins/uptime/server/lib/saved_objects.ts +++ b/x-pack/plugins/uptime/server/lib/saved_objects.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DynamicSettings } from '../../../../legacy/plugins/uptime/common/runtime_types'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../legacy/plugins/uptime/common/constants'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../common/constants'; +import { DynamicSettings } from '../../common/runtime_types'; import { SavedObjectsType, SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { UMSavedObjectsQueryFn } from './adapters'; diff --git a/x-pack/plugins/uptime/server/plugin.ts b/x-pack/plugins/uptime/server/plugin.ts index 7cc591a6b2db17..13d1ae216f2041 100644 --- a/x-pack/plugins/uptime/server/plugin.ts +++ b/x-pack/plugins/uptime/server/plugin.ts @@ -8,19 +8,20 @@ import { PluginInitializerContext, CoreStart, CoreSetup, + Plugin as PluginType, ISavedObjectsRepository, } from '../../../../src/core/server'; import { initServerWithKibana } from './kibana.index'; import { KibanaTelemetryAdapter, UptimeCorePlugins } from './lib/adapters'; import { umDynamicSettings } from './lib/saved_objects'; -export class Plugin { +export class Plugin implements PluginType { private savedObjectsClient?: ISavedObjectsRepository; constructor(_initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup, plugins: UptimeCorePlugins) { - initServerWithKibana({ route: core.http.createRouter() }, plugins); + initServerWithKibana({ router: core.http.createRouter() }, plugins); core.savedObjects.registerType(umDynamicSettings); KibanaTelemetryAdapter.registerUsageCollector( plugins.usageCollection, @@ -28,7 +29,9 @@ export class Plugin { ); } - public start(_core: CoreStart, _plugins: any) { - this.savedObjectsClient = _core.savedObjects.createInternalRepository(); + public start(core: CoreStart, _plugins: any) { + this.savedObjectsClient = core.savedObjects.createInternalRepository(); } + + public stop() {} } diff --git a/x-pack/plugins/uptime/server/rest_api/certs.ts b/x-pack/plugins/uptime/server/rest_api/certs.ts index 31fb3f4ab96a78..f2e1700b23e7d8 100644 --- a/x-pack/plugins/uptime/server/rest_api/certs.ts +++ b/x-pack/plugins/uptime/server/rest_api/certs.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../lib/lib'; import { UMRestApiRouteFactory } from '.'; -import { API_URLS } from '../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../common/constants'; const DEFAULT_INDEX = 0; const DEFAULT_SIZE = 25; diff --git a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts index 3f4e2fc345182c..31833a25ee8acf 100644 --- a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts @@ -8,10 +8,7 @@ import { schema } from '@kbn/config-schema'; import { isRight } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { UMServerLibs } from '../lib/lib'; -import { - DynamicSettings, - DynamicSettingsType, -} from '../../../../legacy/plugins/uptime/common/runtime_types'; +import { DynamicSettings, DynamicSettingsType } from '../../common/runtime_types'; import { UMRestApiRouteFactory } from '.'; import { savedObjectsAdapter } from '../lib/saved_objects'; diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts index 689a75c5903a64..26715f0ff37b64 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts @@ -6,7 +6,7 @@ import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts index 8ed73d90b2389e..9a4280efa98f92 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -6,7 +6,7 @@ import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../common/constants'; export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts index 5cb4e8a6241b74..60b3eafaa765e6 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts @@ -6,8 +6,7 @@ import { schema } from '@kbn/config-schema'; import { UMRestApiRouteFactory } from '../types'; -import { CONTEXT_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS, CONTEXT_DEFAULTS } from '../../../common/constants'; export const createMonitorListRoute: UMRestApiRouteFactory = libs => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts index 66ce9871506d4e..a110209043a7e7 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts index 9cf1340fb94090..bb002f8a8c286c 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../common/constants'; export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts index 1cc010781457e2..69e719efb0719d 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts index 9743ced13350a4..34313211061b08 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const createGetMonitorDurationRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts index deac05f36c8dc9..00cbaf0d16723c 100644 --- a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts +++ b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; import { objectValuesToArrays } from '../../lib/helper'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; const arrayOrStringType = schema.maybe( schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts index dceef5ecb78486..41078f735920b8 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts index 80a887a7f64a9d..d97195a7fe2b17 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts @@ -9,8 +9,8 @@ import { isLeft } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; -import { GetPingsParamsType } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { API_URLS } from '../../../common/constants'; +import { GetPingsParamsType } from '../../../common/runtime_types'; export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts index d870f492801171..7809e102a499f8 100644 --- a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts +++ b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../common/constants'; export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts index 4b2db71037071e..d8387e79e9089b 100644 --- a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts +++ b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { KibanaTelemetryAdapter } from '../../lib/adapters/telemetry'; import { UMRestApiRouteFactory } from '../types'; import { PageViewParams } from '../../lib/adapters/telemetry/types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../common/constants'; export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ method: 'POST', diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index e05e7a4d7faf1a..8720b9dc60b12a 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -15,8 +15,8 @@ import { KibanaRequest, KibanaResponseFactory, IKibanaResponse, -} from 'src/core/server'; -import { DynamicSettings } from '../../../../legacy/plugins/uptime/common/runtime_types'; +} from 'kibana/server'; +import { DynamicSettings } from '../../common/runtime_types'; import { UMServerLibs } from '../lib/lib'; /** diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts index a7551ad7e2fadd..1244657ed99889 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/servicenow.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // node ../scripts/functional_test_runner.js --grep "Actions.servicenddd" --config=test/alerting_api_integration/security_and_spaces/config.ts diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts index 46258e41d5d69d..4151deab45213d 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/slack.ts @@ -9,7 +9,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts index 338610e9243a40..bae6dada48fb7e 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/webhook.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 457b7621e84bd6..870ed3cf0cc0fc 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -8,7 +8,7 @@ import path from 'path'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; -import { getAllExternalServiceSimulatorPaths } from './fixtures/plugins/actions'; +import { getAllExternalServiceSimulatorPaths } from './fixtures/plugins/actions_simulators'; interface CreateTestConfigOptions { license: string; @@ -75,7 +75,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) 'some.non.existent.com', ])}`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, - '--xpack.alerting.enabled=true', '--xpack.eventLog.logEntries=true', `--xpack.actions.preconfigured=${JSON.stringify([ { @@ -124,7 +123,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) ])}`, ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, + `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions_simulators')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'task_manager')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'aad')}`, `--server.xsrf.whitelist=${JSON.stringify(getAllExternalServiceSimulatorPaths())}`, diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts index 05139213b76b9c..400aec7e11c8de 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/aad/index.ts @@ -21,7 +21,7 @@ interface CheckAADRequest extends Hapi.Request { // eslint-disable-next-line import/no-default-export export default function(kibana: any) { return new kibana.Plugin({ - require: ['actions', 'alerting', 'encryptedSavedObjects'], + require: ['encryptedSavedObjects'], name: 'aad-fixtures', init(server: Legacy.Server) { const newPlatform = ((server as unknown) as KbnServer).newPlatform; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/README.md b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/README.md similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/README.md rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/README.md diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts similarity index 98% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/index.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts index 019b15cc1862a4..45edd4c092da97 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/index.ts @@ -36,7 +36,7 @@ export function getAllExternalServiceSimulatorPaths(): string[] { // eslint-disable-next-line import/no-default-export export default function(kibana: any) { return new kibana.Plugin({ - require: ['xpack_main', 'actions'], + require: ['xpack_main'], name: NAME, init: (server: Hapi.Server) => { // this action is specifically NOT enabled in ../../config.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/package.json b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/package.json rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/package.json diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/pagerduty_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/pagerduty_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/pagerduty_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/servicenow_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/servicenow_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/slack_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/slack_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/slack_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/slack_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/webhook_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/webhook_simulation.ts similarity index 100% rename from x-pack/test/alerting_api_integration/common/fixtures/plugins/actions/webhook_simulation.ts rename to x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/webhook_simulation.ts diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts index 43d533ad3ae14d..1a47addf36ab33 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/index.ts @@ -11,8 +11,8 @@ import { ActionTypeExecutorOptions, ActionType } from '../../../../../../plugins // eslint-disable-next-line import/no-default-export export default function(kibana: any) { return new kibana.Plugin({ - require: ['xpack_main', 'actions', 'alerting', 'elasticsearch'], - name: 'alerts', + require: ['xpack_main', 'elasticsearch'], + name: 'alerts-fixture', init(server: any) { const clusterClient = server.newPlatform.start.core.elasticsearch.legacy.client; server.plugins.xpack_main.registerFeature({ diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts index 29708f86b0a9b0..ac32f05805e4a3 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/task_manager/index.ts @@ -31,7 +31,7 @@ const taskByIdQuery = (id: string) => ({ export default function(kibana: any) { return new kibana.Plugin({ name: 'taskManagerHelpers', - require: ['elasticsearch', 'task_manager'], + require: ['elasticsearch'], config(Joi: any) { return Joi.object({ diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts index eeb0818b5fbabd..4c76ebfb93b0bd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // eslint-disable-next-line import/no-default-export export default function pagerdutyTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts index 054f8f6141817e..399ae0f27f5b1a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // node ../scripts/functional_test_runner.js --grep "servicenow" --config=test/alerting_api_integration/security_and_spaces/config.ts diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts index e00589b7e85b7b..386254e49c19c8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts @@ -11,7 +11,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // eslint-disable-next-line import/no-default-export export default function slackTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts index fd996ea4507bab..9b66326fa6157a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; const defaultValues: Record = { headers: null, diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts index 5122a74d53b725..112149a32649a2 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/webhook.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getExternalServiceSimulatorPath, ExternalServiceSimulator, -} from '../../../../common/fixtures/plugins/actions'; +} from '../../../../common/fixtures/plugins/actions_simulators'; // eslint-disable-next-line import/no-default-export export default function webhookTest({ getService }: FtrProviderContext) { diff --git a/x-pack/test/api_integration/apis/endpoint/resolver.ts b/x-pack/test/api_integration/apis/endpoint/resolver.ts index e8d336e875b991..73fe435764b74c 100644 --- a/x-pack/test/api_integration/apis/endpoint/resolver.ts +++ b/x-pack/test/api_integration/apis/endpoint/resolver.ts @@ -27,72 +27,65 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC it('should return details for the root node', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}`) + .get(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}`) .set(commonHeaders) .expect(200); expect(body.events.length).to.eql(1); - expect(body.pagination.next).to.eql(cursor); - expect(body.pagination.total).to.eql(1); - // default limit - expect(body.pagination.limit).to.eql(100); + expect(body.pagination.nextEvent).to.eql(null); }); it('returns no values when there is no more data', async () => { const { body } = await supertest // after is set to the document id of the last event so there shouldn't be any more after it .get( - `/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}&after=${cursor}` + `/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=${cursor}` ) .set(commonHeaders) .expect(200); expect(body.events).be.empty(); - expect(body.pagination.next).to.eql(null); - expect(body.pagination.total).to.eql(1); + expect(body.pagination.nextEvent).to.eql(null); }); it('should return the first page of information when the cursor is invalid', async () => { const { body } = await supertest .get( - `/api/endpoint/resolver/${entityID}/related?legacyEndpointID=${endpointID}&after=blah` + `/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=blah` ) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(1); - expect(body.pagination.next).to.not.eql(null); + expect(body.pagination.nextEvent).to.eql(null); }); it('should error on invalid pagination values', async () => { await supertest - .get(`/api/endpoint/resolver/${entityID}/related?limit=0`) + .get(`/api/endpoint/resolver/${entityID}/events?events=0`) .set(commonHeaders) .expect(400); await supertest - .get(`/api/endpoint/resolver/${entityID}/related?limit=2000`) + .get(`/api/endpoint/resolver/${entityID}/events?events=2000`) .set(commonHeaders) .expect(400); await supertest - .get(`/api/endpoint/resolver/${entityID}/related?limit=-1`) + .get(`/api/endpoint/resolver/${entityID}/events?events=-1`) .set(commonHeaders) .expect(400); }); it('should not find any events', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/5555/related`) + .get(`/api/endpoint/resolver/5555/events`) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextEvent).to.eql(null); expect(body.events).to.be.empty(); }); it('should return no results for an invalid endpoint ID', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/${entityID}/related?legacyEndpointID=foo`) + .get(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=foo`) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextEvent).to.eql(null); expect(body.events).to.be.empty(); }); }); @@ -103,48 +96,46 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC it('should return details for the root node', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}&ancestors=5`) + .get( + `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=5` + ) .set(commonHeaders) .expect(200); expect(body.lifecycle.length).to.eql(2); - expect(body.ancestors.length).to.eql(1); - expect(body.pagination.next).to.eql(null); - // 5 is default parameter - expect(body.pagination.ancestors).to.eql(5); + expect(body.parent).not.to.eql(null); + expect(body.pagination.nextAncestor).to.eql(null); }); it('should have a populated next parameter', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}`) + .get(`/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}`) .set(commonHeaders) .expect(200); - expect(body.pagination.next).to.eql('94041'); + expect(body.pagination.nextAncestor).to.eql('94041'); }); it('should handle an ancestors param request', async () => { let { body } = await supertest - .get(`/api/endpoint/resolver/${entityID}?legacyEndpointID=${endpointID}`) + .get(`/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}`) .set(commonHeaders) .expect(200); - const next = body.pagination.next; + const next = body.pagination.nextAncestor; ({ body } = await supertest - .get(`/api/endpoint/resolver/${next}?legacyEndpointID=${endpointID}&ancestors=1`) + .get(`/api/endpoint/resolver/${next}/ancestry?legacyEndpointID=${endpointID}&ancestors=1`) .set(commonHeaders) .expect(200)); expect(body.lifecycle.length).to.eql(1); - expect(body.ancestors.length).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextAncestor).to.eql(null); }); it('should handle an invalid id', async () => { const { body } = await supertest - .get(`/api/endpoint/resolver/alskdjflasj`) + .get(`/api/endpoint/resolver/alskdjflasj/ancestry`) .set(commonHeaders) .expect(200); expect(body.lifecycle.length).to.eql(0); - expect(body.ancestors.length).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextAncestor).to.eql(null); }); }); @@ -158,51 +149,58 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}`) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(1); - expect(body.pagination.next).to.eql(cursor); - // default limit - expect(body.pagination.limit).to.eql(10); - expect(body.children.length).to.eql(1); expect(body.children[0].lifecycle.length).to.eql(2); expect(body.children[0].lifecycle[0].endgame.unique_pid).to.eql(94042); }); + it('returns multiple levels of child process lifecycle events', async () => { + const { body } = await supertest + .get(`/api/endpoint/resolver/93802/children?legacyEndpointID=${endpointID}&generations=3`) + .set(commonHeaders) + .expect(200); + expect(body.pagination.nextChild).to.be(null); + expect(body.children[0].pagination.nextChild).to.be(null); + + expect(body.children.length).to.eql(8); + expect(body.children[0].children[0].lifecycle.length).to.eql(2); + expect(body.children[0].lifecycle[0].endgame.unique_pid).to.eql(93932); + }); + it('returns no values when there is no more data', async () => { const { body } = await supertest // after is set to the document id of the last event so there shouldn't be any more after it .get( - `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&after=${cursor}` + `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&afterChild=${cursor}` ) .set(commonHeaders) .expect(200); expect(body.children).be.empty(); - expect(body.pagination.next).to.eql(null); - expect(body.pagination.total).to.eql(1); + expect(body.pagination.nextChild).to.eql(null); }); it('returns the first page of information when the cursor is invalid', async () => { const { body } = await supertest .get( - `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&after=blah` + `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&afterChild=blah` ) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(1); - expect(body.pagination.next).to.not.eql(null); + expect(body.children.length).to.eql(1); + expect(body.pagination.nextChild).to.be(null); }); it('errors on invalid pagination values', async () => { await supertest - .get(`/api/endpoint/resolver/${entityID}/children?limit=0`) + .get(`/api/endpoint/resolver/${entityID}/children?children=0`) .set(commonHeaders) .expect(400); await supertest - .get(`/api/endpoint/resolver/${entityID}/children?limit=2000`) + .get(`/api/endpoint/resolver/${entityID}/children?children=2000`) .set(commonHeaders) .expect(400); await supertest - .get(`/api/endpoint/resolver/${entityID}/children?limit=-1`) + .get(`/api/endpoint/resolver/${entityID}/children?children=-1`) .set(commonHeaders) .expect(400); }); @@ -212,8 +210,7 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC .get(`/api/endpoint/resolver/5555/children`) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextChild).to.eql(null); expect(body.children).to.be.empty(); }); @@ -222,10 +219,26 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=foo`) .set(commonHeaders) .expect(200); - expect(body.pagination.total).to.eql(0); - expect(body.pagination.next).to.eql(null); + expect(body.pagination.nextChild).to.eql(null); expect(body.children).to.be.empty(); }); }); + + describe('tree endpoint', () => { + const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a'; + + it('returns ancestors, events, children, and current process lifecycle', async () => { + const { body } = await supertest + .get(`/api/endpoint/resolver/93933?legacyEndpointID=${endpointID}`) + .set(commonHeaders) + .expect(200); + expect(body.pagination.nextAncestor).to.equal(null); + expect(body.pagination.nextEvent).to.equal(null); + expect(body.pagination.nextChild).to.equal(null); + expect(body.children.length).to.equal(0); + expect(body.events.length).to.equal(0); + expect(body.lifecycle.length).to.equal(2); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/infra/metrics_alerting.ts b/x-pack/test/api_integration/apis/infra/metrics_alerting.ts index 4f17f9db674839..19879f5761ab2c 100644 --- a/x-pack/test/api_integration/apis/infra/metrics_alerting.ts +++ b/x-pack/test/api_integration/apis/infra/metrics_alerting.ts @@ -39,6 +39,7 @@ export default function({ getService }: FtrProviderContext) { }); expect(result.error).to.not.be.ok(); expect(result.hits).to.be.ok(); + expect(result.aggregations).to.be.ok(); }); } it('should work with a filterQuery', async () => { @@ -53,6 +54,21 @@ export default function({ getService }: FtrProviderContext) { }); expect(result.error).to.not.be.ok(); expect(result.hits).to.be.ok(); + expect(result.aggregations).to.be.ok(); + }); + it('should work with a filterQuery in KQL format', async () => { + const searchBody = getElasticsearchMetricQuery( + getSearchParams('avg'), + undefined, + '"agent.hostname":"foo"' + ); + const result = await client.search({ + index, + body: searchBody, + }); + expect(result.error).to.not.be.ok(); + expect(result.hits).to.be.ok(); + expect(result.aggregations).to.be.ok(); }); }); describe('querying with a groupBy parameter', () => { @@ -65,6 +81,7 @@ export default function({ getService }: FtrProviderContext) { }); expect(result.error).to.not.be.ok(); expect(result.hits).to.be.ok(); + expect(result.aggregations).to.be.ok(); }); } it('should work with a filterQuery', async () => { @@ -79,6 +96,7 @@ export default function({ getService }: FtrProviderContext) { }); expect(result.error).to.not.be.ok(); expect(result.hits).to.be.ok(); + expect(result.aggregations).to.be.ok(); }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts index bbc766df34dcfe..10857caab98e27 100644 --- a/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts +++ b/x-pack/test/api_integration/apis/ml/anomaly_detectors/create.ts @@ -91,12 +91,11 @@ export default ({ getService }: FtrProviderContext) => { model_plot_config: { enabled: true }, }, expected: { - responseCode: 403, + responseCode: 404, responseBody: { - statusCode: 403, - error: 'Forbidden', - message: - '[security_exception] action [cluster:admin/xpack/ml/job/put] is unauthorized for user [ml_viewer]', + statusCode: 404, + error: 'Not Found', + message: 'Not Found', }, }, }, diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts index 6a57db1687868f..a5cb68d7821267 100644 --- a/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_summary.ts @@ -197,8 +197,8 @@ export default ({ getService }: FtrProviderContext) => { requestBody: {}, // Note that the jobs and datafeeds are loaded async so the actual error message is not deterministic. expected: { - responseCode: 403, - error: 'Forbidden', + responseCode: 404, + error: 'Not Found', }, }, ]; diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts index 23ddd3b63a2eff..c42fc95c1bc7f2 100644 --- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -84,10 +84,9 @@ export default ({ getService }: FtrProviderContext) => { startDatafeed: false, }, expected: { - responseCode: 403, - error: 'Forbidden', - message: - '[security_exception] action [cluster:monitor/xpack/ml/info/get] is unauthorized for user [ml_unauthorized]', + responseCode: 404, + error: 'Not Found', + message: 'Not Found', }, }, ]; diff --git a/x-pack/test/api_integration/apis/security/session.ts b/x-pack/test/api_integration/apis/security/session.ts index ef7e48388ff660..fcdf268ff27b0a 100644 --- a/x-pack/test/api_integration/apis/security/session.ts +++ b/x-pack/test/api_integration/apis/security/session.ts @@ -56,7 +56,7 @@ export default function({ getService }: FtrProviderContext) { expect(body.now).to.be.a('number'); expect(body.idleTimeoutExpiration).to.be.a('number'); expect(body.lifespanExpiration).to.be(null); - expect(body.provider).to.be('basic'); + expect(body.provider).to.eql({ type: 'basic', name: 'basic' }); }); it('should not extend the session', async () => { diff --git a/x-pack/test/api_integration/apis/uptime/feature_controls.ts b/x-pack/test/api_integration/apis/uptime/feature_controls.ts index 6d125807e169d1..6c566ec7cb23b8 100644 --- a/x-pack/test/api_integration/apis/uptime/feature_controls.ts +++ b/x-pack/test/api_integration/apis/uptime/feature_controls.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PINGS_DATE_RANGE_END, PINGS_DATE_RANGE_START } from './constants'; -import { API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../../plugins/uptime/common/constants'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); diff --git a/x-pack/test/api_integration/apis/uptime/rest/certs.ts b/x-pack/test/api_integration/apis/uptime/rest/certs.ts index 7510ea3f34d282..a3a15d8f8b0143 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/certs.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/certs.ts @@ -8,8 +8,8 @@ import expect from '@kbn/expect'; import moment from 'moment'; import { isRight } from 'fp-ts/lib/Either'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; -import { CertType } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; +import { CertType } from '../../../../../plugins/uptime/common/runtime_types'; import { makeChecksWithStatus } from './helper/make_checks'; export default function({ getService }: FtrProviderContext) { diff --git a/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts index 5258426cf193c1..f343cd1da8788b 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/doc_count.ts @@ -5,7 +5,7 @@ */ import { FtrProviderContext } from '../../../ftr_provider_context'; import { expectFixtureEql } from './helper/expect_fixture_eql'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; export default function({ getService }: FtrProviderContext) { describe('docCount query', () => { diff --git a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts index ea980721b831bb..95caf50d1ca7a4 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/dynamic_settings.ts @@ -7,9 +7,8 @@ import expect from '@kbn/expect'; import { isRight } from 'fp-ts/lib/Either'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../legacy/plugins/uptime/common/constants'; -import { DynamicSettingsType } from '../../../../../legacy/plugins/uptime/common/runtime_types'; - +import { DynamicSettingsType } from '../../../../../plugins/uptime/common/runtime_types'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../plugins/uptime/common/constants'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_generated.ts b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_generated.ts index 3c17370532f915..c3d5849e028ab0 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_generated.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_generated.ts @@ -7,8 +7,8 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { makeChecksWithStatus } from './helper/make_checks'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; -import { MonitorSummary } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { MonitorSummary } from '../../../../../plugins/uptime/common/runtime_types'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); diff --git a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts index f1e37bff405fd3..c5a691312f5250 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/monitor_states_real_data.ts @@ -7,8 +7,8 @@ import expect from '@kbn/expect'; import { isRight } from 'fp-ts/lib/Either'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; -import { MonitorSummaryResultType } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { MonitorSummaryResultType } from '../../../../../plugins/uptime/common/runtime_types'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; interface ExpectedMonitorStatesPage { response: any; diff --git a/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts b/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts index a261763d5991f9..3d754d89cf9be0 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/ping_list.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { isLeft } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; -import { PingsResponseType } from '../../../../../legacy/plugins/uptime/common/runtime_types'; +import { PingsResponseType } from '../../../../../plugins/uptime/common/runtime_types'; import { FtrProviderContext } from '../../../ftr_provider_context'; function decodePingsResponseData(response: any) { diff --git a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts index 017ef02afe5ea7..99e09aa5ce886a 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../../../plugins/uptime/common/constants'; import { makeChecksWithStatus } from './helper/make_checks'; export default function({ getService }: FtrProviderContext) { diff --git a/x-pack/test/detection_engine_api_integration/common/config.ts b/x-pack/test/detection_engine_api_integration/common/config.ts index e89352118990a1..1e6600c7cd2c0d 100644 --- a/x-pack/test/detection_engine_api_integration/common/config.ts +++ b/x-pack/test/detection_engine_api_integration/common/config.ts @@ -78,7 +78,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) 'some.non.existent.com', ])}`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, - '--xpack.alerting.enabled=true', '--xpack.eventLog.logEntries=true', ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, diff --git a/x-pack/test/functional/apps/dashboard/reporting/index.js b/x-pack/test/functional/apps/dashboard/reporting/index.ts similarity index 94% rename from x-pack/test/functional/apps/dashboard/reporting/index.js rename to x-pack/test/functional/apps/dashboard/reporting/index.ts index 99be084d80d74c..796e15b4e270f4 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/index.js +++ b/x-pack/test/functional/apps/dashboard/reporting/index.ts @@ -5,9 +5,10 @@ */ import expect from '@kbn/expect'; -import path from 'path'; import fs from 'fs'; +import path from 'path'; import { promisify } from 'util'; +import { FtrProviderContext } from '../../../ftr_provider_context'; import { checkIfPngsMatch } from './lib/compare_pngs'; const writeFileAsync = promisify(fs.writeFile); @@ -15,7 +16,7 @@ const mkdirAsync = promisify(fs.mkdir); const REPORTS_FOLDER = path.resolve(__dirname, 'reports'); -export default function({ getService, getPageObjects }) { +export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); const log = getService('log'); @@ -85,14 +86,14 @@ export default function({ getService, getPageObjects }) { describe('Preserve Layout', () => { it('matches baseline report', async function() { - const writeSessionReport = async (name, rawPdf, reportExt) => { + const writeSessionReport = async (name: string, rawPdf: Buffer, reportExt: string) => { const sessionDirectory = path.resolve(REPORTS_FOLDER, 'session'); await mkdirAsync(sessionDirectory, { recursive: true }); const sessionReportPath = path.resolve(sessionDirectory, `${name}.${reportExt}`); await writeFileAsync(sessionReportPath, rawPdf); return sessionReportPath; }; - const getBaselineReportPath = (fileName, reportExt) => { + const getBaselineReportPath = (fileName: string, reportExt: string) => { const baselineFolder = path.resolve(REPORTS_FOLDER, 'baseline'); const fullPath = path.resolve(baselineFolder, `${fileName}.${reportExt}`); log.debug(`getBaselineReportPath (${fullPath})`); diff --git a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.js b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts similarity index 90% rename from x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.js rename to x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts index 13c97a7fce7858..b2eb645c8372c0 100644 --- a/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.js +++ b/x-pack/test/functional/apps/dashboard/reporting/lib/compare_pngs.ts @@ -4,14 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import path from 'path'; -import fs from 'fs'; import { promisify } from 'bluebird'; +import fs from 'fs'; +import path from 'path'; import { comparePngs } from '../../../../../../../test/functional/services/lib/compare_pngs'; -const mkdirAsync = promisify(fs.mkdir); +const mkdirAsync = promisify(fs.mkdir); -export async function checkIfPngsMatch(actualpngPath, baselinepngPath, screenshotsDirectory, log) { +export async function checkIfPngsMatch( + actualpngPath: string, + baselinepngPath: string, + screenshotsDirectory: string, + log: any +) { log.debug(`checkIfpngsMatch: ${actualpngPath} vs ${baselinepngPath}`); // Copy the pngs into the screenshot session directory, as that's where the generated pngs will automatically be // stored. diff --git a/x-pack/test/functional/apps/discover/reporting.js b/x-pack/test/functional/apps/discover/reporting.ts similarity index 96% rename from x-pack/test/functional/apps/discover/reporting.js rename to x-pack/test/functional/apps/discover/reporting.ts index 4aa005fc2db553..7a33e7f5135d48 100644 --- a/x-pack/test/functional/apps/discover/reporting.js +++ b/x-pack/test/functional/apps/discover/reporting.ts @@ -5,8 +5,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function({ getService, getPageObjects }) { +export default function({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const esArchiver = getService('esArchiver'); const browser = getService('browser'); diff --git a/x-pack/test/functional/apps/uptime/settings.ts b/x-pack/test/functional/apps/uptime/settings.ts index 64b6300e0df631..7a813a5cdfb526 100644 --- a/x-pack/test/functional/apps/uptime/settings.ts +++ b/x-pack/test/functional/apps/uptime/settings.ts @@ -6,8 +6,8 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../legacy/plugins/uptime/common/constants'; -import { DynamicSettings } from '../../../../legacy/plugins/uptime/common/runtime_types'; +import { DynamicSettings } from '../../../../plugins/uptime/common/runtime_types'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../plugins/uptime/common/constants'; import { makeChecks } from '../../../api_integration/apis/uptime/rest/helper/make_checks'; export default ({ getPageObjects, getService }: FtrProviderContext) => { diff --git a/x-pack/test/functional/apps/visualize/reporting.js b/x-pack/test/functional/apps/visualize/reporting.ts similarity index 94% rename from x-pack/test/functional/apps/visualize/reporting.js rename to x-pack/test/functional/apps/visualize/reporting.ts index bc252e1ad4134b..5ef954e334d815 100644 --- a/x-pack/test/functional/apps/visualize/reporting.js +++ b/x-pack/test/functional/apps/visualize/reporting.ts @@ -5,8 +5,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function({ getService, getPageObjects }) { +export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const browser = getService('browser'); const log = getService('log'); diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 782d57adea7707..4b8c2944ef190f 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -19,7 +19,6 @@ import { GraphPageProvider } from './graph_page'; import { GrokDebuggerPageProvider } from './grok_debugger_page'; // @ts-ignore not ts yet import { WatcherPageProvider } from './watcher_page'; -// @ts-ignore not ts yet import { ReportingPageProvider } from './reporting_page'; // @ts-ignore not ts yet import { AccountSettingProvider } from './accountsetting_page'; diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 1bf637c50b0ba4..57b2847cc2e500 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -12,6 +12,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont const testSubjects = getService('testSubjects'); const retry = getService('retry'); const find = getService('find'); + const comboBox = getService('comboBox'); const PageObjects = getPageObjects([ 'header', 'common', @@ -107,20 +108,17 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param opts.operation - the desired operation ID for the dimension * @param opts.field - the desired field for the dimension */ - async configureDimension(opts: { dimension: string; operation?: string; field?: string }) { + async configureDimension(opts: { dimension: string; operation: string; field: string }) { await find.clickByCssSelector(opts.dimension); - if (opts.operation) { - await find.clickByCssSelector( - `[data-test-subj="lns-indexPatternDimensionIncompatible-${opts.operation}"], - [data-test-subj="lns-indexPatternDimension-${opts.operation}"]` - ); - } + await find.clickByCssSelector( + `[data-test-subj="lns-indexPatternDimensionIncompatible-${opts.operation}"], + [data-test-subj="lns-indexPatternDimension-${opts.operation}"]` + ); - if (opts.field) { - await testSubjects.click('indexPattern-dimension-field'); - await testSubjects.click(`lns-fieldOption-${opts.field}`); - } + const target = await testSubjects.find('indexPattern-dimension-field'); + await comboBox.openOptionsList(target); + await comboBox.setElement(target, opts.field); }, /** diff --git a/x-pack/test/functional/page_objects/reporting_page.js b/x-pack/test/functional/page_objects/reporting_page.ts similarity index 84% rename from x-pack/test/functional/page_objects/reporting_page.js rename to x-pack/test/functional/page_objects/reporting_page.ts index b24ba8cf95d1ce..2c20519a8d2140 100644 --- a/x-pack/test/functional/page_objects/reporting_page.js +++ b/x-pack/test/functional/page_objects/reporting_page.ts @@ -4,25 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ +import http, { IncomingMessage } from 'http'; +import { FtrProviderContext } from 'test/functional/ftr_provider_context'; import { parse } from 'url'; -import http from 'http'; -/* - * NOTE: Reporting is a service, not an app. The page objects that are - * important for generating reports belong to the apps that integrate with the - * Reporting service. Eventually, this file should be dissolved across the - * apps that need it for testing their integration. - * Issue: https://github.com/elastic/kibana/issues/52927 - */ -export function ReportingPageProvider({ getService, getPageObjects }) { +export function ReportingPageProvider({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); const testSubjects = getService('testSubjects'); const browser = getService('browser'); - const PageObjects = getPageObjects(['common', 'security', 'share', 'timePicker']); + const PageObjects = getPageObjects(['common', 'security' as any, 'share', 'timePicker']); // FIXME: Security PageObject is not Typescript class ReportingPage { - async forceSharedItemsContainerSize({ width }) { + async forceSharedItemsContainerSize({ width }: { width: number }) { await browser.execute(` var el = document.querySelector('[data-shared-items-container]'); el.style.flex="none"; @@ -30,7 +24,7 @@ export function ReportingPageProvider({ getService, getPageObjects }) { `); } - async getReportURL(timeout) { + async getReportURL(timeout: number) { log.debug('getReportURL'); const url = await testSubjects.getAttribute('downloadCompletedReportButton', 'href', timeout); @@ -48,7 +42,7 @@ export function ReportingPageProvider({ getService, getPageObjects }) { `); } - getResponse(url) { + getResponse(url: string): Promise { log.debug(`getResponse for ${url}`); const auth = 'test_user:changeme'; // FIXME not sure why there is no config that can be read for this const headers = { @@ -62,29 +56,30 @@ export function ReportingPageProvider({ getService, getPageObjects }) { hostname: parsedUrl.hostname, path: parsedUrl.path, port: parsedUrl.port, - responseType: 'arraybuffer', headers, }, - res => { + (res: IncomingMessage) => { resolve(res); } ) - .on('error', e => { + .on('error', (e: Error) => { log.error(e); reject(e); }); }); } - async getRawPdfReportData(url) { - const data = []; // List of Buffer objects + async getRawPdfReportData(url: string): Promise { + const data: Buffer[] = []; // List of Buffer objects log.debug(`getRawPdfReportData for ${url}`); return new Promise(async (resolve, reject) => { const response = await this.getResponse(url).catch(reject); - response.on('data', chunk => data.push(chunk)); - response.on('end', () => resolve(Buffer.concat(data))); + if (response) { + response.on('data', (chunk: Buffer) => data.push(chunk)); + response.on('end', () => resolve(Buffer.concat(data))); + } }); } diff --git a/x-pack/test/functional/services/uptime/settings.ts b/x-pack/test/functional/services/uptime/settings.ts index 9719152b62d354..96f5e45ce2ca47 100644 --- a/x-pack/test/functional/services/uptime/settings.ts +++ b/x-pack/test/functional/services/uptime/settings.ts @@ -5,7 +5,7 @@ */ import { FtrProviderContext } from '../../ftr_provider_context'; -import { DynamicSettings } from '../../../../legacy/plugins/uptime/common/runtime_types'; +import { DynamicSettings } from '../../../../plugins/uptime/common/runtime_types'; export function UptimeSettingsProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 1b75f4a27766a7..d0ce18bbc1c54c 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -504,6 +504,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { } ); + // await first run to complete so we have an initial state + await retry.try(async () => { + const { alertInstances } = await alerting.alerts.getAlertState(alert.id); + expect(Object.keys(alertInstances).length).to.eql(instances.length); + }); + // refresh to see alert await browser.refresh(); @@ -514,12 +520,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // click on first alert await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); - - // await first run to complete so we have an initial state - await retry.try(async () => { - const { alertInstances } = await alerting.alerts.getAlertState(alert.id); - expect(Object.keys(alertInstances).length).to.eql(instances.length); - }); }); const PAGE_SIZE = 10; diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index a620b1d9533767..71b22a336f6b92 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -50,8 +50,6 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { `--elasticsearch.hosts=https://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, `--plugin-path=${join(__dirname, 'fixtures', 'plugins', 'alerts')}`, - '--xpack.actions.enabled=true', - '--xpack.alerting.enabled=true', `--xpack.actions.preconfigured=${JSON.stringify([ { id: 'my-slack1', diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts index c5f3e65581df97..9622715e87e55f 100644 --- a/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/init_routes.ts @@ -40,7 +40,7 @@ export const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger } catch (ex) { logger.info(`log event error: ${ex}`); await context.core.savedObjects.client.create('event_log_test', {}, { id }); - logger.info(`created saved object`); + logger.info(`created saved object ${id}`); } eventLogger.logEvent(event); logger.info(`logged`); diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/kibana.json b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/kibana.json new file mode 100644 index 00000000000000..416ef7fa345919 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "sample_task_plugin", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["taskManager"], + "server": true, + "ui": false +} diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/package.json b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/package.json new file mode 100644 index 00000000000000..c8d47decd94c1c --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/package.json @@ -0,0 +1,18 @@ +{ + "name": "sample_task_plugin", + "version": "1.0.0", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "main": "target/test/plugin_api_integration/plugins/sample_task_plugin", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + }, + "license": "Apache-2.0", + "dependencies": {} +} diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/index.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/index.ts new file mode 100644 index 00000000000000..77233f463734a1 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/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. + */ + +import { SampleTaskManagerFixturePlugin } from './plugin'; + +export const plugin = () => new SampleTaskManagerFixturePlugin(); diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts new file mode 100644 index 00000000000000..1fee2decbcba94 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/init_routes.ts @@ -0,0 +1,252 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, + CoreSetup, +} from 'kibana/server'; +import { EventEmitter } from 'events'; +import { TaskManagerStartContract } from '../../../../../plugins/task_manager/server'; + +const scope = 'testing'; +const taskManagerQuery = { + bool: { + filter: { + bool: { + must: [ + { + term: { + 'task.scope': scope, + }, + }, + ], + }, + }, + }, +}; + +export function initRoutes( + router: IRouter, + core: CoreSetup, + taskManagerStart: Promise, + taskTestingEvents: EventEmitter +) { + async function ensureIndexIsRefreshed() { + return await core.elasticsearch.adminClient.callAsInternalUser('indices.refresh', { + index: '.kibana_task_manager', + }); + } + + router.post( + { + path: `/api/sample_tasks/schedule`, + validate: { + body: schema.object({ + task: schema.object({ + taskType: schema.string(), + schedule: schema.maybe( + schema.object({ + interval: schema.string(), + }) + ), + interval: schema.maybe(schema.string()), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + state: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + id: schema.maybe(schema.string()), + }), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const taskManager = await taskManagerStart; + const { task: taskFields } = req.body; + const task = { + ...taskFields, + scope: [scope], + }; + + const taskResult = await taskManager.schedule(task, { req }); + + return res.ok({ body: taskResult }); + } catch (err) { + return res.internalError({ body: err }); + } + } + ); + + router.post( + { + path: `/api/sample_tasks/run_now`, + validate: { + body: schema.object({ + task: schema.object({ + id: schema.string({}), + }), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { + task: { id }, + } = req.body; + try { + const taskManager = await taskManagerStart; + return res.ok({ body: await taskManager.runNow(id) }); + } catch (err) { + return res.ok({ body: { id, error: `${err}` } }); + } + } + ); + + router.post( + { + path: `/api/sample_tasks/ensure_scheduled`, + validate: { + body: schema.object({ + task: schema.object({ + taskType: schema.string(), + params: schema.object({}), + state: schema.maybe(schema.object({})), + id: schema.maybe(schema.string()), + }), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const { task: taskFields } = req.body; + const task = { + ...taskFields, + scope: [scope], + }; + + const taskManager = await taskManagerStart; + const taskResult = await taskManager.ensureScheduled(task, { req }); + + return res.ok({ body: taskResult }); + } catch (err) { + return res.ok({ body: err }); + } + } + ); + + router.post( + { + path: `/api/sample_tasks/event`, + validate: { + body: schema.object({ + event: schema.string(), + data: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const { event, data } = req.body; + taskTestingEvents.emit(event, data); + return res.ok({ body: event }); + } catch (err) { + return res.ok({ body: err }); + } + } + ); + + router.get( + { + path: `/api/sample_tasks`, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + const taskManager = await taskManagerStart; + return res.ok({ + body: await taskManager.fetch({ + query: taskManagerQuery, + }), + }); + } catch (err) { + return res.ok({ body: err }); + } + } + ); + + router.get( + { + path: `/api/sample_tasks/task/{taskId}`, + validate: { + params: schema.object({ + taskId: schema.string(), + }), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + await ensureIndexIsRefreshed(); + const taskManager = await taskManagerStart; + return res.ok({ body: await taskManager.get(req.params.taskId) }); + } catch (err) { + return res.ok({ body: err }); + } + return res.ok({ body: {} }); + } + ); + + router.delete( + { + path: `/api/sample_tasks`, + validate: {}, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + try { + let tasksFound = 0; + const taskManager = await taskManagerStart; + do { + const { docs: tasks } = await taskManager.fetch({ + query: taskManagerQuery, + }); + tasksFound = tasks.length; + await Promise.all(tasks.map(task => taskManager.remove(task.id))); + } while (tasksFound > 0); + return res.ok({ body: 'OK' }); + } catch (err) { + return res.ok({ body: err }); + } + } + ); +} diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts new file mode 100644 index 00000000000000..508d58b8f0ca96 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.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 { Plugin, CoreSetup, CoreStart } from 'kibana/server'; +import { EventEmitter } from 'events'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { initRoutes } from './init_routes'; +import { + TaskManagerSetupContract, + TaskManagerStartContract, + ConcreteTaskInstance, +} from '../../../../../plugins/task_manager/server'; +import { DEFAULT_MAX_WORKERS } from '../../../../../plugins/task_manager/server/config'; + +// this plugin's dependendencies +export interface SampleTaskManagerFixtureSetupDeps { + taskManager: TaskManagerSetupContract; +} +export interface SampleTaskManagerFixtureStartDeps { + taskManager: TaskManagerStartContract; +} + +export class SampleTaskManagerFixturePlugin + implements + Plugin { + taskManagerStart$: Subject = new Subject(); + taskManagerStart: Promise = this.taskManagerStart$ + .pipe(first()) + .toPromise(); + + public setup(core: CoreSetup, { taskManager }: SampleTaskManagerFixtureSetupDeps) { + const taskTestingEvents = new EventEmitter(); + taskTestingEvents.setMaxListeners(DEFAULT_MAX_WORKERS * 2); + + const defaultSampleTaskConfig = { + timeout: '1m', + // This task allows tests to specify its behavior (whether it reschedules itself, whether it errors, etc) + // taskInstance.params has the following optional fields: + // nextRunMilliseconds: number - If specified, the run method will return a runAt that is now + nextRunMilliseconds + // failWith: string - If specified, the task will throw an error with the specified message + // failOn: number - If specified, the task will only throw the `failWith` error when `count` equals to the failOn value + // waitForParams : boolean - should the task stall ands wait to receive params asynchronously before using the default params + // waitForEvent : string - if provided, the task will stall (after completing the run) and wait for an asyn event before completing + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => ({ + async run() { + const { params, state, id } = taskInstance; + const prevState = state || { count: 0 }; + + const count = (prevState.count || 0) + 1; + + const runParams = { + ...params, + // if this task requires custom params provided async - wait for them + ...(params.waitForParams ? await once(taskTestingEvents, id) : {}), + }; + + if (runParams.failWith) { + if (!runParams.failOn || (runParams.failOn && count === runParams.failOn)) { + throw new Error(runParams.failWith); + } + } + + await core.elasticsearch.adminClient.callAsInternalUser('index', { + index: '.kibana_task_manager_test_result', + body: { + type: 'task', + taskId: taskInstance.id, + params: JSON.stringify(runParams), + state: JSON.stringify(state), + ranAt: new Date(), + }, + refresh: true, + }); + + // Stall task run until a certain event is triggered + if (runParams.waitForEvent) { + await once(taskTestingEvents, runParams.waitForEvent); + } + + return { + state: { count }, + runAt: millisecondsFromNow(runParams.nextRunMilliseconds), + }; + }, + }), + }; + + taskManager.registerTaskDefinitions({ + sampleTask: { + ...defaultSampleTaskConfig, + type: 'sampleTask', + title: 'Sample Task', + description: 'A sample task for testing the task_manager.', + }, + singleAttemptSampleTask: { + ...defaultSampleTaskConfig, + type: 'singleAttemptSampleTask', + title: 'Failing Sample Task', + description: + 'A sample task for testing the task_manager that fails on the first attempt to run.', + // fail after the first failed run + maxAttempts: 1, + }, + }); + + taskManager.addMiddleware({ + async beforeSave({ taskInstance, ...opts }) { + const modifiedInstance = { + ...taskInstance, + params: { + originalParams: taskInstance.params, + superFly: 'My middleware param!', + }, + }; + + return { + ...opts, + taskInstance: modifiedInstance, + }; + }, + + async beforeRun({ taskInstance, ...opts }) { + return { + ...opts, + taskInstance: { + ...taskInstance, + params: taskInstance.params.originalParams, + }, + }; + }, + + async beforeMarkRunning(context) { + return context; + }, + }); + initRoutes(core.http.createRouter(), core, this.taskManagerStart, taskTestingEvents); + } + + public start(core: CoreStart, { taskManager }: SampleTaskManagerFixtureStartDeps) { + this.taskManagerStart$.next(taskManager); + this.taskManagerStart$.complete(); + } + public stop() {} +} + +function millisecondsFromNow(ms: number) { + if (!ms) { + return; + } + + const dt = new Date(); + dt.setTime(dt.getTime() + ms); + return dt; +} + +const once = function(emitter: EventEmitter, event: string): Promise> { + return new Promise(resolve => { + emitter.once(event, data => resolve(data || {})); + }); +}; diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/index.js b/x-pack/test/plugin_api_integration/plugins/task_manager/index.js deleted file mode 100644 index e5b645367b8b71..00000000000000 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/index.js +++ /dev/null @@ -1,150 +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. - */ - -const { DEFAULT_MAX_WORKERS } = require('../../../../plugins/task_manager/server/config.ts'); -const { EventEmitter } = require('events'); - -import { initRoutes } from './init_routes'; - -const once = function(emitter, event) { - return new Promise(resolve => { - emitter.once(event, data => resolve(data || {})); - }); -}; - -export default function TaskTestingAPI(kibana) { - const taskTestingEvents = new EventEmitter(); - taskTestingEvents.setMaxListeners(DEFAULT_MAX_WORKERS * 2); - - return new kibana.Plugin({ - name: 'sampleTask', - require: ['elasticsearch', 'task_manager'], - - config(Joi) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - - init(server) { - const taskManager = { - ...server.newPlatform.setup.plugins.taskManager, - ...server.newPlatform.start.plugins.taskManager, - }; - const legacyTaskManager = server.plugins.task_manager; - - const defaultSampleTaskConfig = { - timeout: '1m', - // This task allows tests to specify its behavior (whether it reschedules itself, whether it errors, etc) - // taskInstance.params has the following optional fields: - // nextRunMilliseconds: number - If specified, the run method will return a runAt that is now + nextRunMilliseconds - // failWith: string - If specified, the task will throw an error with the specified message - // failOn: number - If specified, the task will only throw the `failWith` error when `count` equals to the failOn value - // waitForParams : boolean - should the task stall ands wait to receive params asynchronously before using the default params - // waitForEvent : string - if provided, the task will stall (after completing the run) and wait for an asyn event before completing - createTaskRunner: ({ taskInstance }) => ({ - async run() { - const { params, state, id } = taskInstance; - const prevState = state || { count: 0 }; - - const count = (prevState.count || 0) + 1; - - const runParams = { - ...params, - // if this task requires custom params provided async - wait for them - ...(params.waitForParams ? await once(taskTestingEvents, id) : {}), - }; - - if (runParams.failWith) { - if (!runParams.failOn || (runParams.failOn && count === runParams.failOn)) { - throw new Error(runParams.failWith); - } - } - - const callCluster = server.plugins.elasticsearch.getCluster('admin') - .callWithInternalUser; - await callCluster('index', { - index: '.kibana_task_manager_test_result', - body: { - type: 'task', - taskId: taskInstance.id, - params: JSON.stringify(runParams), - state: JSON.stringify(state), - ranAt: new Date(), - }, - refresh: true, - }); - - // Stall task run until a certain event is triggered - if (runParams.waitForEvent) { - await once(taskTestingEvents, runParams.waitForEvent); - } - - return { - state: { count }, - runAt: millisecondsFromNow(runParams.nextRunMilliseconds), - }; - }, - }), - }; - - taskManager.registerTaskDefinitions({ - sampleTask: { - ...defaultSampleTaskConfig, - title: 'Sample Task', - description: 'A sample task for testing the task_manager.', - }, - singleAttemptSampleTask: { - ...defaultSampleTaskConfig, - title: 'Failing Sample Task', - description: - 'A sample task for testing the task_manager that fails on the first attempt to run.', - // fail after the first failed run - maxAttempts: 1, - }, - }); - - taskManager.addMiddleware({ - async beforeSave({ taskInstance, ...opts }) { - const modifiedInstance = { - ...taskInstance, - params: { - originalParams: taskInstance.params, - superFly: 'My middleware param!', - }, - }; - - return { - ...opts, - taskInstance: modifiedInstance, - }; - }, - - async beforeRun({ taskInstance, ...opts }) { - return { - ...opts, - taskInstance: { - ...taskInstance, - params: taskInstance.params.originalParams, - }, - }; - }, - }); - - initRoutes(server, taskManager, legacyTaskManager, taskTestingEvents); - }, - }); -} - -function millisecondsFromNow(ms) { - if (!ms) { - return; - } - - const dt = new Date(); - dt.setTime(dt.getTime() + ms); - return dt; -} diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js b/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js deleted file mode 100644 index 785fbed3414237..00000000000000 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import Joi from 'joi'; - -const scope = 'testing'; -const taskManagerQuery = { - bool: { - filter: { - bool: { - must: [ - { - term: { - 'task.scope': scope, - }, - }, - ], - }, - }, - }, -}; - -export function initRoutes(server, taskManager, legacyTaskManager, taskTestingEvents) { - const callCluster = server.plugins.elasticsearch.getCluster('admin').callWithInternalUser; - - async function ensureIndexIsRefreshed() { - return await callCluster('indices.refresh', { - index: '.kibana_task_manager', - }); - } - - server.route({ - path: '/api/sample_tasks/schedule', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - task: Joi.object({ - taskType: Joi.string().required(), - schedule: Joi.object({ - interval: Joi.string(), - }).optional(), - interval: Joi.string().optional(), - params: Joi.object().required(), - state: Joi.object().optional(), - id: Joi.string().optional(), - }), - }), - }, - }, - async handler(request) { - try { - const { task: taskFields } = request.payload; - const task = { - ...taskFields, - scope: [scope], - }; - - const taskResult = await taskManager.schedule(task, { request }); - - return taskResult; - } catch (err) { - return err; - } - }, - }); - - /* - Schedule using legacy Api - */ - server.route({ - path: '/api/sample_tasks/schedule_legacy', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - task: Joi.object({ - taskType: Joi.string().required(), - schedule: Joi.object({ - interval: Joi.string(), - }).optional(), - interval: Joi.string().optional(), - params: Joi.object().required(), - state: Joi.object().optional(), - id: Joi.string().optional(), - }), - }), - }, - }, - async handler(request) { - try { - const { task: taskFields } = request.payload; - const task = { - ...taskFields, - scope: [scope], - }; - - const taskResult = await legacyTaskManager.schedule(task, { request }); - - return taskResult; - } catch (err) { - return err; - } - }, - }); - - server.route({ - path: '/api/sample_tasks/run_now', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - task: Joi.object({ - id: Joi.string().optional(), - }), - }), - }, - }, - async handler(request) { - const { - task: { id }, - } = request.payload; - try { - return await taskManager.runNow(id); - } catch (err) { - return { id, error: `${err}` }; - } - }, - }); - - server.route({ - path: '/api/sample_tasks/ensure_scheduled', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - task: Joi.object({ - taskType: Joi.string().required(), - params: Joi.object().required(), - state: Joi.object().optional(), - id: Joi.string().optional(), - }), - }), - }, - }, - async handler(request) { - try { - const { task: taskFields } = request.payload; - const task = { - ...taskFields, - scope: [scope], - }; - - const taskResult = await taskManager.ensureScheduled(task, { request }); - - return taskResult; - } catch (err) { - return err; - } - }, - }); - - server.route({ - path: '/api/sample_tasks/event', - method: 'POST', - config: { - validate: { - payload: Joi.object({ - event: Joi.string().required(), - data: Joi.object() - .optional() - .default({}), - }), - }, - }, - async handler(request) { - try { - const { event, data } = request.payload; - taskTestingEvents.emit(event, data); - return { event }; - } catch (err) { - return err; - } - }, - }); - - server.route({ - path: '/api/sample_tasks', - method: 'GET', - async handler() { - try { - return taskManager.fetch({ - query: taskManagerQuery, - }); - } catch (err) { - return err; - } - }, - }); - - server.route({ - path: '/api/sample_tasks/task/{taskId}', - method: 'GET', - async handler(request) { - try { - await ensureIndexIsRefreshed(); - return await taskManager.get(request.params.taskId); - } catch (err) { - return err; - } - }, - }); - - server.route({ - path: '/api/sample_tasks', - method: 'DELETE', - async handler() { - try { - let tasksFound = 0; - do { - const { docs: tasks } = await taskManager.fetch({ - query: taskManagerQuery, - }); - tasksFound = tasks.length; - await Promise.all(tasks.map(task => taskManager.remove(task.id))); - } while (tasksFound > 0); - return 'OK'; - } catch (err) { - return err; - } - }, - }); -} diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/package.json b/x-pack/test/plugin_api_integration/plugins/task_manager/package.json deleted file mode 100644 index ec63c512e9cd79..00000000000000 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "sample_task_plugin", - "version": "1.0.0", - "kibana": { - "version": "kibana", - "templateVersion": "1.0.0" - }, - "license": "Apache-2.0", - "dependencies": { - "joi": "^13.5.2" - } -} diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts index d664357c3ba126..f3a3d58336b1db 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts @@ -7,7 +7,7 @@ import { merge, omit, times, chunk, isEmpty } from 'lodash'; import uuid from 'uuid'; import expect from '@kbn/expect/expect.js'; -import moment, { Moment } from 'moment'; +import moment from 'moment'; import { FtrProviderContext } from '../../ftr_provider_context'; import { IEvent } from '../../../../plugins/event_log/server'; import { IValidatedEvent } from '../../../../plugins/event_log/server/types'; @@ -19,7 +19,9 @@ export default function({ getService }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); - describe('Event Log public API', () => { + // FLAKY: https://github.com/elastic/kibana/issues/64723 + // FLAKY: https://github.com/elastic/kibana/issues/64812 + describe.skip('Event Log public API', () => { it('should allow querying for events by Saved Object', async () => { const id = uuid.v4(); @@ -43,10 +45,8 @@ export default function({ getService }: FtrProviderContext) { it('should support pagination for events', async () => { const id = uuid.v4(); - const timestamp = moment(); - const [firstExpectedEvent, ...expectedEvents] = times(6, () => - fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))) - ); + const [firstExpectedEvent, ...expectedEvents] = times(6, () => fakeEvent(id)); + // run one first to create the SO and avoid clashes await logTestEvent(id, firstExpectedEvent); await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); @@ -82,10 +82,7 @@ export default function({ getService }: FtrProviderContext) { it('should support sorting by event end', async () => { const id = uuid.v4(); - const timestamp = moment(); - const [firstExpectedEvent, ...expectedEvents] = times(6, () => - fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))) - ); + const [firstExpectedEvent, ...expectedEvents] = times(6, () => fakeEvent(id)); // run one first to create the SO and avoid clashes await logTestEvent(id, firstExpectedEvent); await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); @@ -106,21 +103,24 @@ export default function({ getService }: FtrProviderContext) { it('should support date ranges for events', async () => { const id = uuid.v4(); - const timestamp = moment(); - - const firstEvent = fakeEvent(id, fakeEventTiming(timestamp)); + // write a document that shouldn't be found in the inclusive date range search + const firstEvent = fakeEvent(id); await logTestEvent(id, firstEvent); - await delay(100); - const start = timestamp.add(1, 's').toISOString(); + // wait a second, get the start time for the date range search + await delay(1000); + const start = new Date().toISOString(); - const expectedEvents = times(6, () => fakeEvent(id, fakeEventTiming(timestamp.add(1, 's')))); + // write the documents that we should be found in the date range searches + const expectedEvents = times(6, () => fakeEvent(id)); await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); - const end = timestamp.add(1, 's').toISOString(); + // get the end time for the date range search + const end = new Date().toISOString(); - await delay(100); - const lastEvent = fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))); + // write a document that shouldn't be found in the inclusive date range search + await delay(1000); + const lastEvent = fakeEvent(id); await logTestEvent(id, lastEvent); await retry.try(async () => { @@ -195,33 +195,17 @@ export default function({ getService }: FtrProviderContext) { .expect(200); } - function fakeEventTiming(start: Moment): Partial { - return { - event: { - start: start.toISOString(), - end: start - .clone() - .add(500, 'milliseconds') - .toISOString(), - }, - }; - } - function fakeEvent(id: string, overrides: Partial = {}): IEvent { - const start = moment().toISOString(); - const end = moment().toISOString(); return merge( { event: { provider: 'event_log_fixture', action: 'test', - start, - end, - duration: 1000000, }, kibana: { saved_objects: [ { + rel: 'primary', namespace: 'default', type: 'event_log_test', id, diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index 2de395308ce74c..361d80aaedd419 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -3,6 +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 uuid from 'uuid'; import expect from '@kbn/expect/expect.js'; import { IEvent } from '../../../../plugins/event_log/server'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -97,10 +98,10 @@ export default function({ getService }: FtrProviderContext) { await registerProviderActions('provider4', ['action1', 'action2']); } - const eventId = '1'; + const eventId = uuid.v4(); const event: IEvent = { event: { action: 'action1', provider: 'provider4' }, - kibana: { saved_objects: [{ type: 'event_log_test', id: eventId }] }, + kibana: { saved_objects: [{ rel: 'primary', type: 'event_log_test', id: eventId }] }, }; await logTestEvent(eventId, event); diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js index e8f976d5ae6e30..00cefa42711c9c 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js @@ -11,7 +11,7 @@ import supertestAsPromised from 'supertest-as-promised'; const { task: { properties: taskManagerIndexMapping }, -} = require('../../../../legacy/plugins/task_manager/server/mappings.json'); +} = require('../../../../plugins/task_manager/server/saved_objects/mappings.json'); const { DEFAULT_MAX_WORKERS, @@ -90,15 +90,6 @@ export default function({ getService }) { .then(response => response.body); } - function scheduleTaskUsingLegacyApi(task) { - return supertest - .post('/api/sample_tasks/schedule_legacy') - .set('kbn-xsrf', 'xxx') - .send({ task }) - .expect(200) - .then(response => response.body); - } - function runTaskNow(task) { return supertest .post('/api/sample_tasks/run_now') @@ -587,15 +578,5 @@ export default function({ getService }) { expect(getTaskById(tasks, longRunningTask.id).state.count).to.eql(1); }); }); - - it('should retain the legacy api until v8.0.0', async () => { - const result = await scheduleTaskUsingLegacyApi({ - id: 'task-with-legacy-api', - taskType: 'sampleTask', - params: {}, - }); - - expect(result.id).to.be('task-with-legacy-api'); - }); }); } diff --git a/yarn.lock b/yarn.lock index ee00ef283f07c0..94e6a0a11aa993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4798,6 +4798,11 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== +"@types/set-value@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/set-value/-/set-value-2.0.0.tgz#63d386b103926dcf49b50e16e0f6dd49983046be" + integrity sha512-k8dCJEC80F/mbsIOZ5Hj3YSzTVVVBwMdtP/M9Rtc2TM4F5etVd+2UG8QUiAUfbXm4fABedL2tBZnrBheY7UwpA== + "@types/shot@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/shot/-/shot-4.0.0.tgz#7545500c489b65c69b5bc5446ba4fef3bd26af92" @@ -26910,6 +26915,13 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +set-value@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-3.0.2.tgz#74e8ecd023c33d0f77199d415409a40f21e61b90" + integrity sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA== + dependencies: + is-plain-object "^2.0.4" + setimmediate@^1.0.4, setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"