diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index accc99170bb70..df3a56dd35130 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,33 +29,32 @@ /src/plugins/dashboard/ @elastic/kibana-app # App Architecture +/examples/url_generators_examples/ @elastic/kibana-app-arch +/examples/url_generators_explorer/ @elastic/kibana-app-arch /packages/kbn-interpreter/ @elastic/kibana-app-arch -/src/legacy/core_plugins/data/ @elastic/kibana-app-arch -/src/legacy/core_plugins/elasticsearch/lib/create_proxy.js @elastic/kibana-app-arch /src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch /src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana/public/management/ @elastic/kibana-app-arch -/src/legacy/core_plugins/kibana/server/field_formats/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana/server/routes/api/management/ @elastic/kibana-app-arch -/src/legacy/core_plugins/kibana/server/routes/api/suggestions/ @elastic/kibana-app-arch /src/legacy/core_plugins/visualizations/ @elastic/kibana-app-arch /src/legacy/server/index_patterns/ @elastic/kibana-app-arch +/src/plugins/advanced_settings/ @elastic/kibana-app-arch /src/plugins/bfetch/ @elastic/kibana-app-arch /src/plugins/data/ @elastic/kibana-app-arch /src/plugins/embeddable/ @elastic/kibana-app-arch /src/plugins/expressions/ @elastic/kibana-app-arch /src/plugins/inspector/ @elastic/kibana-app-arch /src/plugins/kibana_react/ @elastic/kibana-app-arch +/src/plugins/kibana_react/public/code_editor @elastic/kibana-canvas /src/plugins/kibana_utils/ @elastic/kibana-app-arch /src/plugins/management/ @elastic/kibana-app-arch /src/plugins/navigation/ @elastic/kibana-app-arch +/src/plugins/share/ @elastic/kibana-app-arch /src/plugins/ui_actions/ @elastic/kibana-app-arch /src/plugins/visualizations/ @elastic/kibana-app-arch -/src/plugins/share/ @elastic/kibana-app-arch -/examples/url_generators_examples/ @elastic/kibana-app-arch -/examples/url_generators_explorer/ @elastic/kibana-app-arch /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch +/x-pack/plugins/data_enhanced/ @elastic/kibana-app-arch /x-pack/plugins/drilldowns/ @elastic/kibana-app-arch # APM @@ -75,9 +74,9 @@ # Observability UIs /x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui /x-pack/plugins/infra/ @elastic/logs-metrics-ui -/x-pack/plugins/ingest_manager/ @elastic/ingest -/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest -/x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest +/x-pack/plugins/ingest_manager/ @elastic/ingest-management +/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 # Machine Learning diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md similarity index 51% rename from docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md rename to docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md index 205f863526e22..78a4442a651e6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.getiscollapsed_.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md @@ -1,15 +1,15 @@ -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getIsCollapsed$](./kibana-plugin-core-public.chromestart.getiscollapsed_.md) +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [getIsNavDrawerLocked$](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) -## ChromeStart.getIsCollapsed$() method +## ChromeStart.getIsNavDrawerLocked$() method -Get an observable of the current collapsed state of the chrome. +Get an observable of the current locked state of the nav drawer. Signature: ```typescript -getIsCollapsed$(): Observable; +getIsNavDrawerLocked$(): Observable; ``` Returns: diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.md index 7d9d47df544d0..c179e089d7cfd 100644 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.md +++ b/docs/development/core/public/kibana-plugin-core-public.chromestart.md @@ -56,7 +56,7 @@ core.chrome.setHelpExtension(elem => { | [getBrand$()](./kibana-plugin-core-public.chromestart.getbrand_.md) | Get an observable of the current brand information. | | [getBreadcrumbs$()](./kibana-plugin-core-public.chromestart.getbreadcrumbs_.md) | Get an observable of the current list of breadcrumbs | | [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent | -| [getIsCollapsed$()](./kibana-plugin-core-public.chromestart.getiscollapsed_.md) | Get an observable of the current collapsed state of the chrome. | +| [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. | | [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. | | [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with addApplicationClass(). If className is unknown it is ignored. | | [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title | @@ -65,6 +65,5 @@ core.chrome.setHelpExtension(elem => { | [setBreadcrumbs(newBreadcrumbs)](./kibana-plugin-core-public.chromestart.setbreadcrumbs.md) | Override the current set of breadcrumbs | | [setHelpExtension(helpExtension)](./kibana-plugin-core-public.chromestart.sethelpextension.md) | Override the current set of custom help content | | [setHelpSupportUrl(url)](./kibana-plugin-core-public.chromestart.sethelpsupporturl.md) | Override the default support URL shown in the help menu | -| [setIsCollapsed(isCollapsed)](./kibana-plugin-core-public.chromestart.setiscollapsed.md) | Set the collapsed state of the chrome navigation. | | [setIsVisible(isVisible)](./kibana-plugin-core-public.chromestart.setisvisible.md) | Set the temporary visibility for the chrome. This does nothing if the chrome is hidden by default and should be used to hide the chrome for things like full-screen modes with an exit button. | diff --git a/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md b/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md deleted file mode 100644 index b1843ef326d96..0000000000000 --- a/docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [setIsCollapsed](./kibana-plugin-core-public.chromestart.setiscollapsed.md) - -## ChromeStart.setIsCollapsed() method - -Set the collapsed state of the chrome navigation. - -Signature: - -```typescript -setIsCollapsed(isCollapsed: boolean): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| isCollapsed | boolean | | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md index e03072f9a41c3..7fd65e5db35f3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.esfilters.md @@ -44,8 +44,8 @@ esFilters: { getPhraseFilterField: (filter: import("../common").PhraseFilter) => string; getPhraseFilterValue: (filter: import("../common").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; - compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("./query/filter_manager/lib/compare_filters").FilterCompareOptions) => boolean; - COMPARE_ALL_OPTIONS: import("./query/filter_manager/lib/compare_filters").FilterCompareOptions; + compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; + COMPARE_ALL_OPTIONS: import("../common").FilterCompareOptions; generateFilters: typeof generateFilters; onlyDisabledFiltersChanged: (newFilters?: import("../common").Filter[] | undefined, oldFilters?: import("../common").Filter[] | undefined) => boolean; changeTimeFilter: typeof changeTimeFilter; diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md deleted file mode 100644 index 27141c68ae1a7..0000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ICancel](./kibana-plugin-plugins-data-server.icancel.md) - -## ICancel type - -Signature: - -```typescript -export declare type ICancel = (id: string) => Promise; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md new file mode 100644 index 0000000000000..99c30515e8da6 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchCancel](./kibana-plugin-plugins-data-server.isearchcancel.md) + +## ISearchCancel type + +Signature: + +```typescript +export declare type ISearchCancel = (id: string) => Promise; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 12d53f1a35ea0..e756eb9b72905 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -67,9 +67,9 @@ | Type Alias | Description | | --- | --- | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-server.fieldformatsgetconfigfn.md) | | -| [ICancel](./kibana-plugin-plugins-data-server.icancel.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-server.ifieldformatsregistry.md) | | | [ISearch](./kibana-plugin-plugins-data-server.isearch.md) | | +| [ISearchCancel](./kibana-plugin-plugins-data-server.isearchcancel.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-server.parsedinterval.md) | | | [TSearchStrategyProvider](./kibana-plugin-plugins-data-server.tsearchstrategyprovider.md) | Search strategy provider creates an instance of a search strategy with the request handler context bound to it. This way every search strategy can use whatever information they require from the request context. | diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 8586d26e9a07a..6645f49029a51 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -20,9 +20,7 @@ which support the same values as <>. To control how data is collected from your {es} nodes, you configure {ref}/monitoring-settings.html[`xpack.monitoring.collection` settings] in `elasticsearch.yml`. To control how monitoring data is collected -from Logstash, you configure -{logstash-ref}/monitoring-internal-collection.html#monitoring-settings[`xpack.monitoring` settings] -in `logstash.yml`. +from Logstash, configure monitoring settings in `logstash.yml`. For more information, see {ref}/monitor-elasticsearch-cluster.html[Monitor a cluster]. diff --git a/package.json b/package.json index ed59fa742f269..8387cc85993ea 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "url": "https://github.com/elastic/kibana.git" }, "resolutions": { - "**/@types/node": "10.12.27", + "**/@types/node": ">=10.17.17 <10.20.0", "**/@types/react": "^16.9.19", "**/@types/react-router": "^5.1.3", "**/@types/hapi": "^17.0.18", @@ -119,7 +119,7 @@ "@elastic/apm-rum": "^4.6.0", "@elastic/charts": "^17.1.1", "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.6.0", + "@elastic/ems-client": "7.7.0", "@elastic/eui": "20.0.2", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", @@ -239,7 +239,7 @@ "react-resize-detector": "^4.2.0", "react-router-dom": "^5.1.2", "react-sizeme": "^2.3.6", - "react-use": "^13.13.0", + "react-use": "^13.27.0", "reactcss": "1.2.3", "redux": "^4.0.5", "redux-actions": "^2.6.5", @@ -350,7 +350,7 @@ "@types/mocha": "^5.2.7", "@types/moment-timezone": "^0.5.12", "@types/mustache": "^0.8.31", - "@types/node": "^10.12.27", + "@types/node": ">=10.17.17 <10.20.0", "@types/node-forge": "^0.9.0", "@types/normalize-path": "^3.0.0", "@types/numeral": "^0.0.26", diff --git a/packages/kbn-config-schema/src/internals/index.ts b/packages/kbn-config-schema/src/internals/index.ts index 8f5d09e5b8b49..f84e14d2f741d 100644 --- a/packages/kbn-config-schema/src/internals/index.ts +++ b/packages/kbn-config-schema/src/internals/index.ts @@ -314,7 +314,8 @@ export const internals = Joi.extend([ for (const [entryKey, entryValue] of value) { const { value: validatedEntryKey, error: keyError } = Joi.validate( entryKey, - params.key + params.key, + { presence: 'required' } ); if (keyError) { @@ -323,7 +324,8 @@ export const internals = Joi.extend([ const { value: validatedEntryValue, error: valueError } = Joi.validate( entryValue, - params.value + params.value, + { presence: 'required' } ); if (valueError) { @@ -374,7 +376,8 @@ export const internals = Joi.extend([ for (const [entryKey, entryValue] of Object.entries(value)) { const { value: validatedEntryKey, error: keyError } = Joi.validate( entryKey, - params.key + params.key, + { presence: 'required' } ); if (keyError) { @@ -383,7 +386,8 @@ export const internals = Joi.extend([ const { value: validatedEntryValue, error: valueError } = Joi.validate( entryValue, - params.value + params.value, + { presence: 'required' } ); if (valueError) { diff --git a/packages/kbn-config-schema/src/types/map_of_type.test.ts b/packages/kbn-config-schema/src/types/map_of_type.test.ts index b015f51bdc8ad..1c5a227ef0fac 100644 --- a/packages/kbn-config-schema/src/types/map_of_type.test.ts +++ b/packages/kbn-config-schema/src/types/map_of_type.test.ts @@ -159,6 +159,24 @@ test('object within mapOf', () => { expect(type.validate(value)).toEqual(expected); }); +test('enforces required object fields within mapOf', () => { + const type = schema.mapOf( + schema.string(), + schema.object({ + bar: schema.object({ + baz: schema.number(), + }), + }) + ); + const value = { + foo: {}, + }; + + expect(() => type.validate(value)).toThrowErrorMatchingInlineSnapshot( + `"[foo.bar.baz]: expected value of type [number] but got [undefined]"` + ); +}); + test('error preserves full path', () => { const type = schema.object({ grandParentKey: schema.object({ diff --git a/packages/kbn-config-schema/src/types/map_type.ts b/packages/kbn-config-schema/src/types/map_type.ts index 231c3726ae9d5..6da664bf95616 100644 --- a/packages/kbn-config-schema/src/types/map_type.ts +++ b/packages/kbn-config-schema/src/types/map_type.ts @@ -57,7 +57,10 @@ export class MapOfType extends Type> { path.length, 0, // If `key` validation failed, let's stress that to make error more obvious. - type === 'map.key' ? `key("${entryKey}")` : entryKey.toString() + type === 'map.key' ? `key("${entryKey}")` : entryKey.toString(), + // Error could have happened deep inside value/key schema and error message should + // include full path. + ...(reason instanceof SchemaTypeError ? reason.path : []) ); return reason instanceof SchemaTypesError diff --git a/packages/kbn-config-schema/src/types/record_of_type.test.ts b/packages/kbn-config-schema/src/types/record_of_type.test.ts index ef15e7b0f6ad6..aee7dde71c3e4 100644 --- a/packages/kbn-config-schema/src/types/record_of_type.test.ts +++ b/packages/kbn-config-schema/src/types/record_of_type.test.ts @@ -159,6 +159,24 @@ test('object within recordOf', () => { expect(type.validate(value)).toEqual({ foo: { bar: 123 } }); }); +test('enforces required object fields within recordOf', () => { + const type = schema.recordOf( + schema.string(), + schema.object({ + bar: schema.object({ + baz: schema.number(), + }), + }) + ); + const value = { + foo: {}, + }; + + expect(() => type.validate(value)).toThrowErrorMatchingInlineSnapshot( + `"[foo.bar.baz]: expected value of type [number] but got [undefined]"` + ); +}); + test('error preserves full path', () => { const type = schema.object({ grandParentKey: schema.object({ diff --git a/packages/kbn-config-schema/src/types/record_type.ts b/packages/kbn-config-schema/src/types/record_type.ts index c6d4b4d71b4f1..ef9e70cbabc08 100644 --- a/packages/kbn-config-schema/src/types/record_type.ts +++ b/packages/kbn-config-schema/src/types/record_type.ts @@ -49,7 +49,10 @@ export class RecordOfType extends Type> { path.length, 0, // If `key` validation failed, let's stress that to make error more obvious. - type === 'record.key' ? `key("${entryKey}")` : entryKey.toString() + type === 'record.key' ? `key("${entryKey}")` : entryKey.toString(), + // Error could have happened deep inside value/key schema and error message should + // include full path. + ...(reason instanceof SchemaTypeError ? reason.path : []) ); return reason instanceof SchemaTypesError diff --git a/packages/kbn-dev-utils/src/run/run.ts b/packages/kbn-dev-utils/src/run/run.ts index e185f86cc3bf7..35477e988d837 100644 --- a/packages/kbn-dev-utils/src/run/run.ts +++ b/packages/kbn-dev-utils/src/run/run.ts @@ -17,6 +17,8 @@ * under the License. */ +import { inspect } from 'util'; + // @ts-ignore @types are outdated and module is super simple import exitHook from 'exit-hook'; @@ -62,7 +64,11 @@ export async function run(fn: RunFn, options: Options = {}) { process.on('unhandledRejection', error => { log.error('UNHANDLED PROMISE REJECTION'); - log.error(error); + log.error( + error instanceof Error + ? error + : new Error(`non-Error type rejection value: ${inspect(error)}`) + ); process.exit(1); }); diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 338d489300526..9fab74ea47a87 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(704); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(705); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); @@ -39806,6 +39806,7 @@ exports.isFailError = fail_1.isFailError; */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = __webpack_require__(36); +const util_1 = __webpack_require__(29); // @ts-ignore @types are outdated and module is super simple const exit_hook_1 = tslib_1.__importDefault(__webpack_require__(348)); const tooling_log_1 = __webpack_require__(415); @@ -39825,7 +39826,9 @@ async function run(fn, options = {}) { }); process.on('unhandledRejection', error => { log.error('UNHANDLED PROMISE REJECTION'); - log.error(error); + log.error(error instanceof Error + ? error + : new Error(`non-Error type rejection value: ${util_1.inspect(error)}`)); process.exit(1); }); const handleErrorWithoutExit = (error) => { @@ -57074,7 +57077,7 @@ async function getChangesForProjects(projects, kbn, log) { log.verbose('getting changed files'); const { stdout - } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmt', '--', ...Array.from(projects.values()).map(p => p.path)], { + } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['ls-files', '-dmt', '--', ...Array.from(projects.values()).filter(p => kbn.isPartOfRepo(p)).map(p => p.path)], { cwd: kbn.getAbsolute() }); const output = stdout.trim(); @@ -57117,6 +57120,11 @@ async function getChangesForProjects(projects, kbn, log) { const changesByProject = new Map(); for (const project of sortedRelevantProjects) { + if (kbn.isOutsideRepo(project)) { + changesByProject.set(project, undefined); + continue; + } + const ownChanges = new Map(); const prefix = kbn.getRelative(project.path); @@ -57141,6 +57149,10 @@ async function getChangesForProjects(projects, kbn, log) { async function getLatestSha(project, kbn) { + if (kbn.isOutsideRepo(project)) { + return; + } + const { stdout } = await execa__WEBPACK_IMPORTED_MODULE_3___default()('git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], { @@ -57200,7 +57212,7 @@ async function getChecksum(project, changes, yarnLock, kbn, log) { log.verbose(`[${project.name}] local sha:`, sha); } - if (Array.from(changes.values()).includes('invalid')) { + if (!changes || Array.from(changes.values()).includes('invalid')) { log.warning(`[${project.name}] unable to determine local changes, caching disabled`); return; } @@ -79162,8 +79174,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(700); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(501); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(579); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(704); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(579); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -79192,6 +79206,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope + /** * Helper class for dealing with a set of projects as children of * the Kibana project. The kbn/pm is currently implemented to be @@ -79206,7 +79221,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope class Kibana { static async loadFrom(rootPath) { - return new Kibana((await Object(_projects__WEBPACK_IMPORTED_MODULE_2__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ + return new Kibana((await Object(_projects__WEBPACK_IMPORTED_MODULE_3__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])({ rootPath })))); } @@ -79265,7 +79280,7 @@ class Kibana { getProjectAndDeps(name) { const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_2__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + return Object(_projects__WEBPACK_IMPORTED_MODULE_3__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); } /** filter the projects to just those matching certain paths/include/exclude tags */ @@ -79274,7 +79289,7 @@ class Kibana { const allProjects = this.getAllProjects(); const filteredProjects = new Map(); const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])(_objectSpread({}, options, { + const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_4__["getProjectPaths"])(_objectSpread({}, options, { rootPath: this.kibanaProject.path })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); @@ -79292,6 +79307,14 @@ class Kibana { return filteredProjects; } + isPartOfRepo(project) { + return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_2___default()(project.path, this.kibanaProject.path); + } + + isOutsideRepo(project) { + return !this.isPartOfRepo(project); + } + } /***/ }), @@ -79385,14 +79408,42 @@ module.exports = arrify; /***/ }), /* 704 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const path = __webpack_require__(16); + +module.exports = (childPath, parentPath) => { + childPath = path.resolve(childPath); + parentPath = path.resolve(parentPath); + + if (process.platform === 'win32') { + childPath = childPath.toLowerCase(); + parentPath = parentPath.toLowerCase(); + } + + if (childPath === parentPath) { + return false; + } + + childPath += path.sep; + parentPath += path.sep; + + return childPath.startsWith(parentPath); +}; + + +/***/ }), +/* 705 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(705); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(928); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(929); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -79417,13 +79468,13 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 705 */ +/* 706 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(707); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(587); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); @@ -79565,7 +79616,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 706 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79573,13 +79624,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(379); const path = __webpack_require__(16); const os = __webpack_require__(11); -const pAll = __webpack_require__(707); -const arrify = __webpack_require__(709); -const globby = __webpack_require__(710); +const pAll = __webpack_require__(708); +const arrify = __webpack_require__(710); +const globby = __webpack_require__(711); const isGlob = __webpack_require__(605); -const cpFile = __webpack_require__(913); -const junk = __webpack_require__(925); -const CpyError = __webpack_require__(926); +const cpFile = __webpack_require__(914); +const junk = __webpack_require__(926); +const CpyError = __webpack_require__(927); const defaultOptions = { ignoreJunk: true @@ -79698,12 +79749,12 @@ module.exports = (source, destination, { /***/ }), -/* 707 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(708); +const pMap = __webpack_require__(709); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -79711,7 +79762,7 @@ module.exports.default = module.exports; /***/ }), -/* 708 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79790,7 +79841,7 @@ module.exports.default = pMap; /***/ }), -/* 709 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79820,17 +79871,17 @@ module.exports = arrify; /***/ }), -/* 710 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const arrayUnion = __webpack_require__(711); -const glob = __webpack_require__(713); -const fastGlob = __webpack_require__(718); -const dirGlob = __webpack_require__(906); -const gitignore = __webpack_require__(909); +const arrayUnion = __webpack_require__(712); +const glob = __webpack_require__(714); +const fastGlob = __webpack_require__(719); +const dirGlob = __webpack_require__(907); +const gitignore = __webpack_require__(910); const DEFAULT_FILTER = () => false; @@ -79975,12 +80026,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 711 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(712); +var arrayUniq = __webpack_require__(713); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -79988,7 +80039,7 @@ module.exports = function () { /***/ }), -/* 712 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80057,7 +80108,7 @@ if ('Set' in global) { /***/ }), -/* 713 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -80106,13 +80157,13 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(714) +var inherits = __webpack_require__(715) var EE = __webpack_require__(379).EventEmitter var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var globSync = __webpack_require__(716) -var common = __webpack_require__(717) +var globSync = __webpack_require__(717) +var common = __webpack_require__(718) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -80853,7 +80904,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 714 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -80863,12 +80914,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(715); + module.exports = __webpack_require__(716); } /***/ }), -/* 715 */ +/* 716 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -80901,7 +80952,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 716 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -80911,12 +80962,12 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(713).Glob +var Glob = __webpack_require__(714).Glob var util = __webpack_require__(29) var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var common = __webpack_require__(717) +var common = __webpack_require__(718) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -81393,7 +81444,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 717 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -81639,10 +81690,10 @@ function childrenIgnored (self, path) { /***/ }), -/* 718 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(719); +const pkg = __webpack_require__(720); module.exports = pkg.async; module.exports.default = pkg.async; @@ -81655,19 +81706,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 719 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(720); -var taskManager = __webpack_require__(721); -var reader_async_1 = __webpack_require__(877); -var reader_stream_1 = __webpack_require__(901); -var reader_sync_1 = __webpack_require__(902); -var arrayUtils = __webpack_require__(904); -var streamUtils = __webpack_require__(905); +var optionsManager = __webpack_require__(721); +var taskManager = __webpack_require__(722); +var reader_async_1 = __webpack_require__(878); +var reader_stream_1 = __webpack_require__(902); +var reader_sync_1 = __webpack_require__(903); +var arrayUtils = __webpack_require__(905); +var streamUtils = __webpack_require__(906); /** * Synchronous API. */ @@ -81733,7 +81784,7 @@ function isString(source) { /***/ }), -/* 720 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81771,13 +81822,13 @@ exports.prepare = prepare; /***/ }), -/* 721 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(722); +var patternUtils = __webpack_require__(723); /** * Generate tasks based on parent directory of each pattern. */ @@ -81868,16 +81919,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 722 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var globParent = __webpack_require__(723); -var isGlob = __webpack_require__(726); -var micromatch = __webpack_require__(727); +var globParent = __webpack_require__(724); +var isGlob = __webpack_require__(727); +var micromatch = __webpack_require__(728); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -82023,15 +82074,15 @@ exports.matchAny = matchAny; /***/ }), -/* 723 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(16); -var isglob = __webpack_require__(724); -var pathDirname = __webpack_require__(725); +var isglob = __webpack_require__(725); +var pathDirname = __webpack_require__(726); var isWin32 = __webpack_require__(11).platform() === 'win32'; module.exports = function globParent(str) { @@ -82054,7 +82105,7 @@ module.exports = function globParent(str) { /***/ }), -/* 724 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82085,7 +82136,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 725 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82235,7 +82286,7 @@ module.exports.win32 = win32; /***/ }), -/* 726 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82287,7 +82338,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 727 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82298,18 +82349,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(29); -var braces = __webpack_require__(728); -var toRegex = __webpack_require__(830); -var extend = __webpack_require__(838); +var braces = __webpack_require__(729); +var toRegex = __webpack_require__(831); +var extend = __webpack_require__(839); /** * Local dependencies */ -var compilers = __webpack_require__(841); -var parsers = __webpack_require__(873); -var cache = __webpack_require__(874); -var utils = __webpack_require__(875); +var compilers = __webpack_require__(842); +var parsers = __webpack_require__(874); +var cache = __webpack_require__(875); +var utils = __webpack_require__(876); var MAX_LENGTH = 1024 * 64; /** @@ -83171,7 +83222,7 @@ module.exports = micromatch; /***/ }), -/* 728 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83181,18 +83232,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(729); -var unique = __webpack_require__(741); -var extend = __webpack_require__(738); +var toRegex = __webpack_require__(730); +var unique = __webpack_require__(742); +var extend = __webpack_require__(739); /** * Local dependencies */ -var compilers = __webpack_require__(742); -var parsers = __webpack_require__(757); -var Braces = __webpack_require__(767); -var utils = __webpack_require__(743); +var compilers = __webpack_require__(743); +var parsers = __webpack_require__(758); +var Braces = __webpack_require__(768); +var utils = __webpack_require__(744); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -83496,15 +83547,15 @@ module.exports = braces; /***/ }), -/* 729 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(730); -var extend = __webpack_require__(738); -var not = __webpack_require__(740); +var define = __webpack_require__(731); +var extend = __webpack_require__(739); +var not = __webpack_require__(741); var MAX_LENGTH = 1024 * 64; /** @@ -83651,7 +83702,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 730 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83664,7 +83715,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(731); +var isDescriptor = __webpack_require__(732); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -83689,7 +83740,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 731 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83702,9 +83753,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(732); -var isAccessor = __webpack_require__(733); -var isData = __webpack_require__(736); +var typeOf = __webpack_require__(733); +var isAccessor = __webpack_require__(734); +var isData = __webpack_require__(737); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -83718,7 +83769,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 732 */ +/* 733 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -83871,7 +83922,7 @@ function isBuffer(val) { /***/ }), -/* 733 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83884,7 +83935,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(734); +var typeOf = __webpack_require__(735); // accessor descriptor properties var accessor = { @@ -83947,10 +83998,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 734 */ +/* 735 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -84069,7 +84120,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 735 */ +/* 736 */ /***/ (function(module, exports) { /*! @@ -84096,7 +84147,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 736 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84109,7 +84160,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(737); +var typeOf = __webpack_require__(738); // data descriptor properties var data = { @@ -84158,10 +84209,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 737 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -84280,13 +84331,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 738 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(739); +var isObject = __webpack_require__(740); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -84320,7 +84371,7 @@ function hasOwn(obj, key) { /***/ }), -/* 739 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84340,13 +84391,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 740 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(738); +var extend = __webpack_require__(739); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -84413,7 +84464,7 @@ module.exports = toRegex; /***/ }), -/* 741 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84463,13 +84514,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 742 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(743); +var utils = __webpack_require__(744); module.exports = function(braces, options) { braces.compiler @@ -84752,25 +84803,25 @@ function hasQueue(node) { /***/ }), -/* 743 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(744); +var splitString = __webpack_require__(745); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(738); -utils.flatten = __webpack_require__(750); -utils.isObject = __webpack_require__(748); -utils.fillRange = __webpack_require__(751); -utils.repeat = __webpack_require__(756); -utils.unique = __webpack_require__(741); +utils.extend = __webpack_require__(739); +utils.flatten = __webpack_require__(751); +utils.isObject = __webpack_require__(749); +utils.fillRange = __webpack_require__(752); +utils.repeat = __webpack_require__(757); +utils.unique = __webpack_require__(742); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -85102,7 +85153,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 744 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85115,7 +85166,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(745); +var extend = __webpack_require__(746); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -85280,14 +85331,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 745 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(746); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(747); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -85347,7 +85398,7 @@ function isEnum(obj, key) { /***/ }), -/* 746 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85360,7 +85411,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -85368,7 +85419,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 747 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85381,7 +85432,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); function isObjectObject(o) { return isObject(o) === true @@ -85412,7 +85463,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 748 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85431,7 +85482,7 @@ module.exports = function isObject(val) { /***/ }), -/* 749 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85478,7 +85529,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 750 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85507,7 +85558,7 @@ function flat(arr, res) { /***/ }), -/* 751 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85521,10 +85572,10 @@ function flat(arr, res) { var util = __webpack_require__(29); -var isNumber = __webpack_require__(752); -var extend = __webpack_require__(738); -var repeat = __webpack_require__(754); -var toRegex = __webpack_require__(755); +var isNumber = __webpack_require__(753); +var extend = __webpack_require__(739); +var repeat = __webpack_require__(755); +var toRegex = __webpack_require__(756); /** * Return a range of numbers or letters. @@ -85722,7 +85773,7 @@ module.exports = fillRange; /***/ }), -/* 752 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85735,7 +85786,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); module.exports = function isNumber(num) { var type = typeOf(num); @@ -85751,10 +85802,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 753 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -85873,7 +85924,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 754 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85950,7 +86001,7 @@ function repeat(str, num) { /***/ }), -/* 755 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85963,8 +86014,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(754); -var isNumber = __webpack_require__(752); +var repeat = __webpack_require__(755); +var isNumber = __webpack_require__(753); var cache = {}; function toRegexRange(min, max, options) { @@ -86251,7 +86302,7 @@ module.exports = toRegexRange; /***/ }), -/* 756 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86276,14 +86327,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 757 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(758); -var utils = __webpack_require__(743); +var Node = __webpack_require__(759); +var utils = __webpack_require__(744); /** * Braces parsers @@ -86643,15 +86694,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 758 */ +/* 759 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(748); -var define = __webpack_require__(759); -var utils = __webpack_require__(766); +var isObject = __webpack_require__(749); +var define = __webpack_require__(760); +var utils = __webpack_require__(767); var ownNames; /** @@ -87142,7 +87193,7 @@ exports = module.exports = Node; /***/ }), -/* 759 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87155,7 +87206,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -87180,7 +87231,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 760 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87193,9 +87244,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(761); -var isAccessor = __webpack_require__(762); -var isData = __webpack_require__(764); +var typeOf = __webpack_require__(762); +var isAccessor = __webpack_require__(763); +var isData = __webpack_require__(765); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -87209,7 +87260,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 761 */ +/* 762 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87344,7 +87395,7 @@ function isBuffer(val) { /***/ }), -/* 762 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87357,7 +87408,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(763); +var typeOf = __webpack_require__(764); // accessor descriptor properties var accessor = { @@ -87420,7 +87471,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 763 */ +/* 764 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87555,7 +87606,7 @@ function isBuffer(val) { /***/ }), -/* 764 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87568,7 +87619,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(765); +var typeOf = __webpack_require__(766); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -87611,7 +87662,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 765 */ +/* 766 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87746,13 +87797,13 @@ function isBuffer(val) { /***/ }), -/* 766 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); var utils = module.exports; /** @@ -88772,17 +88823,17 @@ function assert(val, message) { /***/ }), -/* 767 */ +/* 768 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(738); -var Snapdragon = __webpack_require__(768); -var compilers = __webpack_require__(742); -var parsers = __webpack_require__(757); -var utils = __webpack_require__(743); +var extend = __webpack_require__(739); +var Snapdragon = __webpack_require__(769); +var compilers = __webpack_require__(743); +var parsers = __webpack_require__(758); +var utils = __webpack_require__(744); /** * Customize Snapdragon parser and renderer @@ -88883,17 +88934,17 @@ module.exports = Braces; /***/ }), -/* 768 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(769); -var define = __webpack_require__(730); -var Compiler = __webpack_require__(798); -var Parser = __webpack_require__(827); -var utils = __webpack_require__(807); +var Base = __webpack_require__(770); +var define = __webpack_require__(731); +var Compiler = __webpack_require__(799); +var Parser = __webpack_require__(828); +var utils = __webpack_require__(808); var regexCache = {}; var cache = {}; @@ -89064,20 +89115,20 @@ module.exports.Parser = Parser; /***/ }), -/* 769 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var define = __webpack_require__(770); -var CacheBase = __webpack_require__(771); -var Emitter = __webpack_require__(772); -var isObject = __webpack_require__(748); -var merge = __webpack_require__(789); -var pascal = __webpack_require__(792); -var cu = __webpack_require__(793); +var define = __webpack_require__(771); +var CacheBase = __webpack_require__(772); +var Emitter = __webpack_require__(773); +var isObject = __webpack_require__(749); +var merge = __webpack_require__(790); +var pascal = __webpack_require__(793); +var cu = __webpack_require__(794); /** * Optionally define a custom `cache` namespace to use. @@ -89506,7 +89557,7 @@ module.exports.namespace = namespace; /***/ }), -/* 770 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89519,7 +89570,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -89544,21 +89595,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 771 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(748); -var Emitter = __webpack_require__(772); -var visit = __webpack_require__(773); -var toPath = __webpack_require__(776); -var union = __webpack_require__(777); -var del = __webpack_require__(781); -var get = __webpack_require__(779); -var has = __webpack_require__(786); -var set = __webpack_require__(780); +var isObject = __webpack_require__(749); +var Emitter = __webpack_require__(773); +var visit = __webpack_require__(774); +var toPath = __webpack_require__(777); +var union = __webpack_require__(778); +var del = __webpack_require__(782); +var get = __webpack_require__(780); +var has = __webpack_require__(787); +var set = __webpack_require__(781); /** * Create a `Cache` constructor that when instantiated will @@ -89812,7 +89863,7 @@ module.exports.namespace = namespace; /***/ }), -/* 772 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { @@ -89981,7 +90032,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 773 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89994,8 +90045,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(774); -var mapVisit = __webpack_require__(775); +var visit = __webpack_require__(775); +var mapVisit = __webpack_require__(776); module.exports = function(collection, method, val) { var result; @@ -90018,7 +90069,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 774 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90031,7 +90082,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -90058,14 +90109,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 775 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var visit = __webpack_require__(774); +var visit = __webpack_require__(775); /** * Map `visit` over an array of objects. @@ -90102,7 +90153,7 @@ function isObject(val) { /***/ }), -/* 776 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90115,7 +90166,7 @@ function isObject(val) { -var typeOf = __webpack_require__(753); +var typeOf = __webpack_require__(754); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -90142,16 +90193,16 @@ function filter(arr) { /***/ }), -/* 777 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(739); -var union = __webpack_require__(778); -var get = __webpack_require__(779); -var set = __webpack_require__(780); +var isObject = __webpack_require__(740); +var union = __webpack_require__(779); +var get = __webpack_require__(780); +var set = __webpack_require__(781); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -90179,7 +90230,7 @@ function arrayify(val) { /***/ }), -/* 778 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90215,7 +90266,7 @@ module.exports = function union(init) { /***/ }), -/* 779 */ +/* 780 */ /***/ (function(module, exports) { /*! @@ -90271,7 +90322,7 @@ function toString(val) { /***/ }), -/* 780 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90284,10 +90335,10 @@ function toString(val) { -var split = __webpack_require__(744); -var extend = __webpack_require__(738); -var isPlainObject = __webpack_require__(747); -var isObject = __webpack_require__(739); +var split = __webpack_require__(745); +var extend = __webpack_require__(739); +var isPlainObject = __webpack_require__(748); +var isObject = __webpack_require__(740); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -90333,7 +90384,7 @@ function isValidKey(key) { /***/ }), -/* 781 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90346,8 +90397,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(748); -var has = __webpack_require__(782); +var isObject = __webpack_require__(749); +var has = __webpack_require__(783); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -90372,7 +90423,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 782 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90385,9 +90436,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(783); -var hasValues = __webpack_require__(785); -var get = __webpack_require__(779); +var isObject = __webpack_require__(784); +var hasValues = __webpack_require__(786); +var get = __webpack_require__(780); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -90398,7 +90449,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 783 */ +/* 784 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90411,7 +90462,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(784); +var isArray = __webpack_require__(785); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -90419,7 +90470,7 @@ module.exports = function isObject(val) { /***/ }), -/* 784 */ +/* 785 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -90430,7 +90481,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 785 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90473,7 +90524,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 786 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90486,9 +90537,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(748); -var hasValues = __webpack_require__(787); -var get = __webpack_require__(779); +var isObject = __webpack_require__(749); +var hasValues = __webpack_require__(788); +var get = __webpack_require__(780); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -90496,7 +90547,7 @@ module.exports = function(val, prop) { /***/ }), -/* 787 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90509,8 +90560,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(788); -var isNumber = __webpack_require__(752); +var typeOf = __webpack_require__(789); +var isNumber = __webpack_require__(753); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -90563,10 +90614,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 788 */ +/* 789 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(735); +var isBuffer = __webpack_require__(736); var toString = Object.prototype.toString; /** @@ -90688,14 +90739,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 789 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(790); -var forIn = __webpack_require__(791); +var isExtendable = __webpack_require__(791); +var forIn = __webpack_require__(792); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -90759,7 +90810,7 @@ module.exports = mixinDeep; /***/ }), -/* 790 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90772,7 +90823,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -90780,7 +90831,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 791 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90803,7 +90854,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 792 */ +/* 793 */ /***/ (function(module, exports) { /*! @@ -90830,14 +90881,14 @@ module.exports = pascalcase; /***/ }), -/* 793 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var utils = __webpack_require__(794); +var utils = __webpack_require__(795); /** * Expose class utils @@ -91202,7 +91253,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 794 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91216,10 +91267,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(778); -utils.define = __webpack_require__(730); -utils.isObj = __webpack_require__(748); -utils.staticExtend = __webpack_require__(795); +utils.union = __webpack_require__(779); +utils.define = __webpack_require__(731); +utils.isObj = __webpack_require__(749); +utils.staticExtend = __webpack_require__(796); /** @@ -91230,7 +91281,7 @@ module.exports = utils; /***/ }), -/* 795 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91243,8 +91294,8 @@ module.exports = utils; -var copy = __webpack_require__(796); -var define = __webpack_require__(730); +var copy = __webpack_require__(797); +var define = __webpack_require__(731); var util = __webpack_require__(29); /** @@ -91327,15 +91378,15 @@ module.exports = extend; /***/ }), -/* 796 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(753); -var copyDescriptor = __webpack_require__(797); -var define = __webpack_require__(730); +var typeOf = __webpack_require__(754); +var copyDescriptor = __webpack_require__(798); +var define = __webpack_require__(731); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -91508,7 +91559,7 @@ module.exports.has = has; /***/ }), -/* 797 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91596,16 +91647,16 @@ function isObject(val) { /***/ }), -/* 798 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(799); -var define = __webpack_require__(730); -var debug = __webpack_require__(801)('snapdragon:compiler'); -var utils = __webpack_require__(807); +var use = __webpack_require__(800); +var define = __webpack_require__(731); +var debug = __webpack_require__(802)('snapdragon:compiler'); +var utils = __webpack_require__(808); /** * Create a new `Compiler` with the given `options`. @@ -91759,7 +91810,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(826); + var sourcemaps = __webpack_require__(827); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -91780,7 +91831,7 @@ module.exports = Compiler; /***/ }), -/* 799 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91793,7 +91844,7 @@ module.exports = Compiler; -var utils = __webpack_require__(800); +var utils = __webpack_require__(801); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -91908,7 +91959,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 800 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91922,8 +91973,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(730); -utils.isObject = __webpack_require__(748); +utils.define = __webpack_require__(731); +utils.isObject = __webpack_require__(749); utils.isString = function(val) { @@ -91938,7 +91989,7 @@ module.exports = utils; /***/ }), -/* 801 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -91947,14 +91998,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(802); + module.exports = __webpack_require__(803); } else { - module.exports = __webpack_require__(805); + module.exports = __webpack_require__(806); } /***/ }), -/* 802 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -91963,7 +92014,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(803); +exports = module.exports = __webpack_require__(804); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -92145,7 +92196,7 @@ function localstorage() { /***/ }), -/* 803 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { @@ -92161,7 +92212,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(804); +exports.humanize = __webpack_require__(805); /** * The currently active debug mode names, and names to skip. @@ -92353,7 +92404,7 @@ function coerce(val) { /***/ }), -/* 804 */ +/* 805 */ /***/ (function(module, exports) { /** @@ -92511,7 +92562,7 @@ function plural(ms, n, name) { /***/ }), -/* 805 */ +/* 806 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -92527,7 +92578,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(803); +exports = module.exports = __webpack_require__(804); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -92706,7 +92757,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(806); + var net = __webpack_require__(807); stream = new net.Socket({ fd: fd, readable: false, @@ -92765,13 +92816,13 @@ exports.enable(load()); /***/ }), -/* 806 */ +/* 807 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 807 */ +/* 808 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92781,9 +92832,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(738); -exports.SourceMap = __webpack_require__(808); -exports.sourceMapResolve = __webpack_require__(819); +exports.extend = __webpack_require__(739); +exports.SourceMap = __webpack_require__(809); +exports.sourceMapResolve = __webpack_require__(820); /** * Convert backslash in the given string to forward slashes @@ -92826,7 +92877,7 @@ exports.last = function(arr, n) { /***/ }), -/* 808 */ +/* 809 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -92834,13 +92885,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(815).SourceMapConsumer; -exports.SourceNode = __webpack_require__(818).SourceNode; +exports.SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(816).SourceMapConsumer; +exports.SourceNode = __webpack_require__(819).SourceNode; /***/ }), -/* 809 */ +/* 810 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -92850,10 +92901,10 @@ exports.SourceNode = __webpack_require__(818).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(810); -var util = __webpack_require__(812); -var ArraySet = __webpack_require__(813).ArraySet; -var MappingList = __webpack_require__(814).MappingList; +var base64VLQ = __webpack_require__(811); +var util = __webpack_require__(813); +var ArraySet = __webpack_require__(814).ArraySet; +var MappingList = __webpack_require__(815).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -93262,7 +93313,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 810 */ +/* 811 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93302,7 +93353,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(811); +var base64 = __webpack_require__(812); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -93408,7 +93459,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 811 */ +/* 812 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93481,7 +93532,7 @@ exports.decode = function (charCode) { /***/ }), -/* 812 */ +/* 813 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93904,7 +93955,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 813 */ +/* 814 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93914,7 +93965,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); +var util = __webpack_require__(813); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -94031,7 +94082,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 814 */ +/* 815 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94041,7 +94092,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); +var util = __webpack_require__(813); /** * Determine whether mappingB is after mappingA with respect to generated @@ -94116,7 +94167,7 @@ exports.MappingList = MappingList; /***/ }), -/* 815 */ +/* 816 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94126,11 +94177,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(812); -var binarySearch = __webpack_require__(816); -var ArraySet = __webpack_require__(813).ArraySet; -var base64VLQ = __webpack_require__(810); -var quickSort = __webpack_require__(817).quickSort; +var util = __webpack_require__(813); +var binarySearch = __webpack_require__(817); +var ArraySet = __webpack_require__(814).ArraySet; +var base64VLQ = __webpack_require__(811); +var quickSort = __webpack_require__(818).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -95204,7 +95255,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 816 */ +/* 817 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95321,7 +95372,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 817 */ +/* 818 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95441,7 +95492,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 818 */ +/* 819 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95451,8 +95502,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; -var util = __webpack_require__(812); +var SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; +var util = __webpack_require__(813); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -95860,17 +95911,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 819 */ +/* 820 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(820) -var resolveUrl = __webpack_require__(821) -var decodeUriComponent = __webpack_require__(822) -var urix = __webpack_require__(824) -var atob = __webpack_require__(825) +var sourceMappingURL = __webpack_require__(821) +var resolveUrl = __webpack_require__(822) +var decodeUriComponent = __webpack_require__(823) +var urix = __webpack_require__(825) +var atob = __webpack_require__(826) @@ -96168,7 +96219,7 @@ module.exports = { /***/ }), -/* 820 */ +/* 821 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -96231,7 +96282,7 @@ void (function(root, factory) { /***/ }), -/* 821 */ +/* 822 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96249,13 +96300,13 @@ module.exports = resolveUrl /***/ }), -/* 822 */ +/* 823 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(823) +var decodeUriComponent = __webpack_require__(824) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -96266,7 +96317,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 823 */ +/* 824 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96367,7 +96418,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 824 */ +/* 825 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96390,7 +96441,7 @@ module.exports = urix /***/ }), -/* 825 */ +/* 826 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96404,7 +96455,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 826 */ +/* 827 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96412,8 +96463,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(23); var path = __webpack_require__(16); -var define = __webpack_require__(730); -var utils = __webpack_require__(807); +var define = __webpack_require__(731); +var utils = __webpack_require__(808); /** * Expose `mixin()`. @@ -96556,19 +96607,19 @@ exports.comment = function(node) { /***/ }), -/* 827 */ +/* 828 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(799); +var use = __webpack_require__(800); var util = __webpack_require__(29); -var Cache = __webpack_require__(828); -var define = __webpack_require__(730); -var debug = __webpack_require__(801)('snapdragon:parser'); -var Position = __webpack_require__(829); -var utils = __webpack_require__(807); +var Cache = __webpack_require__(829); +var define = __webpack_require__(731); +var debug = __webpack_require__(802)('snapdragon:parser'); +var Position = __webpack_require__(830); +var utils = __webpack_require__(808); /** * Create a new `Parser` with the given `input` and `options`. @@ -97096,7 +97147,7 @@ module.exports = Parser; /***/ }), -/* 828 */ +/* 829 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97203,13 +97254,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 829 */ +/* 830 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(730); +var define = __webpack_require__(731); /** * Store position for a node @@ -97224,16 +97275,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 830 */ +/* 831 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(831); -var define = __webpack_require__(837); -var extend = __webpack_require__(838); -var not = __webpack_require__(840); +var safe = __webpack_require__(832); +var define = __webpack_require__(838); +var extend = __webpack_require__(839); +var not = __webpack_require__(841); var MAX_LENGTH = 1024 * 64; /** @@ -97386,10 +97437,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 831 */ +/* 832 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(832); +var parse = __webpack_require__(833); var types = parse.types; module.exports = function (re, opts) { @@ -97435,13 +97486,13 @@ function isRegExp (x) { /***/ }), -/* 832 */ +/* 833 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(833); -var types = __webpack_require__(834); -var sets = __webpack_require__(835); -var positions = __webpack_require__(836); +var util = __webpack_require__(834); +var types = __webpack_require__(835); +var sets = __webpack_require__(836); +var positions = __webpack_require__(837); module.exports = function(regexpStr) { @@ -97723,11 +97774,11 @@ module.exports.types = types; /***/ }), -/* 833 */ +/* 834 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); -var sets = __webpack_require__(835); +var types = __webpack_require__(835); +var sets = __webpack_require__(836); // All of these are private and only used by randexp. @@ -97840,7 +97891,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 834 */ +/* 835 */ /***/ (function(module, exports) { module.exports = { @@ -97856,10 +97907,10 @@ module.exports = { /***/ }), -/* 835 */ +/* 836 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); +var types = __webpack_require__(835); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -97944,10 +97995,10 @@ exports.anyChar = function() { /***/ }), -/* 836 */ +/* 837 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(834); +var types = __webpack_require__(835); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -97967,7 +98018,7 @@ exports.end = function() { /***/ }), -/* 837 */ +/* 838 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97980,8 +98031,8 @@ exports.end = function() { -var isobject = __webpack_require__(748); -var isDescriptor = __webpack_require__(760); +var isobject = __webpack_require__(749); +var isDescriptor = __webpack_require__(761); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -98012,14 +98063,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 838 */ +/* 839 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(839); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(840); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -98079,7 +98130,7 @@ function isEnum(obj, key) { /***/ }), -/* 839 */ +/* 840 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98092,7 +98143,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -98100,14 +98151,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 840 */ +/* 841 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(838); -var safe = __webpack_require__(831); +var extend = __webpack_require__(839); +var safe = __webpack_require__(832); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -98179,14 +98230,14 @@ module.exports = toRegex; /***/ }), -/* 841 */ +/* 842 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(842); -var extglob = __webpack_require__(857); +var nanomatch = __webpack_require__(843); +var extglob = __webpack_require__(858); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -98263,7 +98314,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 842 */ +/* 843 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98274,17 +98325,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(29); -var toRegex = __webpack_require__(729); -var extend = __webpack_require__(843); +var toRegex = __webpack_require__(730); +var extend = __webpack_require__(844); /** * Local dependencies */ -var compilers = __webpack_require__(845); -var parsers = __webpack_require__(846); -var cache = __webpack_require__(849); -var utils = __webpack_require__(851); +var compilers = __webpack_require__(846); +var parsers = __webpack_require__(847); +var cache = __webpack_require__(850); +var utils = __webpack_require__(852); var MAX_LENGTH = 1024 * 64; /** @@ -99108,14 +99159,14 @@ module.exports = nanomatch; /***/ }), -/* 843 */ +/* 844 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(844); -var assignSymbols = __webpack_require__(749); +var isExtendable = __webpack_require__(845); +var assignSymbols = __webpack_require__(750); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -99175,7 +99226,7 @@ function isEnum(obj, key) { /***/ }), -/* 844 */ +/* 845 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99188,7 +99239,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(747); +var isPlainObject = __webpack_require__(748); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -99196,7 +99247,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 845 */ +/* 846 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99542,15 +99593,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 846 */ +/* 847 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(740); -var toRegex = __webpack_require__(729); -var isOdd = __webpack_require__(847); +var regexNot = __webpack_require__(741); +var toRegex = __webpack_require__(730); +var isOdd = __webpack_require__(848); /** * Characters to use in negation regex (we want to "not" match @@ -99936,7 +99987,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 847 */ +/* 848 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99949,7 +100000,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(848); +var isNumber = __webpack_require__(849); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -99963,7 +100014,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 848 */ +/* 849 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99991,14 +100042,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 849 */ +/* 850 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(850))(); +module.exports = new (__webpack_require__(851))(); /***/ }), -/* 850 */ +/* 851 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100011,7 +100062,7 @@ module.exports = new (__webpack_require__(850))(); -var MapCache = __webpack_require__(828); +var MapCache = __webpack_require__(829); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -100133,7 +100184,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 851 */ +/* 852 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100146,14 +100197,14 @@ var path = __webpack_require__(16); * Module dependencies */ -var isWindows = __webpack_require__(852)(); -var Snapdragon = __webpack_require__(768); -utils.define = __webpack_require__(853); -utils.diff = __webpack_require__(854); -utils.extend = __webpack_require__(843); -utils.pick = __webpack_require__(855); -utils.typeOf = __webpack_require__(856); -utils.unique = __webpack_require__(741); +var isWindows = __webpack_require__(853)(); +var Snapdragon = __webpack_require__(769); +utils.define = __webpack_require__(854); +utils.diff = __webpack_require__(855); +utils.extend = __webpack_require__(844); +utils.pick = __webpack_require__(856); +utils.typeOf = __webpack_require__(857); +utils.unique = __webpack_require__(742); /** * Returns true if the given value is effectively an empty string @@ -100519,7 +100570,7 @@ utils.unixify = function(options) { /***/ }), -/* 852 */ +/* 853 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -100547,7 +100598,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 853 */ +/* 854 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100560,8 +100611,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(748); -var isDescriptor = __webpack_require__(760); +var isobject = __webpack_require__(749); +var isDescriptor = __webpack_require__(761); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -100592,7 +100643,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 854 */ +/* 855 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100646,7 +100697,7 @@ function diffArray(one, two) { /***/ }), -/* 855 */ +/* 856 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100659,7 +100710,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(748); +var isObject = __webpack_require__(749); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -100688,7 +100739,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 856 */ +/* 857 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -100823,7 +100874,7 @@ function isBuffer(val) { /***/ }), -/* 857 */ +/* 858 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100833,18 +100884,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(738); -var unique = __webpack_require__(741); -var toRegex = __webpack_require__(729); +var extend = __webpack_require__(739); +var unique = __webpack_require__(742); +var toRegex = __webpack_require__(730); /** * Local dependencies */ -var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); -var Extglob = __webpack_require__(872); -var utils = __webpack_require__(871); +var compilers = __webpack_require__(859); +var parsers = __webpack_require__(870); +var Extglob = __webpack_require__(873); +var utils = __webpack_require__(872); var MAX_LENGTH = 1024 * 64; /** @@ -101161,13 +101212,13 @@ module.exports = extglob; /***/ }), -/* 858 */ +/* 859 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(859); +var brackets = __webpack_require__(860); /** * Extglob compilers @@ -101337,7 +101388,7 @@ module.exports = function(extglob) { /***/ }), -/* 859 */ +/* 860 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101347,17 +101398,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(860); -var parsers = __webpack_require__(862); +var compilers = __webpack_require__(861); +var parsers = __webpack_require__(863); /** * Module dependencies */ -var debug = __webpack_require__(864)('expand-brackets'); -var extend = __webpack_require__(738); -var Snapdragon = __webpack_require__(768); -var toRegex = __webpack_require__(729); +var debug = __webpack_require__(865)('expand-brackets'); +var extend = __webpack_require__(739); +var Snapdragon = __webpack_require__(769); +var toRegex = __webpack_require__(730); /** * Parses the given POSIX character class `pattern` and returns a @@ -101555,13 +101606,13 @@ module.exports = brackets; /***/ }), -/* 860 */ +/* 861 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(861); +var posix = __webpack_require__(862); module.exports = function(brackets) { brackets.compiler @@ -101649,7 +101700,7 @@ module.exports = function(brackets) { /***/ }), -/* 861 */ +/* 862 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101678,14 +101729,14 @@ module.exports = { /***/ }), -/* 862 */ +/* 863 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(863); -var define = __webpack_require__(730); +var utils = __webpack_require__(864); +var define = __webpack_require__(731); /** * Text regex @@ -101904,14 +101955,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 863 */ +/* 864 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(729); -var regexNot = __webpack_require__(740); +var toRegex = __webpack_require__(730); +var regexNot = __webpack_require__(741); var cached; /** @@ -101945,7 +101996,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 864 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -101954,14 +102005,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(865); + module.exports = __webpack_require__(866); } else { - module.exports = __webpack_require__(868); + module.exports = __webpack_require__(869); } /***/ }), -/* 865 */ +/* 866 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -101970,7 +102021,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(866); +exports = module.exports = __webpack_require__(867); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -102152,7 +102203,7 @@ function localstorage() { /***/ }), -/* 866 */ +/* 867 */ /***/ (function(module, exports, __webpack_require__) { @@ -102168,7 +102219,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(867); +exports.humanize = __webpack_require__(868); /** * The currently active debug mode names, and names to skip. @@ -102360,7 +102411,7 @@ function coerce(val) { /***/ }), -/* 867 */ +/* 868 */ /***/ (function(module, exports) { /** @@ -102518,7 +102569,7 @@ function plural(ms, n, name) { /***/ }), -/* 868 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -102534,7 +102585,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(866); +exports = module.exports = __webpack_require__(867); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -102713,7 +102764,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(806); + var net = __webpack_require__(807); stream = new net.Socket({ fd: fd, readable: false, @@ -102772,15 +102823,15 @@ exports.enable(load()); /***/ }), -/* 869 */ +/* 870 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(859); -var define = __webpack_require__(870); -var utils = __webpack_require__(871); +var brackets = __webpack_require__(860); +var define = __webpack_require__(871); +var utils = __webpack_require__(872); /** * Characters to use in text regex (we want to "not" match @@ -102935,7 +102986,7 @@ module.exports = parsers; /***/ }), -/* 870 */ +/* 871 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102948,7 +102999,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(760); +var isDescriptor = __webpack_require__(761); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -102973,14 +103024,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 871 */ +/* 872 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(740); -var Cache = __webpack_require__(850); +var regex = __webpack_require__(741); +var Cache = __webpack_require__(851); /** * Utils @@ -103049,7 +103100,7 @@ utils.createRegex = function(str) { /***/ }), -/* 872 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103059,16 +103110,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(768); -var define = __webpack_require__(870); -var extend = __webpack_require__(738); +var Snapdragon = __webpack_require__(769); +var define = __webpack_require__(871); +var extend = __webpack_require__(739); /** * Local dependencies */ -var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); +var compilers = __webpack_require__(859); +var parsers = __webpack_require__(870); /** * Customize Snapdragon parser and renderer @@ -103134,16 +103185,16 @@ module.exports = Extglob; /***/ }), -/* 873 */ +/* 874 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(857); -var nanomatch = __webpack_require__(842); -var regexNot = __webpack_require__(740); -var toRegex = __webpack_require__(830); +var extglob = __webpack_require__(858); +var nanomatch = __webpack_require__(843); +var regexNot = __webpack_require__(741); +var toRegex = __webpack_require__(831); var not; /** @@ -103224,14 +103275,14 @@ function textRegex(pattern) { /***/ }), -/* 874 */ +/* 875 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(850))(); +module.exports = new (__webpack_require__(851))(); /***/ }), -/* 875 */ +/* 876 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103244,13 +103295,13 @@ var path = __webpack_require__(16); * Module dependencies */ -var Snapdragon = __webpack_require__(768); -utils.define = __webpack_require__(837); -utils.diff = __webpack_require__(854); -utils.extend = __webpack_require__(838); -utils.pick = __webpack_require__(855); -utils.typeOf = __webpack_require__(876); -utils.unique = __webpack_require__(741); +var Snapdragon = __webpack_require__(769); +utils.define = __webpack_require__(838); +utils.diff = __webpack_require__(855); +utils.extend = __webpack_require__(839); +utils.pick = __webpack_require__(856); +utils.typeOf = __webpack_require__(877); +utils.unique = __webpack_require__(742); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -103547,7 +103598,7 @@ utils.unixify = function(options) { /***/ }), -/* 876 */ +/* 877 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -103682,7 +103733,7 @@ function isBuffer(val) { /***/ }), -/* 877 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103701,9 +103752,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_stream_1 = __webpack_require__(896); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -103764,15 +103815,15 @@ exports.default = ReaderAsync; /***/ }), -/* 878 */ +/* 879 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(879); -const readdirAsync = __webpack_require__(887); -const readdirStream = __webpack_require__(890); +const readdirSync = __webpack_require__(880); +const readdirAsync = __webpack_require__(888); +const readdirStream = __webpack_require__(891); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -103856,7 +103907,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 879 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103864,11 +103915,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(881); let syncFacade = { - fs: __webpack_require__(885), - forEach: __webpack_require__(886), + fs: __webpack_require__(886), + forEach: __webpack_require__(887), sync: true }; @@ -103897,7 +103948,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 880 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103906,9 +103957,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(881); -const stat = __webpack_require__(883); -const call = __webpack_require__(884); +const normalizeOptions = __webpack_require__(882); +const stat = __webpack_require__(884); +const call = __webpack_require__(885); /** * Asynchronously reads the contents of a directory and streams the results @@ -104284,14 +104335,14 @@ module.exports = DirectoryReader; /***/ }), -/* 881 */ +/* 882 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(882); +const globToRegExp = __webpack_require__(883); module.exports = normalizeOptions; @@ -104468,7 +104519,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 882 */ +/* 883 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -104605,13 +104656,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 883 */ +/* 884 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(884); +const call = __webpack_require__(885); module.exports = stat; @@ -104686,7 +104737,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 884 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104747,14 +104798,14 @@ function callOnce (fn) { /***/ }), -/* 885 */ +/* 886 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(884); +const call = __webpack_require__(885); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -104818,7 +104869,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 886 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104847,7 +104898,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 887 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104855,12 +104906,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(888); -const DirectoryReader = __webpack_require__(880); +const maybe = __webpack_require__(889); +const DirectoryReader = __webpack_require__(881); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(890), async: true }; @@ -104902,7 +104953,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 888 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104929,7 +104980,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 889 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104965,7 +105016,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 890 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104973,11 +105024,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(881); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(890), async: true }; @@ -104997,16 +105048,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 891 */ +/* 892 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(892); -var entry_1 = __webpack_require__(894); -var pathUtil = __webpack_require__(893); +var deep_1 = __webpack_require__(893); +var entry_1 = __webpack_require__(895); +var pathUtil = __webpack_require__(894); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -105072,14 +105123,14 @@ exports.default = Reader; /***/ }), -/* 892 */ +/* 893 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); -var patternUtils = __webpack_require__(722); +var pathUtils = __webpack_require__(894); +var patternUtils = __webpack_require__(723); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -105162,7 +105213,7 @@ exports.default = DeepFilter; /***/ }), -/* 893 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105193,14 +105244,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 894 */ +/* 895 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); -var patternUtils = __webpack_require__(722); +var pathUtils = __webpack_require__(894); +var patternUtils = __webpack_require__(723); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -105285,7 +105336,7 @@ exports.default = EntryFilter; /***/ }), -/* 895 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105305,8 +105356,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(897); +var fs_1 = __webpack_require__(901); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -105356,14 +105407,14 @@ exports.default = FileSystemStream; /***/ }), -/* 896 */ +/* 897 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(897); -const statProvider = __webpack_require__(899); +const optionsManager = __webpack_require__(898); +const statProvider = __webpack_require__(900); /** * Asynchronous API. */ @@ -105394,13 +105445,13 @@ exports.statSync = statSync; /***/ }), -/* 897 */ +/* 898 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(898); +const fsAdapter = __webpack_require__(899); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -105413,7 +105464,7 @@ exports.prepare = prepare; /***/ }), -/* 898 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105436,7 +105487,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 899 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105488,7 +105539,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 900 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105519,7 +105570,7 @@ exports.default = FileSystem; /***/ }), -/* 901 */ +/* 902 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105539,9 +105590,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_stream_1 = __webpack_require__(896); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -105609,7 +105660,7 @@ exports.default = ReaderStream; /***/ }), -/* 902 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105628,9 +105679,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_sync_1 = __webpack_require__(903); +var readdir = __webpack_require__(879); +var reader_1 = __webpack_require__(892); +var fs_sync_1 = __webpack_require__(904); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -105690,7 +105741,7 @@ exports.default = ReaderSync; /***/ }), -/* 903 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105709,8 +105760,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(897); +var fs_1 = __webpack_require__(901); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -105756,7 +105807,7 @@ exports.default = FileSystemSync; /***/ }), -/* 904 */ +/* 905 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105772,7 +105823,7 @@ exports.flatten = flatten; /***/ }), -/* 905 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105793,13 +105844,13 @@ exports.merge = merge; /***/ }), -/* 906 */ +/* 907 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(907); +const pathType = __webpack_require__(908); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -105865,13 +105916,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 907 */ +/* 908 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(908); +const pify = __webpack_require__(909); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -105914,7 +105965,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 908 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106005,17 +106056,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 909 */ +/* 910 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); const path = __webpack_require__(16); -const fastGlob = __webpack_require__(718); -const gitIgnore = __webpack_require__(910); -const pify = __webpack_require__(911); -const slash = __webpack_require__(912); +const fastGlob = __webpack_require__(719); +const gitIgnore = __webpack_require__(911); +const pify = __webpack_require__(912); +const slash = __webpack_require__(913); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -106113,7 +106164,7 @@ module.exports.sync = options => { /***/ }), -/* 910 */ +/* 911 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -106582,7 +106633,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 911 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106657,7 +106708,7 @@ module.exports = (input, options) => { /***/ }), -/* 912 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106675,17 +106726,17 @@ module.exports = input => { /***/ }), -/* 913 */ +/* 914 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); -const fs = __webpack_require__(921); -const ProgressEmitter = __webpack_require__(924); +const pEvent = __webpack_require__(915); +const CpFileError = __webpack_require__(918); +const fs = __webpack_require__(922); +const ProgressEmitter = __webpack_require__(925); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -106799,12 +106850,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 914 */ +/* 915 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(915); +const pTimeout = __webpack_require__(916); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -107095,12 +107146,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 915 */ +/* 916 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(916); +const pFinally = __webpack_require__(917); class TimeoutError extends Error { constructor(message) { @@ -107146,7 +107197,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 916 */ +/* 917 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107168,12 +107219,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 917 */ +/* 918 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(918); +const NestedError = __webpack_require__(919); class CpFileError extends NestedError { constructor(message, nested) { @@ -107187,10 +107238,10 @@ module.exports = CpFileError; /***/ }), -/* 918 */ +/* 919 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(919); +var inherits = __webpack_require__(920); var NestedError = function (message, nested) { this.nested = nested; @@ -107241,7 +107292,7 @@ module.exports = NestedError; /***/ }), -/* 919 */ +/* 920 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -107249,12 +107300,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(920); + module.exports = __webpack_require__(921); } /***/ }), -/* 920 */ +/* 921 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -107283,16 +107334,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 921 */ +/* 922 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(29); const fs = __webpack_require__(22); -const makeDir = __webpack_require__(922); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); +const makeDir = __webpack_require__(923); +const pEvent = __webpack_require__(915); +const CpFileError = __webpack_require__(918); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -107389,7 +107440,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 922 */ +/* 923 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107397,7 +107448,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const {promisify} = __webpack_require__(29); -const semver = __webpack_require__(923); +const semver = __webpack_require__(924); const defaults = { mode: 0o777 & (~process.umask()), @@ -107546,7 +107597,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 923 */ +/* 924 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -109148,7 +109199,7 @@ function coerce (version, options) { /***/ }), -/* 924 */ +/* 925 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109189,7 +109240,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 925 */ +/* 926 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109235,12 +109286,12 @@ exports.default = module.exports; /***/ }), -/* 926 */ +/* 927 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(927); +const NestedError = __webpack_require__(928); class CpyError extends NestedError { constructor(message, nested) { @@ -109254,7 +109305,7 @@ module.exports = CpyError; /***/ }), -/* 927 */ +/* 928 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -109310,7 +109361,7 @@ module.exports = NestedError; /***/ }), -/* 928 */ +/* 929 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 444d46307b059..a05e1634226e5 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -26,7 +26,7 @@ "@types/lodash.clonedeepwith": "^4.5.3", "@types/log-symbols": "^2.0.0", "@types/ncp": "^2.0.1", - "@types/node": "^10.12.27", + "@types/node": ">=10.17.17 <10.20.0", "@types/ora": "^1.3.5", "@types/read-pkg": "^4.0.0", "@types/strip-ansi": "^3.0.0", @@ -48,6 +48,7 @@ "globby": "^8.0.1", "has-ansi": "^3.0.0", "indent-string": "^3.2.0", + "is-path-inside": "^3.0.2", "lodash.clonedeepwith": "^4.5.0", "log-symbols": "^2.2.0", "multimatch": "^4.0.0", diff --git a/packages/kbn-pm/src/utils/kibana.ts b/packages/kbn-pm/src/utils/kibana.ts index 36f697d19fc1f..58af98b2a92db 100644 --- a/packages/kbn-pm/src/utils/kibana.ts +++ b/packages/kbn-pm/src/utils/kibana.ts @@ -20,6 +20,7 @@ import Path from 'path'; import multimatch from 'multimatch'; +import isPathInside from 'is-path-inside'; import { ProjectMap, getProjects, includeTransitiveProjects } from './projects'; import { Project } from './project'; @@ -121,4 +122,15 @@ export class Kibana { return filteredProjects; } + + isPartOfRepo(project: Project) { + return ( + project.path === this.kibanaProject.path || + isPathInside(project.path, this.kibanaProject.path) + ); + } + + isOutsideRepo(project: Project) { + return !this.isPartOfRepo(project); + } } diff --git a/packages/kbn-pm/src/utils/project_checksums.ts b/packages/kbn-pm/src/utils/project_checksums.ts index 2fd24c8fc9577..572f2adb19bd9 100644 --- a/packages/kbn-pm/src/utils/project_checksums.ts +++ b/packages/kbn-pm/src/utils/project_checksums.ts @@ -43,7 +43,14 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too const { stdout } = await execa( 'git', - ['ls-files', '-dmt', '--', ...Array.from(projects.values()).map(p => p.path)], + [ + 'ls-files', + '-dmt', + '--', + ...Array.from(projects.values()) + .filter(p => kbn.isPartOfRepo(p)) + .map(p => p.path), + ], { cwd: kbn.getAbsolute(), } @@ -84,9 +91,14 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too } const sortedRelevantProjects = Array.from(projects.values()).sort(projectBySpecificitySorter); - const changesByProject = new Map(); + const changesByProject = new Map(); for (const project of sortedRelevantProjects) { + if (kbn.isOutsideRepo(project)) { + changesByProject.set(project, undefined); + continue; + } + const ownChanges: Changes = new Map(); const prefix = kbn.getRelative(project.path); @@ -114,6 +126,10 @@ async function getChangesForProjects(projects: ProjectMap, kbn: Kibana, log: Too /** Get the latest commit sha for a project */ async function getLatestSha(project: Project, kbn: Kibana) { + if (kbn.isOutsideRepo(project)) { + return; + } + const { stdout } = await execa( 'git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], @@ -175,7 +191,7 @@ function resolveDepsForProject(project: Project, yarnLock: YarnLock, kbn: Kibana */ async function getChecksum( project: Project, - changes: Changes, + changes: Changes | undefined, yarnLock: YarnLock, kbn: Kibana, log: ToolingLog @@ -185,7 +201,7 @@ async function getChecksum( log.verbose(`[${project.name}] local sha:`, sha); } - if (Array.from(changes.values()).includes('invalid')) { + if (!changes || Array.from(changes.values()).includes('invalid')) { log.warning(`[${project.name}] unable to determine local changes, caching disabled`); return; } @@ -248,7 +264,7 @@ export async function getAllChecksums(kbn: Kibana, log: ToolingLog) { Array.from(projects.values()).map(async project => { cacheKeys.set( project.name, - await getChecksum(project, changesByProject.get(project)!, yarnLock, kbn, log) + await getChecksum(project, changesByProject.get(project), yarnLock, kbn, log) ); }) ); diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index 3aaaa47ead5b6..276a51c3a6a99 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -18,6 +18,7 @@ */ import { resolve } from 'path'; +import { inspect } from 'util'; import { run, createFlagError, Flags } from '@kbn/dev-utils'; import { FunctionalTestRunner } from './functional_test_runner'; @@ -86,7 +87,11 @@ export function runFtrCli() { } }; - process.on('unhandledRejection', err => teardown(err)); + process.on('unhandledRejection', err => + teardown( + err instanceof Error ? err : new Error(`non-Error type rejection value: ${inspect(err)}`) + ) + ); process.on('SIGTERM', () => teardown()); process.on('SIGINT', () => teardown()); diff --git a/src/core/public/chrome/chrome_service.mock.ts b/src/core/public/chrome/chrome_service.mock.ts index bd932c5961eca..89007461b63e6 100644 --- a/src/core/public/chrome/chrome_service.mock.ts +++ b/src/core/public/chrome/chrome_service.mock.ts @@ -61,8 +61,6 @@ const createStartContractMock = () => { getBrand$: jest.fn(), setIsVisible: jest.fn(), getIsVisible$: jest.fn(), - setIsCollapsed: jest.fn(), - getIsCollapsed$: jest.fn(), addApplicationClass: jest.fn(), removeApplicationClass: jest.fn(), getApplicationClasses$: jest.fn(), @@ -73,15 +71,16 @@ const createStartContractMock = () => { getHelpExtension$: jest.fn(), setHelpExtension: jest.fn(), setHelpSupportUrl: jest.fn(), + getIsNavDrawerLocked$: jest.fn(), }; startContract.navLinks.getAll.mockReturnValue([]); startContract.getBrand$.mockReturnValue(new BehaviorSubject({} as ChromeBrand)); startContract.getIsVisible$.mockReturnValue(new BehaviorSubject(false)); - startContract.getIsCollapsed$.mockReturnValue(new BehaviorSubject(false)); startContract.getApplicationClasses$.mockReturnValue(new BehaviorSubject(['class-name'])); startContract.getBadge$.mockReturnValue(new BehaviorSubject({} as ChromeBadge)); startContract.getBreadcrumbs$.mockReturnValue(new BehaviorSubject([{} as ChromeBreadcrumb])); startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined)); + startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false)); return startContract; }; diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 9018b21973634..bf531aaa00fac 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -259,40 +259,6 @@ describe('start', () => { }); }); - describe('is collapsed', () => { - it('updates/emits isCollapsed', async () => { - const { chrome, service } = await start(); - const promise = chrome - .getIsCollapsed$() - .pipe(toArray()) - .toPromise(); - - chrome.setIsCollapsed(true); - chrome.setIsCollapsed(false); - chrome.setIsCollapsed(true); - service.stop(); - - await expect(promise).resolves.toMatchInlineSnapshot(` - Array [ - false, - true, - false, - true, - ] - `); - }); - - it('only stores true in localStorage', async () => { - const { chrome } = await start(); - - chrome.setIsCollapsed(true); - expect(store.size).toBe(1); - - chrome.setIsCollapsed(false); - expect(store.size).toBe(0); - }); - }); - describe('application classes', () => { it('updates/emits the application classes', async () => { const { chrome, service } = await start(); @@ -442,12 +408,12 @@ describe('start', () => { }); describe('stop', () => { - it('completes applicationClass$, isCollapsed$, breadcrumbs$, isVisible$, and brand$ observables', async () => { + it('completes applicationClass$, getIsNavDrawerLocked, breadcrumbs$, isVisible$, and brand$ observables', async () => { const { chrome, service } = await start(); const promise = Rx.combineLatest( chrome.getBrand$(), chrome.getApplicationClasses$(), - chrome.getIsCollapsed$(), + chrome.getIsNavDrawerLocked$(), chrome.getBreadcrumbs$(), chrome.getIsVisible$(), chrome.getHelpExtension$() @@ -465,7 +431,7 @@ describe('stop', () => { Rx.combineLatest( chrome.getBrand$(), chrome.getApplicationClasses$(), - chrome.getIsCollapsed$(), + chrome.getIsNavDrawerLocked$(), chrome.getBreadcrumbs$(), chrome.getIsVisible$(), chrome.getHelpExtension$() diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 2b0b115ce068e..7c9b644b8b984 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -34,14 +34,14 @@ import { ChromeNavLinks, NavLinksService } from './nav_links'; import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed'; import { NavControlsService, ChromeNavControls } from './nav_controls'; import { DocTitleService, ChromeDocTitle } from './doc_title'; -import { LoadingIndicator, HeaderWrapper as Header } from './ui'; +import { LoadingIndicator, Header } from './ui'; import { DocLinksStart } from '../doc_links'; import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu'; import { KIBANA_ASK_ELASTIC_LINK } from './constants'; import { IUiSettingsClient } from '../ui_settings'; export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle }; -const IS_COLLAPSED_KEY = 'core.chrome.isCollapsed'; +const IS_LOCKED_KEY = 'core.chrome.isLocked'; /** @public */ export interface ChromeBadge { @@ -146,18 +146,25 @@ export class ChromeService { const appTitle$ = new BehaviorSubject('Kibana'); const brand$ = new BehaviorSubject({}); - const isCollapsed$ = new BehaviorSubject(!!localStorage.getItem(IS_COLLAPSED_KEY)); const applicationClasses$ = new BehaviorSubject>(new Set()); const helpExtension$ = new BehaviorSubject(undefined); const breadcrumbs$ = new BehaviorSubject([]); const badge$ = new BehaviorSubject(undefined); const helpSupportUrl$ = new BehaviorSubject(KIBANA_ASK_ELASTIC_LINK); + const isNavDrawerLocked$ = new BehaviorSubject(localStorage.getItem(IS_LOCKED_KEY) === 'true'); const navControls = this.navControls.start(); const navLinks = this.navLinks.start({ application, http }); const recentlyAccessed = await this.recentlyAccessed.start({ http }); const docTitle = this.docTitle.start({ document: window.document }); + const setIsNavDrawerLocked = (isLocked: boolean) => { + isNavDrawerLocked$.next(isLocked); + localStorage.setItem(IS_LOCKED_KEY, `${isLocked}`); + }; + + const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$)); + if (!this.params.browserSupportsCsp && injectedMetadata.getCspConfig().warnLegacyBrowsers) { notifications.toasts.addWarning( i18n.translate('core.chrome.legacyBrowserWarning', { @@ -193,6 +200,8 @@ export class ChromeService { recentlyAccessed$={recentlyAccessed.get$()} navControlsLeft$={navControls.getLeft$()} navControlsRight$={navControls.getRight$()} + onIsLockedUpdate={setIsNavDrawerLocked} + isLocked$={getIsNavDrawerLocked$} /> ), @@ -214,17 +223,6 @@ export class ChromeService { setIsVisible: (isVisible: boolean) => this.toggleHidden$.next(!isVisible), - getIsCollapsed$: () => isCollapsed$.pipe(takeUntil(this.stop$)), - - setIsCollapsed: (isCollapsed: boolean) => { - isCollapsed$.next(isCollapsed); - if (isCollapsed) { - localStorage.setItem(IS_COLLAPSED_KEY, 'true'); - } else { - localStorage.removeItem(IS_COLLAPSED_KEY); - } - }, - getApplicationClasses$: () => applicationClasses$.pipe( map(set => [...set]), @@ -262,6 +260,8 @@ export class ChromeService { }, setHelpSupportUrl: (url: string) => helpSupportUrl$.next(url), + + getIsNavDrawerLocked$: () => getIsNavDrawerLocked$, }; } @@ -353,16 +353,6 @@ export interface ChromeStart { */ setIsVisible(isVisible: boolean): void; - /** - * Get an observable of the current collapsed state of the chrome. - */ - getIsCollapsed$(): Observable; - - /** - * Set the collapsed state of the chrome navigation. - */ - setIsCollapsed(isCollapsed: boolean): void; - /** * Get the current set of classNames that will be set on the application container. */ @@ -413,6 +403,11 @@ export interface ChromeStart { * @param url The updated support URL */ setHelpSupportUrl(url: string): void; + + /** + * Get an observable of the current locked state of the nav drawer. + */ + getIsNavDrawerLocked$(): Observable; } /** @internal */ diff --git a/src/core/public/chrome/ui/_loading_indicator.scss b/src/core/public/chrome/ui/_loading_indicator.scss index 80694347393ce..026c23b93b040 100644 --- a/src/core/public/chrome/ui/_loading_indicator.scss +++ b/src/core/public/chrome/ui/_loading_indicator.scss @@ -22,29 +22,34 @@ $kbnLoadingIndicatorColor2: tint($euiColorAccent, 60%); } } - .kbnLoadingIndicator__bar { - top: 0; - left: 0; - right: 0; - bottom: 0; - position: absolute; - z-index: $euiZLevel1 + 1; - visibility: visible; - display: block; - animation: kbn-animate-loading-indicator 2s linear infinite; - background-color: $kbnLoadingIndicatorColor2; - background-image: linear-gradient(to right, - $kbnLoadingIndicatorColor1 0%, - $kbnLoadingIndicatorColor1 50%, - $kbnLoadingIndicatorColor2 50%, - $kbnLoadingIndicatorColor2 100% - ); - background-repeat: repeat-x; - background-size: $kbnLoadingIndicatorBackgroundSize $kbnLoadingIndicatorBackgroundSize; - width: 200%; - } +.kbnLoadingIndicator__bar { + top: 0; + left: 0; + right: 0; + bottom: 0; + position: absolute; + z-index: $euiZLevel1 + 1; + visibility: visible; + display: block; + animation: kbn-animate-loading-indicator 2s linear infinite; + background-color: $kbnLoadingIndicatorColor2; + background-image: linear-gradient( + to right, + $kbnLoadingIndicatorColor1 0%, + $kbnLoadingIndicatorColor1 50%, + $kbnLoadingIndicatorColor2 50%, + $kbnLoadingIndicatorColor2 100% + ); + background-repeat: repeat-x; + background-size: $kbnLoadingIndicatorBackgroundSize $kbnLoadingIndicatorBackgroundSize; + width: 200%; +} - @keyframes kbn-animate-loading-indicator { - from { transform: translateX(0); } - to { transform: translateX(-$kbnLoadingIndicatorBackgroundSize); } +@keyframes kbn-animate-loading-indicator { + from { + transform: translateX(0); } + to { + transform: translateX(-$kbnLoadingIndicatorBackgroundSize); + } +} diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index c9a583f39b30c..4dec084fd8a83 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -30,6 +30,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Component, createRef } from 'react'; +import classnames from 'classnames'; import * as Rx from 'rxjs'; import { ChromeBadge, @@ -68,8 +69,8 @@ export interface HeaderProps { navControlsLeft$: Rx.Observable; navControlsRight$: Rx.Observable; basePath: HttpStart['basePath']; - isLocked?: boolean; - onIsLockedUpdate?: OnIsLockedUpdate; + isLocked$: Rx.Observable; + onIsLockedUpdate: OnIsLockedUpdate; } interface State { @@ -81,6 +82,7 @@ interface State { navControlsLeft: readonly ChromeNavControl[]; navControlsRight: readonly ChromeNavControl[]; currentAppId: string | undefined; + isLocked: boolean; } export class Header extends Component { @@ -99,6 +101,7 @@ export class Header extends Component { navControlsLeft: [], navControlsRight: [], currentAppId: '', + isLocked: false, }; } @@ -109,11 +112,12 @@ export class Header extends Component { this.props.forceAppSwitcherNavigation$, this.props.navLinks$, this.props.recentlyAccessed$, - // Types for combineLatest only handle up to 6 inferred types so we combine these two separately. + // Types for combineLatest only handle up to 6 inferred types so we combine these separately. Rx.combineLatest( this.props.navControlsLeft$, this.props.navControlsRight$, - this.props.application.currentAppId$ + this.props.application.currentAppId$, + this.props.isLocked$ ) ).subscribe({ next: ([ @@ -122,7 +126,7 @@ export class Header extends Component { forceNavigation, navLinks, recentlyAccessed, - [navControlsLeft, navControlsRight, currentAppId], + [navControlsLeft, navControlsRight, currentAppId, isLocked], ]) => { this.setState({ appTitle, @@ -133,6 +137,7 @@ export class Header extends Component { navControlsLeft, navControlsRight, currentAppId, + isLocked, }); }, }); @@ -181,8 +186,16 @@ export class Header extends Component { return null; } + const className = classnames( + 'chrHeaderWrapper', + { + 'chrHeaderWrapper--navIsLocked': this.state.isLocked, + }, + 'hide-for-sharing' + ); + return ( -
+
@@ -220,7 +233,7 @@ export class Header extends Component { = props => { - const initialIsLocked = localStorage.getItem(IS_LOCKED_KEY); - const [isLocked, setIsLocked] = useState(initialIsLocked === 'true'); - const setIsLockedStored = (locked: boolean) => { - localStorage.setItem(IS_LOCKED_KEY, `${locked}`); - setIsLocked(locked); - }; - const className = classnames( - 'chrHeaderWrapper', - { - 'chrHeaderWrapper--navIsLocked': isLocked, - }, - 'hide-for-sharing' - ); - return ( -
-
-
- ); -}; diff --git a/src/core/public/chrome/ui/header/index.ts b/src/core/public/chrome/ui/header/index.ts index 4521f1f74b31b..49e002a66d939 100644 --- a/src/core/public/chrome/ui/header/index.ts +++ b/src/core/public/chrome/ui/header/index.ts @@ -18,7 +18,6 @@ */ export { Header, HeaderProps } from './header'; -export { HeaderWrapper } from './header_wrapper'; export { ChromeHelpExtensionMenuLink, ChromeHelpExtensionMenuCustomLink, diff --git a/src/core/public/chrome/ui/index.ts b/src/core/public/chrome/ui/index.ts index 81b2fdfb0fcc0..460e19b7d9780 100644 --- a/src/core/public/chrome/ui/index.ts +++ b/src/core/public/chrome/ui/index.ts @@ -20,7 +20,6 @@ export { LoadingIndicator } from './loading_indicator'; export { Header, - HeaderWrapper, ChromeHelpExtensionMenuLink, ChromeHelpExtensionMenuCustomLink, ChromeHelpExtensionMenuDiscussLink, diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index fa5dc745e6931..7428280b2dccb 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -337,7 +337,7 @@ export interface ChromeStart { getBrand$(): Observable; getBreadcrumbs$(): Observable; getHelpExtension$(): Observable; - getIsCollapsed$(): Observable; + getIsNavDrawerLocked$(): Observable; getIsVisible$(): Observable; navControls: ChromeNavControls; navLinks: ChromeNavLinks; @@ -349,7 +349,6 @@ export interface ChromeStart { setBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]): void; setHelpExtension(helpExtension?: ChromeHelpExtension): void; setHelpSupportUrl(url: string): void; - setIsCollapsed(isCollapsed: boolean): void; setIsVisible(isVisible: boolean): void; } diff --git a/src/core/server/rendering/views/styles.tsx b/src/core/server/rendering/views/styles.tsx index 9ab9f2ad0d6b8..71b42e3464118 100644 --- a/src/core/server/rendering/views/styles.tsx +++ b/src/core/server/rendering/views/styles.tsx @@ -53,7 +53,7 @@ export const Styles: FunctionComponent = ({ darkMode }) => { .kbnWelcomeView { line-height: 1.5; - background-color: #FFF; + background-color: ${darkMode ? '#1D1E24' : '#FFF'}; height: 100%; display: -webkit-box; display: -webkit-flex; @@ -97,6 +97,7 @@ export const Styles: FunctionComponent = ({ darkMode }) => { line-height: 40px !important; height: 40px !important; color: #98a2b3; + color: ${darkMode ? '#98A2B3' : '#69707D'}; } .kbnLoaderWrap { @@ -128,7 +129,7 @@ export const Styles: FunctionComponent = ({ darkMode }) => { width: 32px; height: 4px; overflow: hidden; - background-color: #D3DAE6; + background-color: ${darkMode ? '#25262E' : '#F5F7FA'}; line-height: 1; } @@ -142,7 +143,7 @@ export const Styles: FunctionComponent = ({ darkMode }) => { left: 0; transform: scaleX(0) translateX(0%); animation: kbnProgress 1s cubic-bezier(.694, .0482, .335, 1) infinite; - background-color: #006DE4; + background-color: ${darkMode ? '#1BA9F5' : '#006DE4'}; } @keyframes kbnProgress { diff --git a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts index b497f73f3df2a..3f81bfe5aadf2 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/legacy_imports.ts @@ -33,7 +33,6 @@ export { IInjector } from 'ui/chrome'; export { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; export { configureAppAngularModule, - ensureDefaultIndexPattern, IPrivate, migrateLegacyQuery, PrivateProvider, diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js index f7baba663da75..64abbdfb87d58 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/legacy_app.js @@ -23,11 +23,11 @@ import dashboardTemplate from './dashboard_app.html'; import dashboardListingTemplate from './listing/dashboard_listing_ng_wrapper.html'; import { createHashHistory } from 'history'; -import { ensureDefaultIndexPattern } from '../legacy_imports'; import { initDashboardAppDirective } from './dashboard_app'; import { createDashboardEditUrl, DashboardConstants } from './dashboard_constants'; import { createKbnUrlStateStorage, + ensureDefaultIndexPattern, redirectWhenMissing, InvalidJSONProperty, SavedObjectNotFound, @@ -137,8 +137,8 @@ export function initDashboardApp(app, deps) { }); }, resolve: { - dash: function($rootScope, $route, kbnUrl, history) { - return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl).then(() => { + dash: function($route, history) { + return ensureDefaultIndexPattern(deps.core, deps.data, history).then(() => { const savedObjectsClient = deps.savedObjectsClient; const title = $route.current.params.title; if (title) { @@ -172,11 +172,9 @@ export function initDashboardApp(app, deps) { controller: createNewDashboardCtrl, requireUICapability: 'dashboard.createNew', resolve: { - dash: function($rootScope, kbnUrl, history) { - return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) - .then(() => { - return deps.savedDashboards.get(); - }) + dash: history => + ensureDefaultIndexPattern(deps.core, deps.data, history) + .then(() => deps.savedDashboards.get()) .catch( redirectWhenMissing({ history, @@ -185,8 +183,7 @@ export function initDashboardApp(app, deps) { }, toastNotifications: deps.core.notifications.toasts, }) - ); - }, + ), }, }) .when(createDashboardEditUrl(':id'), { @@ -194,13 +191,11 @@ export function initDashboardApp(app, deps) { template: dashboardTemplate, controller: createNewDashboardCtrl, resolve: { - dash: function($rootScope, $route, kbnUrl, history) { + dash: function($route, kbnUrl, history) { const id = $route.current.params.id; - return ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl) - .then(() => { - return deps.savedDashboards.get(id); - }) + return ensureDefaultIndexPattern(deps.core, deps.data, history) + .then(() => deps.savedDashboards.get(id)) .then(savedDashboard => { deps.chrome.recentlyAccessed.add( savedDashboard.getFullPath(), diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 8202ba13b30cc..725e94f16e2e8 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -53,17 +53,18 @@ export { wrapInI18nContext } from 'ui/i18n'; import { search } from '../../../../../plugins/data/public'; export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; // @ts-ignore -export { shortenDottedString } from '../../common/utils/shorten_dotted_string'; -// @ts-ignore export { intervalOptions } from 'ui/agg_types'; -export { subscribeWithScope } from '../../../../../plugins/kibana_legacy/public'; // @ts-ignore export { timezoneProvider } from 'ui/vis/lib/timezone'; -export { unhashUrl, redirectWhenMissing } from '../../../../../plugins/kibana_utils/public'; export { + unhashUrl, + redirectWhenMissing, ensureDefaultIndexPattern, +} from '../../../../../plugins/kibana_utils/public'; +export { formatMsg, formatStack, + subscribeWithScope, } from '../../../../../plugins/kibana_legacy/public'; // EXPORT types diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx index 26d8a5abb2471..1b3b16332fa4f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx @@ -21,7 +21,7 @@ import classNames from 'classnames'; import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { FieldIcon, FieldIconProps } from '../../../../../../../../../plugins/kibana_react/public'; -import { shortenDottedString } from '../../../../kibana_services'; +import { shortenDottedString } from '../../../helpers'; import { getFieldTypeName } from './field_type_name'; // property field is provided at discover's field chooser diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js index 6978781fe6696..9a383565f4f43 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js @@ -115,9 +115,9 @@ app.config($routeProvider => { template: indexTemplate, reloadOnSearch: false, resolve: { - savedObjects: function($route, kbnUrl, Promise, $rootScope) { + savedObjects: function($route, Promise) { const savedSearchId = $route.current.params.id; - return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl).then(() => { + return ensureDefaultIndexPattern(core, data, history).then(() => { const { appStateContainer } = getState({ history }); const { index } = appStateContainer.getState(); return Promise.props({ diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx index a2ad18d59d935..bd48b1e083871 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header/helpers.tsx @@ -16,7 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { IndexPattern, shortenDottedString } from '../../../../../kibana_services'; +import { IndexPattern } from '../../../../../kibana_services'; +import { shortenDottedString } from '../../../../helpers'; export type SortOrder = [string, string]; export interface ColumnProps { diff --git a/src/legacy/ui/public/chrome/services/index.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts similarity index 92% rename from src/legacy/ui/public/chrome/services/index.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts index 3b3967f51b2ff..7196c96989e97 100644 --- a/src/legacy/ui/public/chrome/services/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts @@ -17,4 +17,4 @@ * under the License. */ -import './global_nav_state'; +export { shortenDottedString } from './shorten_dotted_string'; diff --git a/src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts similarity index 81% rename from src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts index ca76a2a537742..9d78a96784339 100644 --- a/src/legacy/core_plugins/kibana/common/utils/shorten_dotted_string.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/shorten_dotted_string.ts @@ -22,10 +22,5 @@ const DOT_PREFIX_RE = /(.).+?\./g; /** * Convert a dot.notated.string into a short * version (d.n.string) - * - * @param {string} str - the long string to convert - * @return {string} */ -export function shortenDottedString(input) { - return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); -} +export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.'); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap index 805131042f385..a4dcfb9c38184 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/__jest__/__snapshots__/table.test.js.snap @@ -126,6 +126,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` Array [ Object { "align": "center", + "data-test-subj": "savedObjectsTableRowType", "description": "Type of the saved object", "field": "type", "name": "Type", @@ -134,6 +135,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` "width": "50px", }, Object { + "data-test-subj": "savedObjectsTableRowTitle", "dataType": "string", "description": "Title of the saved object", "field": "meta.title", @@ -145,6 +147,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` "actions": Array [ Object { "available": [Function], + "data-test-subj": "savedObjectsTableAction-inspect", "description": "Inspect this saved object", "icon": "inspect", "name": "Inspect", @@ -152,6 +155,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` "type": "icon", }, Object { + "data-test-subj": "savedObjectsTableAction-relationships", "description": "View the relationships this saved object has to other saved objects", "icon": "kqlSelector", "name": "Relationships", @@ -198,6 +202,7 @@ exports[`Table prevents saved objects from being deleted 1`] = ` } } responsive={true} + rowProps={[Function]} selection={ Object { "onSelectionChange": [Function], @@ -334,6 +339,7 @@ exports[`Table should render normally 1`] = ` Array [ Object { "align": "center", + "data-test-subj": "savedObjectsTableRowType", "description": "Type of the saved object", "field": "type", "name": "Type", @@ -342,6 +348,7 @@ exports[`Table should render normally 1`] = ` "width": "50px", }, Object { + "data-test-subj": "savedObjectsTableRowTitle", "dataType": "string", "description": "Title of the saved object", "field": "meta.title", @@ -353,6 +360,7 @@ exports[`Table should render normally 1`] = ` "actions": Array [ Object { "available": [Function], + "data-test-subj": "savedObjectsTableAction-inspect", "description": "Inspect this saved object", "icon": "inspect", "name": "Inspect", @@ -360,6 +368,7 @@ exports[`Table should render normally 1`] = ` "type": "icon", }, Object { + "data-test-subj": "savedObjectsTableAction-relationships", "description": "View the relationships this saved object has to other saved objects", "icon": "kqlSelector", "name": "Relationships", @@ -406,6 +415,7 @@ exports[`Table should render normally 1`] = ` } } responsive={true} + rowProps={[Function]} selection={ Object { "onSelectionChange": [Function], diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js index a119817fdc0c9..386b35399b754 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/table/table.js @@ -178,6 +178,7 @@ export class Table extends PureComponent { { defaultMessage: 'Type of the saved object' } ), sortable: false, + 'data-test-subj': 'savedObjectsTableRowType', render: (type, object) => { return ( @@ -201,6 +202,7 @@ export class Table extends PureComponent { ), dataType: 'string', sortable: false, + 'data-test-subj': 'savedObjectsTableRowTitle', render: (title, object) => { const { path } = object.meta.inAppUrl || {}; const canGoInApp = this.props.canGoInApp(object); @@ -230,6 +232,7 @@ export class Table extends PureComponent { icon: 'inspect', onClick: object => goInspectObject(object), available: object => !!object.meta.editUrl, + 'data-test-subj': 'savedObjectsTableAction-inspect', }, { name: i18n.translate( @@ -246,10 +249,12 @@ export class Table extends PureComponent { type: 'icon', icon: 'kqlSelector', onClick: object => onShowRelationships(object), + 'data-test-subj': 'savedObjectsTableAction-relationships', }, ...this.extraActions.map(action => { return { ...action.euiAction, + 'data-test-subj': `savedObjectsTableAction-${action.id}`, onClick: object => { this.setState({ activeAction: action, @@ -372,6 +377,9 @@ export class Table extends PureComponent { pagination={pagination} selection={selection} onChange={onTableChange} + rowProps={item => ({ + 'data-test-subj': `savedObjectsTableRow row-${item.id}`, + })} /> diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index 69af466a03729..e6b7a29e28d89 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -33,7 +33,6 @@ export { DashboardConstants } from '../dashboard/np_ready/dashboard_constants'; export { VisSavedObject, VISUALIZE_EMBEDDABLE_TYPE } from '../../../visualizations/public/'; export { configureAppAngularModule, - ensureDefaultIndexPattern, IPrivate, migrateLegacyQuery, PrivateProvider, diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index 1002f401706cd..0f1d50b149cd9 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -24,6 +24,7 @@ import { createHashHistory } from 'history'; import { createKbnUrlStateStorage, redirectWhenMissing, + ensureDefaultIndexPattern, } from '../../../../../../plugins/kibana_utils/public'; import editorTemplate from './editor/editor.html'; @@ -32,7 +33,6 @@ import visualizeListingTemplate from './listing/visualize_listing.html'; import { initVisualizeAppDirective } from './visualize_app'; import { VisualizeConstants } from './visualize_constants'; import { VisualizeListingController } from './listing/visualize_listing'; -import { ensureDefaultIndexPattern } from '../legacy_imports'; import { getLandingBreadcrumbs, @@ -82,8 +82,7 @@ export function initVisualizeApp(app, deps) { controllerAs: 'listingController', resolve: { createNewVis: () => false, - hasDefaultIndex: ($rootScope, kbnUrl) => - ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl), + hasDefaultIndex: history => ensureDefaultIndexPattern(deps.core, deps.data, history), }, }) .when(VisualizeConstants.WIZARD_STEP_1_PAGE_PATH, { @@ -94,8 +93,7 @@ export function initVisualizeApp(app, deps) { controllerAs: 'listingController', resolve: { createNewVis: () => true, - hasDefaultIndex: ($rootScope, kbnUrl) => - ensureDefaultIndexPattern(deps.core, deps.data, $rootScope, kbnUrl), + hasDefaultIndex: history => ensureDefaultIndexPattern(deps.core, deps.data, history), }, }) .when(VisualizeConstants.CREATE_PATH, { @@ -103,7 +101,7 @@ export function initVisualizeApp(app, deps) { template: editorTemplate, k7Breadcrumbs: getCreateBreadcrumbs, resolve: { - savedVis: function($route, $rootScope, kbnUrl, history) { + savedVis: function($route, history) { const { core, data, savedVisualizations, visualizations, toastNotifications } = deps; const visTypes = visualizations.all(); const visType = find(visTypes, { name: $route.current.params.type }); @@ -121,7 +119,7 @@ export function initVisualizeApp(app, deps) { ); } - return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl) + return ensureDefaultIndexPattern(core, data, history) .then(() => savedVisualizations.get($route.current.params)) .then(savedVis => { if (savedVis.vis.type.setup) { @@ -144,9 +142,9 @@ export function initVisualizeApp(app, deps) { template: editorTemplate, k7Breadcrumbs: getEditBreadcrumbs, resolve: { - savedVis: function($route, $rootScope, kbnUrl, history) { + savedVis: function($route, history) { const { chrome, core, data, savedVisualizations, toastNotifications } = deps; - return ensureDefaultIndexPattern(core, data, $rootScope, kbnUrl) + return ensureDefaultIndexPattern(core, data, history) .then(() => savedVisualizations.get($route.current.params.id)) .then(savedVis => { chrome.recentlyAccessed.add(savedVis.getFullPath(), savedVis.title, savedVis.id); diff --git a/src/legacy/ui/public/chrome/chrome.js b/src/legacy/ui/public/chrome/chrome.js index 3355870eabfe7..7a75ad906a870 100644 --- a/src/legacy/ui/public/chrome/chrome.js +++ b/src/legacy/ui/public/chrome/chrome.js @@ -28,7 +28,6 @@ import '../private'; import '../promises'; import '../directives/storage'; import '../directives/watch_multi'; -import './services'; import '../react_components'; import '../i18n'; diff --git a/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap b/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap index 27226eb010ba2..365f3afdab395 100644 --- a/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap +++ b/src/legacy/ui/public/exit_full_screen/__snapshots__/exit_full_screen_button.test.js.snap @@ -12,19 +12,45 @@ exports[`is rendered 1`] = ` diff --git a/src/legacy/ui/public/legacy_compat/index.ts b/src/legacy/ui/public/legacy_compat/index.ts index 3b700c8d59399..2067fa6489304 100644 --- a/src/legacy/ui/public/legacy_compat/index.ts +++ b/src/legacy/ui/public/legacy_compat/index.ts @@ -17,7 +17,4 @@ * under the License. */ -export { - configureAppAngularModule, - ensureDefaultIndexPattern, -} from '../../../../plugins/kibana_legacy/public'; +export { configureAppAngularModule } from '../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index ea84ba1ad2838..c58a7d2fbb5cd 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -21,13 +21,15 @@ import sinon from 'sinon'; import { getFieldFormatsRegistry } from '../../../../test_utils/public/stub_field_formats'; import { METRIC_TYPE } from '@kbn/analytics'; import { + setFieldFormats, setIndexPatterns, - setQueryService, - setUiSettings, setInjectedMetadata, - setFieldFormats, - setSearchService, + setHttp, + setNotifications, setOverlays, + setQueryService, + setSearchService, + setUiSettings, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../plugins/data/public/services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -477,11 +479,13 @@ export function __start__(coreStart) { // Services that need to be set in the legacy platform since the legacy data plugin // which previously provided them has been removed. + setHttp(npStart.core.http); + setNotifications(npStart.core.notifications); + setOverlays(npStart.core.overlays); setUiSettings(npStart.core.uiSettings); - setQueryService(npStart.plugins.data.query); - setIndexPatterns(npStart.plugins.data.indexPatterns); setFieldFormats(npStart.plugins.data.fieldFormats); + setIndexPatterns(npStart.plugins.data.indexPatterns); + setQueryService(npStart.plugins.data.query); setSearchService(npStart.plugins.data.search); setAggs(npStart.plugins.data.search.aggs); - setOverlays(npStart.core.overlays); } diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts index 498f05457bba9..dd41093f3a1f0 100644 --- a/src/legacy/ui/public/new_platform/new_platform.test.ts +++ b/src/legacy/ui/public/new_platform/new_platform.test.ts @@ -20,8 +20,19 @@ jest.mock('history'); import { setRootControllerMock, historyMock } from './new_platform.test.mocks'; -import { legacyAppRegister, __reset__, __setup__ } from './new_platform'; +import { + legacyAppRegister, + __reset__, + __setup__, + __start__, + PluginsSetup, + PluginsStart, +} from './new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import * as dataServices from '../../../../plugins/data/public/services'; +import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; import { coreMock } from '../../../../core/public/mocks'; +import { npSetup, npStart } from './__mocks__'; describe('ui/new_platform', () => { describe('legacyAppRegister', () => { @@ -108,4 +119,25 @@ describe('ui/new_platform', () => { expect(unmountMock).toHaveBeenCalled(); }); }); + + describe('service getters', () => { + const services: Record = dataServices; + const getters = Object.keys(services).filter(k => k.substring(0, 3) === 'get'); + + getters.forEach(g => { + it(`sets a value for ${g}`, () => { + __reset__(); + __setup__( + (coreMock.createSetup() as unknown) as LegacyCoreSetup, + (npSetup.plugins as unknown) as PluginsSetup + ); + __start__( + (coreMock.createStart() as unknown) as LegacyCoreStart, + (npStart.plugins as unknown) as PluginsStart + ); + + expect(services[g]()).toBeDefined(); + }); + }); + }); }); diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index 07e17ad562291..deb8387fee29c 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -31,13 +31,15 @@ import { } from '../../../../core/public'; import { Plugin as DataPlugin } from '../../../../plugins/data/public'; import { + setFieldFormats, setIndexPatterns, - setQueryService, - setUiSettings, setInjectedMetadata, - setFieldFormats, - setSearchService, + setHttp, + setNotifications, setOverlays, + setQueryService, + setSearchService, + setUiSettings, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../plugins/data/public/services'; import { Plugin as ExpressionsPlugin } from '../../../../plugins/expressions/public'; @@ -141,12 +143,14 @@ export function __start__(coreStart: LegacyCoreStart, plugins: PluginsStart) { // Services that need to be set in the legacy platform since the legacy data plugin // which previously provided them has been removed. + setHttp(npStart.core.http); + setNotifications(npStart.core.notifications); + setOverlays(npStart.core.overlays); setUiSettings(npStart.core.uiSettings); - setQueryService(npStart.plugins.data.query); - setIndexPatterns(npStart.plugins.data.indexPatterns); setFieldFormats(npStart.plugins.data.fieldFormats); + setIndexPatterns(npStart.plugins.data.indexPatterns); + setQueryService(npStart.plugins.data.query); setSearchService(npStart.plugins.data.search); - setOverlays(npStart.core.overlays); } /** Flag used to ensure `legacyAppRegister` is only called once. */ diff --git a/src/legacy/ui/public/vis/map/service_settings.js b/src/legacy/ui/public/vis/map/service_settings.js index 233ee526c439b..9f3d21831e3da 100644 --- a/src/legacy/ui/public/vis/map/service_settings.js +++ b/src/legacy/ui/public/vis/map/service_settings.js @@ -47,7 +47,8 @@ uiModules this._showZoomMessage = true; this._emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: kbnVersion, + appVersion: kbnVersion, + appName: 'kibana', fileApiUrl: mapConfig.emsFileApiUrl, tileApiUrl: mapConfig.emsTileApiUrl, htmlSanitizer: $sanitize, diff --git a/src/plugins/console/public/lib/kb/kb.js b/src/plugins/console/public/lib/kb/kb.js index 95896bed02988..053b82bd81d0a 100644 --- a/src/plugins/console/public/lib/kb/kb.js +++ b/src/plugins/console/public/lib/kb/kb.js @@ -147,13 +147,9 @@ function loadApisFromJson( } export function setActiveApi(api) { - if (_.isString(api)) { + if (!api) { $.ajax({ - url: - '../api/console/api_server?sense_version=' + - encodeURIComponent('@@SENSE_VERSION') + - '&apis=' + - encodeURIComponent(api), + url: '../api/console/api_server', dataType: 'json', // disable automatic guessing }).then( function(data) { @@ -169,7 +165,7 @@ export function setActiveApi(api) { ACTIVE_API = api; } -setActiveApi('es_6_0'); +setActiveApi(); export const _test = { loadApisFromJson: loadApisFromJson, diff --git a/src/plugins/console/server/lib/index.ts b/src/plugins/console/server/lib/index.ts index 98004768f880b..2347084b73a66 100644 --- a/src/plugins/console/server/lib/index.ts +++ b/src/plugins/console/server/lib/index.ts @@ -22,4 +22,4 @@ export { ProxyConfigCollection } from './proxy_config_collection'; export { proxyRequest } from './proxy_request'; export { getElasticsearchProxyConfig } from './elasticsearch_proxy_config'; export { setHeaders } from './set_headers'; -export { addProcessorDefinition, addExtensionSpecFilePath } from './spec_definitions'; +export { addProcessorDefinition, addExtensionSpecFilePath, loadSpec } from './spec_definitions'; diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0.js b/src/plugins/console/server/lib/spec_definitions/es.js similarity index 54% rename from src/plugins/console/server/lib/spec_definitions/es_6_0.js rename to src/plugins/console/server/lib/spec_definitions/es.js index 171d232407956..fc24a64f8a6f4 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0.js +++ b/src/plugins/console/server/lib/spec_definitions/es.js @@ -18,26 +18,30 @@ */ import Api from './api'; -import { getSpec } from './spec'; -import { register } from './es_6_0/ingest'; -const ES_6_0 = new Api('es_6_0'); -const spec = getSpec(); +import { getSpec } from './json'; +import { register } from './js/ingest'; +const ES = new Api('es'); -// adding generated specs -Object.keys(spec).forEach(endpoint => { - ES_6_0.addEndpointDescription(endpoint, spec[endpoint]); -}); +export const loadSpec = () => { + const spec = getSpec(); -//adding globals and custom API definitions -require('./es_6_0/aliases')(ES_6_0); -require('./es_6_0/aggregations')(ES_6_0); -require('./es_6_0/document')(ES_6_0); -require('./es_6_0/filter')(ES_6_0); -require('./es_6_0/globals')(ES_6_0); -register(ES_6_0); -require('./es_6_0/mappings')(ES_6_0); -require('./es_6_0/query')(ES_6_0); -require('./es_6_0/reindex')(ES_6_0); -require('./es_6_0/search')(ES_6_0); + // adding generated specs + Object.keys(spec).forEach(endpoint => { + ES.addEndpointDescription(endpoint, spec[endpoint]); + }); -export default ES_6_0; + // adding globals and custom API definitions + require('./js/aliases')(ES); + require('./js/aggregations')(ES); + require('./js/document')(ES); + require('./js/filter')(ES); + require('./js/globals')(ES); + register(ES); + require('./js/mappings')(ES); + require('./js/settings')(ES); + require('./js/query')(ES); + require('./js/reindex')(ES); + require('./js/search')(ES); +}; + +export default ES; diff --git a/src/plugins/console/server/lib/spec_definitions/index.d.ts b/src/plugins/console/server/lib/spec_definitions/index.d.ts index 0a79d3fb386f1..da0125a186c15 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.d.ts +++ b/src/plugins/console/server/lib/spec_definitions/index.d.ts @@ -19,6 +19,13 @@ export declare function addProcessorDefinition(...args: any[]): any; -export declare function resolveApi(senseVersion: string, apis: string[]): object; +export declare function resolveApi(): object; export declare function addExtensionSpecFilePath(...args: any[]): any; + +/** + * A function that synchronously reads files JSON from disk and builds + * the autocomplete structures served to the client. This must be called + * after any extensions have been loaded. + */ +export declare function loadSpec(): any; diff --git a/src/plugins/console/server/lib/spec_definitions/index.js b/src/plugins/console/server/lib/spec_definitions/index.js index 3fe1913d5a193..abf55639fbee8 100644 --- a/src/plugins/console/server/lib/spec_definitions/index.js +++ b/src/plugins/console/server/lib/spec_definitions/index.js @@ -17,8 +17,10 @@ * under the License. */ -export { addProcessorDefinition } from './es_6_0/ingest'; +export { addProcessorDefinition } from './js/ingest'; -export { addExtensionSpecFilePath } from './spec'; +export { addExtensionSpecFilePath } from './json'; + +export { loadSpec } from './es'; export { resolveApi } from './server'; diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/aggregations.js b/src/plugins/console/server/lib/spec_definitions/js/aggregations.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/aggregations.js rename to src/plugins/console/server/lib/spec_definitions/js/aggregations.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/aliases.js b/src/plugins/console/server/lib/spec_definitions/js/aliases.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/aliases.js rename to src/plugins/console/server/lib/spec_definitions/js/aliases.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/document.js b/src/plugins/console/server/lib/spec_definitions/js/document.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/document.js rename to src/plugins/console/server/lib/spec_definitions/js/document.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/filter.js b/src/plugins/console/server/lib/spec_definitions/js/filter.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/filter.js rename to src/plugins/console/server/lib/spec_definitions/js/filter.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/globals.js b/src/plugins/console/server/lib/spec_definitions/js/globals.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/globals.js rename to src/plugins/console/server/lib/spec_definitions/js/globals.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/ingest.js b/src/plugins/console/server/lib/spec_definitions/js/ingest.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/ingest.js rename to src/plugins/console/server/lib/spec_definitions/js/ingest.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js b/src/plugins/console/server/lib/spec_definitions/js/mappings.js similarity index 99% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js rename to src/plugins/console/server/lib/spec_definitions/js/mappings.js index 8c31e5bc6fbb2..5884d14d4dc8b 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/mappings.js +++ b/src/plugins/console/server/lib/spec_definitions/js/mappings.js @@ -19,9 +19,7 @@ const _ = require('lodash'); -const BOOLEAN = { - __one_of: [true, false], -}; +import { BOOLEAN } from './shared'; export default function(api) { api.addEndpointDescription('put_mapping', { diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/js/query/dsl.js similarity index 99% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js rename to src/plugins/console/server/lib/spec_definitions/js/query/dsl.js index a5f0d15dee0e9..16b952fe0fe4f 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js +++ b/src/plugins/console/server/lib/spec_definitions/js/query/dsl.js @@ -281,9 +281,11 @@ export function queryDsl(api) { __scope_link: '.', }, ], - filter: { - __scope_link: 'GLOBAL.filter', - }, + filter: [ + { + __scope_link: 'GLOBAL.filter', + }, + ], minimum_should_match: 1, boost: 1.0, }, diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/index.js b/src/plugins/console/server/lib/spec_definitions/js/query/index.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/index.js rename to src/plugins/console/server/lib/spec_definitions/js/query/index.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/templates.js b/src/plugins/console/server/lib/spec_definitions/js/query/templates.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/query/templates.js rename to src/plugins/console/server/lib/spec_definitions/js/query/templates.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/reindex.js b/src/plugins/console/server/lib/spec_definitions/js/reindex.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/reindex.js rename to src/plugins/console/server/lib/spec_definitions/js/reindex.js diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/search.js b/src/plugins/console/server/lib/spec_definitions/js/search.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/es_6_0/search.js rename to src/plugins/console/server/lib/spec_definitions/js/search.js diff --git a/src/plugins/console/server/lib/spec_definitions/js/settings.js b/src/plugins/console/server/lib/spec_definitions/js/settings.js new file mode 100644 index 0000000000000..26cd0987c34a5 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/js/settings.js @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { BOOLEAN } from './shared'; + +export default function(api) { + api.addEndpointDescription('put_settings', { + data_autocomplete_rules: { + refresh_interval: '1s', + number_of_shards: 1, + number_of_replicas: 1, + 'blocks.read_only': BOOLEAN, + 'blocks.read': BOOLEAN, + 'blocks.write': BOOLEAN, + 'blocks.metadata': BOOLEAN, + term_index_interval: 32, + term_index_divisor: 1, + 'translog.flush_threshold_ops': 5000, + 'translog.flush_threshold_size': '200mb', + 'translog.flush_threshold_period': '30m', + 'translog.disable_flush': BOOLEAN, + 'cache.filter.max_size': '2gb', + 'cache.filter.expire': '2h', + 'gateway.snapshot_interval': '10s', + routing: { + allocation: { + include: { + tag: '', + }, + exclude: { + tag: '', + }, + require: { + tag: '', + }, + total_shards_per_node: -1, + }, + }, + 'recovery.initial_shards': { + __one_of: ['quorum', 'quorum-1', 'half', 'full', 'full-1'], + }, + 'ttl.disable_purge': BOOLEAN, + analysis: { + analyzer: {}, + tokenizer: {}, + filter: {}, + char_filter: {}, + }, + 'cache.query.enable': BOOLEAN, + shadow_replicas: BOOLEAN, + shared_filesystem: BOOLEAN, + data_path: 'path', + codec: { + __one_of: ['default', 'best_compression', 'lucene_default'], + }, + }, + }); +} diff --git a/src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js b/src/plugins/console/server/lib/spec_definitions/js/shared.js similarity index 60% rename from src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js rename to src/plugins/console/server/lib/spec_definitions/js/shared.js index 267ca74c7c42a..ace189e2d0913 100644 --- a/src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js +++ b/src/plugins/console/server/lib/spec_definitions/js/shared.js @@ -17,18 +17,6 @@ * under the License. */ -import expect from '@kbn/expect'; -import { shortenDottedString } from '../shorten_dotted_string'; - -describe('shortenDottedString', () => { - it('Convert a dot.notated.string into a short string', () => { - expect(shortenDottedString('dot.notated.string')).to.equal('d.n.string'); - }); - - it('Ignores non-string values', () => { - expect(shortenDottedString(true)).to.equal(true); - expect(shortenDottedString(123)).to.equal(123); - const obj = { key: 'val' }; - expect(shortenDottedString(obj)).to.equal(obj); - }); +export const BOOLEAN = Object.freeze({ + __one_of: [true, false], }); diff --git a/src/plugins/console/server/lib/spec_definitions/spec/.eslintrc b/src/plugins/console/server/lib/spec_definitions/json/.eslintrc similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/.eslintrc rename to src/plugins/console/server/lib/spec_definitions/json/.eslintrc diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/_common.json b/src/plugins/console/server/lib/spec_definitions/json/generated/_common.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/_common.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/_common.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/bulk.json b/src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/bulk.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/bulk.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.aliases.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.allocation.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.allocation.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.allocation.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.allocation.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.count.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.count.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.fielddata.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.fielddata.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.fielddata.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.fielddata.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.health.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.health.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.help.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.help.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.help.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.help.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.indices.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.indices.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.indices.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.master.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.master.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.master.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.master.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodeattrs.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodeattrs.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodeattrs.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodeattrs.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodes.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.nodes.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.nodes.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.pending_tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.pending_tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.pending_tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.pending_tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.plugins.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.plugins.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.plugins.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.recovery.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.recovery.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.recovery.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.recovery.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.repositories.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.repositories.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.repositories.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.repositories.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.segments.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.segments.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.segments.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.segments.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.shards.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.shards.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.shards.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.snapshots.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.snapshots.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.snapshots.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.snapshots.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.templates.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.templates.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.templates.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.templates.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cat.thread_pool.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cat.thread_pool.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cat.thread_pool.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/clear_scroll.json b/src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/clear_scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/clear_scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.allocation_explain.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.allocation_explain.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.allocation_explain.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.allocation_explain.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.get_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.get_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.get_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.health.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.health.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.pending_tasks.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.pending_tasks.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.pending_tasks.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.pending_tasks.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.remote_info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.remote_info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.remote_info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.remote_info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.reroute.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.reroute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.reroute.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.reroute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.state.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.state.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.state.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.state.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/cluster.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/cluster.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/cluster.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/count.json b/src/plugins/console/server/lib/spec_definitions/json/generated/count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/count.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_by_query_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_by_query_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/delete_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/delete_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/delete_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/delete_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/exists.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/exists.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/exists.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/exists_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/exists_source.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/exists_source.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/explain.json b/src/plugins/console/server/lib/spec_definitions/json/generated/explain.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/explain.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/explain.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/field_caps.json b/src/plugins/console/server/lib/spec_definitions/json/generated/field_caps.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/field_caps.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/field_caps.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_context.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_context.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script_context.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_languages.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_script_languages.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_script_languages.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/get_source.json b/src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/get_source.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/get_source.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/index.json b/src/plugins/console/server/lib/spec_definitions/json/generated/index.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/index.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/index.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.analyze.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.analyze.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.analyze.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.analyze.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clear_cache.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.clear_cache.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clear_cache.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.clear_cache.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clone.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.clone.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.clone.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.clone.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.close.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.close.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.close.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.close.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.delete_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.delete_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_type.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_type.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.exists_type.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.exists_type.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush_synced.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush_synced.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.flush_synced.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.flush_synced.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.forcemerge.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.forcemerge.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.forcemerge.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.forcemerge.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_field_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_field_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_field_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_upgrade.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.get_upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.get_upgrade.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.open.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.open.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.open.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.open.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_alias.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.put_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.put_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.recovery.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.recovery.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.recovery.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.recovery.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.refresh.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.refresh.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.refresh.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.refresh.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.rollover.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.rollover.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.rollover.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.segments.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.segments.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.segments.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.segments.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shard_stores.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shard_stores.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shard_stores.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.shard_stores.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shrink.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.shrink.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.shrink.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.split.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.split.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.split.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.update_aliases.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.update_aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.update_aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.update_aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.upgrade.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.upgrade.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.upgrade.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.upgrade.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/indices.validate_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/indices.validate_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/indices.validate_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/indices.validate_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.delete_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.delete_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.delete_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.delete_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.get_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.get_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.get_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.processor_grok.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.processor_grok.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.processor_grok.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.processor_grok.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.put_pipeline.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.put_pipeline.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.put_pipeline.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.put_pipeline.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.simulate.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ingest.simulate.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ingest.simulate.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ingest.simulate.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/mget.json b/src/plugins/console/server/lib/spec_definitions/json/generated/mget.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/mget.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/mget.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/msearch.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/msearch.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/msearch.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/msearch_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/msearch_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/msearch_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/mtermvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/mtermvectors.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/mtermvectors.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.hot_threads.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.hot_threads.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.hot_threads.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.hot_threads.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.info.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.info.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.info.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.info.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.reload_secure_settings.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.reload_secure_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.reload_secure_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.reload_secure_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.stats.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.stats.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.stats.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.usage.json b/src/plugins/console/server/lib/spec_definitions/json/generated/nodes.usage.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/nodes.usage.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/nodes.usage.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/ping.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/ping.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/ping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/put_script.json b/src/plugins/console/server/lib/spec_definitions/json/generated/put_script.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/put_script.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/put_script.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/rank_eval.json b/src/plugins/console/server/lib/spec_definitions/json/generated/rank_eval.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/rank_eval.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/rank_eval.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/reindex.json b/src/plugins/console/server/lib/spec_definitions/json/generated/reindex.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/reindex.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/reindex.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/reindex_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/reindex_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/reindex_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/reindex_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/render_search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/render_search_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/render_search_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/scripts_painless_execute.json b/src/plugins/console/server/lib/spec_definitions/json/generated/scripts_painless_execute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/scripts_painless_execute.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/scripts_painless_execute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/scroll.json b/src/plugins/console/server/lib/spec_definitions/json/generated/scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search_shards.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search_shards.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search_shards.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search_shards.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/search_template.json b/src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/search_template.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/search_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.cleanup_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.cleanup_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.cleanup_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.create_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.create_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.delete_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.delete_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.get_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.get_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.restore.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.restore.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.restore.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.restore.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.status.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.status.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.status.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.status.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.verify_repository.json b/src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.verify_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/snapshot.verify_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/snapshot.verify_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.cancel.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.cancel.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.cancel.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.get.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.get.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.get.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.get.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.list.json b/src/plugins/console/server/lib/spec_definitions/json/generated/tasks.list.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/tasks.list.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/tasks.list.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/termvectors.json b/src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/termvectors.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/termvectors.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query_rethrottle.json b/src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query_rethrottle.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/generated/update_by_query_rethrottle.json rename to src/plugins/console/server/lib/spec_definitions/json/generated/update_by_query_rethrottle.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/index.js b/src/plugins/console/server/lib/spec_definitions/json/index.js similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/index.js rename to src/plugins/console/server/lib/spec_definitions/json/index.js diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/clear_scroll.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/clear_scroll.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/clear_scroll.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/clear_scroll.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.health.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.health.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.health.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.health.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.put_settings.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.reroute.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.reroute.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/cluster.reroute.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.reroute.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/count.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/count.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/count.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/count.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.analyze.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.analyze.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.analyze.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.analyze.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.clone.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.clone.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.clone.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.clone.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.create.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.create.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.delete_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.delete_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.delete_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.delete_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.exists_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.exists_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.exists_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.exists_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_field_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_field_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_field_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_field_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_mapping.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_mapping.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_mapping.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_mapping.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.get_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.get_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_alias.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_alias.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_alias.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_alias.json diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json new file mode 100644 index 0000000000000..2ae8fd82be4d8 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json @@ -0,0 +1,7 @@ +{ + "indices.put_settings": { + "data_autocomplete_rules": { + "__scope_link": "put_settings" + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_template.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_template.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_template.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_template.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.rollover.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.rollover.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.rollover.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.rollover.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.update_aliases.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.update_aliases.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.update_aliases.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.update_aliases.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.validate_query.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/indices.validate_query.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.validate_query.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/indices.validate_query.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create_repository.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.create_repository.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.create_repository.json diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.restore.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.restore.json similarity index 100% rename from src/plugins/console/server/lib/spec_definitions/spec/overrides/snapshot.restore.json rename to src/plugins/console/server/lib/spec_definitions/json/overrides/snapshot.restore.json diff --git a/src/plugins/console/server/lib/spec_definitions/server.js b/src/plugins/console/server/lib/spec_definitions/server.js index dd700bf019507..cb855958d403a 100644 --- a/src/plugins/console/server/lib/spec_definitions/server.js +++ b/src/plugins/console/server/lib/spec_definitions/server.js @@ -17,21 +17,10 @@ * under the License. */ -import _ from 'lodash'; +import es from './es'; -const KNOWN_APIS = ['es_6_0']; - -export function resolveApi(senseVersion, apis) { - const result = {}; - _.each(apis, function(name) { - { - if (KNOWN_APIS.includes(name)) { - // for now we ignore sense_version. might add it in the api name later - const api = require('./' + name); // eslint-disable-line import/no-dynamic-require - result[name] = api.asJson(); - } - } - }); - - return result; +export function resolveApi() { + return { + es: es.asJson(), + }; } diff --git a/src/plugins/console/server/lib/spec_definitions/server.test.js b/src/plugins/console/server/lib/spec_definitions/server.test.js deleted file mode 100644 index 747689237c177..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/server.test.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { resolveApi } from './server'; - -describe('resolveApi', () => { - it('allows known APIs to be resolved', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['es_6_0'], { response: mockReply }); - expect(result).toMatchObject({ - es_6_0: { - endpoints: expect.any(Object), - globals: expect.any(Object), - name: expect.any(String), - }, - }); - }); - - it('does not resolve APIs that are not known', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['unknown'], { response: mockReply }); - expect(result).toEqual({}); - }); - - it('handles request for apis that are known and unknown', () => { - const mockReply = jest.fn(result => ({ type: () => result })); - const result = resolveApi('Sense Version', ['es_6_0'], { response: mockReply }); - expect(result).toMatchObject({ - es_6_0: { - endpoints: expect.any(Object), - globals: expect.any(Object), - name: expect.any(String), - }, - }); - }); -}); diff --git a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json b/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json deleted file mode 100644 index 2e1e3024665a4..0000000000000 --- a/src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "indices.put_settings": { - "data_autocomplete_rules": { - "refresh_interval": "1s", - "number_of_shards": 1, - "number_of_replicas": 1, - "blocks.read_only": { - "__one_of": [ - false, - true - ] - }, - "blocks.read": { - "__one_of": [ - true, - false - ] - }, - "blocks.write": { - "__one_of": [ - true, - false - ] - }, - "blocks.metadata": { - "__one_of": [ - true, - false - ] - }, - "term_index_interval": 32, - "term_index_divisor": 1, - "translog.flush_threshold_ops": 5000, - "translog.flush_threshold_size": "200mb", - "translog.flush_threshold_period": "30m", - "translog.disable_flush": { - "__one_of": [ - true, - false - ] - }, - "cache.filter.max_size": "2gb", - "cache.filter.expire": "2h", - "gateway.snapshot_interval": "10s", - "routing": { - "allocation": { - "include": { - "tag": "" - }, - "exclude": { - "tag": "" - }, - "require": { - "tag": "" - }, - "total_shards_per_node": -1 - } - }, - "recovery.initial_shards": { - "__one_of": [ - "quorum", - "quorum-1", - "half", - "full", - "full-1" - ] - }, - "ttl.disable_purge": { - "__one_of": [ - true, - false - ] - }, - "analysis": { - "analyzer": {}, - "tokenizer": {}, - "filter": {}, - "char_filter": {} - }, - "cache.query.enable": { - "__one_of": [ - true, - false - ] - }, - "shadow_replicas": { - "__one_of": [ - true, - false - ] - }, - "shared_filesystem": { - "__one_of": [ - true, - false - ] - }, - "data_path": "path", - "codec": { - "__one_of": [ - "default", - "best_compression", - "lucene_default" - ] - } - } - } -} diff --git a/src/plugins/console/server/plugin.ts b/src/plugins/console/server/plugin.ts index 65647bd5acb7c..1954918f4d74f 100644 --- a/src/plugins/console/server/plugin.ts +++ b/src/plugins/console/server/plugin.ts @@ -21,7 +21,12 @@ import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/serv import { readLegacyEsConfig } from '../../../legacy/core_plugins/console_legacy'; -import { ProxyConfigCollection, addExtensionSpecFilePath, addProcessorDefinition } from './lib'; +import { + ProxyConfigCollection, + addExtensionSpecFilePath, + addProcessorDefinition, + loadSpec, +} from './lib'; import { ConfigType } from './config'; import { registerProxyRoute } from './routes/api/console/proxy'; import { registerSpecDefinitionsRoute } from './routes/api/console/spec_definitions'; @@ -75,5 +80,7 @@ export class ConsoleServerPlugin implements Plugin { }; } - start() {} + start() { + loadSpec(); + } } diff --git a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts index e2ece37f407ac..88bc250bbfce6 100644 --- a/src/plugins/console/server/routes/api/console/spec_definitions/index.ts +++ b/src/plugins/console/server/routes/api/console/spec_definitions/index.ts @@ -16,33 +16,19 @@ * specific language governing permissions and limitations * under the License. */ -import { schema, TypeOf } from '@kbn/config-schema'; import { IRouter, RequestHandler } from 'kibana/server'; import { resolveApi } from '../../../../lib/spec_definitions'; export const registerSpecDefinitionsRoute = ({ router }: { router: IRouter }) => { - const handler: RequestHandler> = async ( - ctx, - request, - response - ) => { - const { sense_version: version, apis } = request.query; - + const handler: RequestHandler = async (ctx, request, response) => { return response.ok({ - body: resolveApi(version, apis.split(',')), + body: resolveApi(), headers: { 'Content-Type': 'application/json', }, }); }; - const validate = { - query: schema.object({ - sense_version: schema.string({ defaultValue: '' }), - apis: schema.string(), - }), - }; - - router.get({ path: '/api/console/api_server', validate }, handler); - router.post({ path: '/api/console/api_server', validate }, handler); + router.get({ path: '/api/console/api_server', validate: false }, handler); + router.post({ path: '/api/console/api_server', validate: false }, handler); }; diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts b/src/plugins/data/common/query/filter_manager/compare_filters.test.ts similarity index 99% rename from src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts rename to src/plugins/data/common/query/filter_manager/compare_filters.test.ts index da8f5b3564948..b0bb2f754d6cf 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts +++ b/src/plugins/data/common/query/filter_manager/compare_filters.test.ts @@ -18,7 +18,7 @@ */ import { compareFilters, COMPARE_ALL_OPTIONS } from './compare_filters'; -import { buildEmptyFilter, buildQueryFilter, FilterStateStore } from '../../../../common'; +import { buildEmptyFilter, buildQueryFilter, FilterStateStore } from '../../es_query'; describe('filter manager utilities', () => { describe('compare filters', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts b/src/plugins/data/common/query/filter_manager/compare_filters.ts similarity index 98% rename from src/plugins/data/public/query/filter_manager/lib/compare_filters.ts rename to src/plugins/data/common/query/filter_manager/compare_filters.ts index a2105fdc1d3ef..e047d5e0665d5 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts +++ b/src/plugins/data/common/query/filter_manager/compare_filters.ts @@ -18,7 +18,7 @@ */ import { defaults, isEqual, omit, map } from 'lodash'; -import { FilterMeta, Filter } from '../../../../common'; +import { FilterMeta, Filter } from '../../es_query'; export interface FilterCompareOptions { disabled?: boolean; diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts b/src/plugins/data/common/query/filter_manager/dedup_filters.test.ts similarity index 95% rename from src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts rename to src/plugins/data/common/query/filter_manager/dedup_filters.test.ts index ecc0ec94e07c8..228489de37daa 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts +++ b/src/plugins/data/common/query/filter_manager/dedup_filters.test.ts @@ -18,14 +18,8 @@ */ import { dedupFilters } from './dedup_filters'; -import { - Filter, - IIndexPattern, - IFieldType, - buildRangeFilter, - buildQueryFilter, - FilterStateStore, -} from '../../../../common'; +import { Filter, buildRangeFilter, buildQueryFilter, FilterStateStore } from '../../es_query'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; describe('filter manager utilities', () => { let indexPattern: IIndexPattern; diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts b/src/plugins/data/common/query/filter_manager/dedup_filters.ts similarity index 97% rename from src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts rename to src/plugins/data/common/query/filter_manager/dedup_filters.ts index d5d0e70504b41..7d1b00ac10c0d 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts +++ b/src/plugins/data/common/query/filter_manager/dedup_filters.ts @@ -19,7 +19,7 @@ import { filter, find } from 'lodash'; import { compareFilters, FilterCompareOptions } from './compare_filters'; -import { Filter } from '../../../../common'; +import { Filter } from '../../es_query'; /** * Combine 2 filter collections, removing duplicates diff --git a/src/legacy/ui/public/chrome/services/global_nav_state.js b/src/plugins/data/common/query/filter_manager/index.ts similarity index 54% rename from src/legacy/ui/public/chrome/services/global_nav_state.js rename to src/plugins/data/common/query/filter_manager/index.ts index 5a67806852fe8..315c124f083a8 100644 --- a/src/legacy/ui/public/chrome/services/global_nav_state.js +++ b/src/plugins/data/common/query/filter_manager/index.ts @@ -17,29 +17,6 @@ * under the License. */ -import { distinctUntilChanged } from 'rxjs/operators'; -import { npStart } from 'ui/new_platform'; -import { uiModules } from '../../modules'; - -const newPlatformChrome = npStart.core.chrome; - -uiModules.get('kibana').service('globalNavState', $rootScope => { - let isOpen = false; - newPlatformChrome - .getIsCollapsed$() - .pipe(distinctUntilChanged()) - .subscribe(isCollapsed => { - $rootScope.$evalAsync(() => { - isOpen = !isCollapsed; - $rootScope.$broadcast('globalNavState:change'); - }); - }); - - return { - isOpen: () => isOpen, - - setOpen: newValue => { - newPlatformChrome.setIsCollapsed(!newValue); - }, - }; -}); +export { dedupFilters } from './dedup_filters'; +export { uniqFilters } from './uniq_filters'; +export { compareFilters, COMPARE_ALL_OPTIONS, FilterCompareOptions } from './compare_filters'; diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts b/src/plugins/data/common/query/filter_manager/uniq_filters.test.ts similarity index 99% rename from src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts rename to src/plugins/data/common/query/filter_manager/uniq_filters.test.ts index 8b525a3d2a2e4..5a35e85c95eaa 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts +++ b/src/plugins/data/common/query/filter_manager/uniq_filters.test.ts @@ -18,7 +18,7 @@ */ import { uniqFilters } from './uniq_filters'; -import { buildQueryFilter, Filter, FilterStateStore } from '../../../../common'; +import { buildQueryFilter, Filter, FilterStateStore } from '../../es_query'; describe('filter manager utilities', () => { describe('niqFilter', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts b/src/plugins/data/common/query/filter_manager/uniq_filters.ts similarity index 96% rename from src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts rename to src/plugins/data/common/query/filter_manager/uniq_filters.ts index 44c102d7ab15d..683cbf7c78a89 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts +++ b/src/plugins/data/common/query/filter_manager/uniq_filters.ts @@ -17,8 +17,8 @@ * under the License. */ import { each, union } from 'lodash'; +import { Filter } from '../../es_query'; import { dedupFilters } from './dedup_filters'; -import { Filter } from '../../../../common'; /** * Remove duplicate filters from an array of filters diff --git a/src/plugins/data/common/query/index.ts b/src/plugins/data/common/query/index.ts index d8f7b5091eb8f..421cc4f63e4ef 100644 --- a/src/plugins/data/common/query/index.ts +++ b/src/plugins/data/common/query/index.ts @@ -17,4 +17,5 @@ * under the License. */ +export * from './filter_manager'; export * from './types'; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 58bd9a5ab05d7..339a5fea91c5f 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -47,13 +47,13 @@ import { isQueryStringFilter, isRangeFilter, toggleFilterNegated, + compareFilters, + COMPARE_ALL_OPTIONS, } from '../common'; import { FilterLabel } from './ui/filter_bar'; import { - compareFilters, - COMPARE_ALL_OPTIONS, generateFilters, onlyDisabledFiltersChanged, changeTimeFilter, diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index a01c133712206..fc5dde94fa851 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -39,6 +39,7 @@ import { createIndexPatternSelect } from './ui/index_pattern_select'; import { IndexPatternsService } from './index_patterns'; import { setFieldFormats, + setHttp, setIndexPatterns, setInjectedMetadata, setNotifications, @@ -128,6 +129,7 @@ export class DataPublicPlugin implements Plugin string; getPhraseFilterValue: (filter: import("../common").PhraseFilter) => string | number | boolean; getDisplayValueFromFilter: typeof getDisplayValueFromFilter; - compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("./query/filter_manager/lib/compare_filters").FilterCompareOptions) => boolean; - COMPARE_ALL_OPTIONS: import("./query/filter_manager/lib/compare_filters").FilterCompareOptions; + compareFilters: (first: import("../common").Filter | import("../common").Filter[], second: import("../common").Filter | import("../common").Filter[], comparatorOptions?: import("../common").FilterCompareOptions) => boolean; + COMPARE_ALL_OPTIONS: import("../common").FilterCompareOptions; generateFilters: typeof generateFilters; onlyDisabledFiltersChanged: (newFilters?: import("../common").Filter[] | undefined, oldFilters?: import("../common").Filter[] | undefined) => boolean; changeTimeFilter: typeof changeTimeFilter; @@ -1843,8 +1843,8 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:38:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromEvent" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:60:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index c951953b26555..fba1866ebd615 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -22,13 +22,19 @@ import { Subject } from 'rxjs'; import { IUiSettingsClient } from 'src/core/public'; -import { COMPARE_ALL_OPTIONS, compareFilters } from './lib/compare_filters'; import { sortFilters } from './lib/sort_filters'; import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; -import { uniqFilters } from './lib/uniq_filters'; import { onlyDisabledFiltersChanged } from './lib/only_disabled'; import { PartitionedFilters } from './types'; -import { FilterStateStore, Filter, isFilterPinned } from '../../../common'; + +import { + FilterStateStore, + Filter, + uniqFilters, + isFilterPinned, + compareFilters, + COMPARE_ALL_OPTIONS, +} from '../../../common'; export class FilterManager { private filters: Filter[] = []; diff --git a/src/plugins/data/public/query/filter_manager/index.ts b/src/plugins/data/public/query/filter_manager/index.ts index 09990adacde45..be512c503d531 100644 --- a/src/plugins/data/public/query/filter_manager/index.ts +++ b/src/plugins/data/public/query/filter_manager/index.ts @@ -19,8 +19,6 @@ export { FilterManager } from './filter_manager'; -export { uniqFilters } from './lib/uniq_filters'; export { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; export { onlyDisabledFiltersChanged } from './lib/only_disabled'; export { generateFilters } from './lib/generate_filters'; -export { compareFilters, COMPARE_ALL_OPTIONS } from './lib/compare_filters'; diff --git a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts index 34e1ac38ae95f..18c51ebeabe54 100644 --- a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts +++ b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts @@ -18,8 +18,7 @@ */ import { filter } from 'lodash'; -import { Filter } from '../../../../common'; -import { compareFilters, COMPARE_ALL_OPTIONS } from './compare_filters'; +import { Filter, compareFilters, COMPARE_ALL_OPTIONS } from '../../../../common'; const isEnabled = (f: Filter) => f && f.meta && !f.meta.disabled; diff --git a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts index a22e66860c765..331d8969f2483 100644 --- a/src/plugins/data/public/query/state_sync/connect_to_query_state.ts +++ b/src/plugins/data/public/query/state_sync/connect_to_query_state.ts @@ -21,10 +21,9 @@ import { Subscription } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import _ from 'lodash'; import { BaseStateContainer } from '../../../../kibana_utils/public'; -import { COMPARE_ALL_OPTIONS, compareFilters } from '../filter_manager/lib/compare_filters'; import { QuerySetup, QueryStart } from '../query_service'; import { QueryState, QueryStateChange } from './types'; -import { FilterStateStore } from '../../../common/es_query/filters'; +import { FilterStateStore, COMPARE_ALL_OPTIONS, compareFilters } from '../../../common'; /** * Helper to setup two-way syncing of global data and a state container diff --git a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts index d0d97bfaaeb36..dd075f9be7d94 100644 --- a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts +++ b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts @@ -20,10 +20,10 @@ import { Observable, Subscription } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { TimefilterSetup } from '../timefilter'; -import { COMPARE_ALL_OPTIONS, compareFilters, FilterManager } from '../filter_manager'; +import { FilterManager } from '../filter_manager'; import { QueryState, QueryStateChange } from './index'; import { createStateContainer } from '../../../../kibana_utils/public'; -import { isFilterPinned } from '../../../common/es_query/filters'; +import { isFilterPinned, compareFilters, COMPARE_ALL_OPTIONS } from '../../../common'; export function createQueryStateObservable({ timefilter: { timefilter }, diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 0165486fc2de7..5038b4226fad8 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -166,7 +166,7 @@ export { ParsedInterval } from '../common'; export { ISearch, - ICancel, + ISearchCancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap, diff --git a/src/plugins/data/server/search/i_route_handler_search_context.ts b/src/plugins/data/server/search/i_route_handler_search_context.ts index 89862781b826e..9888c774ea104 100644 --- a/src/plugins/data/server/search/i_route_handler_search_context.ts +++ b/src/plugins/data/server/search/i_route_handler_search_context.ts @@ -17,9 +17,9 @@ * under the License. */ -import { ISearchGeneric, ICancelGeneric } from './i_search'; +import { ISearchGeneric, ISearchCancelGeneric } from './i_search'; export interface IRouteHandlerSearchContext { search: ISearchGeneric; - cancel: ICancelGeneric; + cancel: ISearchCancelGeneric; } diff --git a/src/plugins/data/server/search/i_search.ts b/src/plugins/data/server/search/i_search.ts index ea014c5e136d9..fa4aa72ac7287 100644 --- a/src/plugins/data/server/search/i_search.ts +++ b/src/plugins/data/server/search/i_search.ts @@ -42,7 +42,7 @@ export type ISearchGeneric = Promise; -export type ICancelGeneric = ( +export type ISearchCancelGeneric = ( id: string, strategy?: T ) => Promise; @@ -52,4 +52,4 @@ export type ISearch = ( options?: ISearchOptions ) => Promise; -export type ICancel = (id: string) => Promise; +export type ISearchCancel = (id: string) => Promise; diff --git a/src/plugins/data/server/search/i_search_strategy.ts b/src/plugins/data/server/search/i_search_strategy.ts index 4cfc9608383a9..9b405034f883f 100644 --- a/src/plugins/data/server/search/i_search_strategy.ts +++ b/src/plugins/data/server/search/i_search_strategy.ts @@ -18,7 +18,7 @@ */ import { APICaller } from 'kibana/server'; -import { ISearch, ICancel, ISearchGeneric } from './i_search'; +import { ISearch, ISearchCancel, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; import { ISearchContext } from './i_search_context'; @@ -28,7 +28,7 @@ import { ISearchContext } from './i_search_context'; */ export interface ISearchStrategy { search: ISearch; - cancel?: ICancel; + cancel?: ISearchCancel; } /** diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 385e96ee803b6..15738a3befb27 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -21,7 +21,13 @@ export { ISearchSetup } from './i_search_setup'; export { ISearchContext } from './i_search_context'; -export { ISearch, ICancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap } from './i_search'; +export { + ISearch, + ISearchCancel, + ISearchOptions, + IRequestTypesMap, + IResponseTypesMap, +} from './i_search'; export { TStrategyTypes } from './strategy_types'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 2a2d9bb414c14..178b2949a9456 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -329,12 +329,6 @@ export function getDefaultSearchParams(config: SharedGlobalConfig): { restTotalHitsAsInt: boolean; }; -// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "ICancel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type ICancel = (id: string) => Promise; - // Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -507,11 +501,17 @@ export interface IResponseTypesMap { [ES_SEARCH_STRATEGY]: IEsSearchResponse; } +// Warning: (ae-forgotten-export) The symbol "TStrategyTypes" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ISearch" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type ISearch = (request: IRequestTypesMap[T], options?: ISearchOptions) => Promise; +// Warning: (ae-missing-release-tag) "ISearchCancel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type ISearchCancel = (id: string) => Promise; + // Warning: (ae-missing-release-tag) "ISearchContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts b/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts index 4092dfbba00d5..b8be273d7bbd3 100644 --- a/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts +++ b/src/plugins/expressions/common/expression_functions/specs/kibana_context.ts @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ - +import { uniq } from 'lodash'; import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from '../../expression_functions'; import { KibanaContext } from '../../expression_types'; +import { Query, uniqFilters } from '../../../../data/common'; interface Arguments { q?: string | null; @@ -35,6 +36,15 @@ export type ExpressionFunctionKibanaContext = ExpressionFunctionDefinition< Promise >; +const getParsedValue = (data: any, defaultValue: any) => + typeof data === 'string' && data.length ? JSON.parse(data) || defaultValue : defaultValue; + +const mergeQueries = (first: Query | Query[] = [], second: Query | Query[]) => + uniq( + [...(Array.isArray(first) ? first : [first]), ...(Array.isArray(second) ? second : [second])], + (n: any) => JSON.stringify(n.query) + ); + export const kibanaContextFunction: ExpressionFunctionKibanaContext = { name: 'kibana_context', type: 'kibana_context', @@ -75,9 +85,9 @@ export const kibanaContextFunction: ExpressionFunctionKibanaContext = { }, async fn(input, args, { getSavedObject }) { - const queryArg = args.q ? JSON.parse(args.q) : []; - let queries = Array.isArray(queryArg) ? queryArg : [queryArg]; - let filters = args.filters ? JSON.parse(args.filters) : []; + const timeRange = getParsedValue(args.timeRange, input?.timeRange); + let queries = mergeQueries(input?.query, getParsedValue(args?.q, [])); + let filters = [...(input?.filters || []), ...getParsedValue(args?.filters, [])]; if (args.savedSearchId) { if (typeof getSavedObject !== 'function') { @@ -89,29 +99,20 @@ export const kibanaContextFunction: ExpressionFunctionKibanaContext = { } const obj = await getSavedObject('search', args.savedSearchId); const search = obj.attributes.kibanaSavedObjectMeta as { searchSourceJSON: string }; - const data = JSON.parse(search.searchSourceJSON) as { query: string; filter: any[] }; - queries = queries.concat(data.query); - filters = filters.concat(data.filter); - } + const { query, filter } = getParsedValue(search.searchSourceJSON, {}); - if (input && input.query) { - queries = queries.concat(input.query); - } - - if (input && input.filters) { - filters = filters.concat(input.filters).filter((f: any) => !f.meta.disabled); + if (query) { + queries = mergeQueries(queries, query); + } + if (filter) { + filters = [...filters, ...(Array.isArray(filter) ? filter : [filter])]; + } } - const timeRange = args.timeRange - ? JSON.parse(args.timeRange) - : input - ? input.timeRange - : undefined; - return { type: 'kibana_context', query: queries, - filters, + filters: uniqFilters(filters).filter((f: any) => !f.meta?.disabled), timeRange, }; }, diff --git a/src/plugins/kibana_legacy/public/angular/index.ts b/src/plugins/kibana_legacy/public/angular/index.ts index 5fc37ac39612a..16bae6c4cffe0 100644 --- a/src/plugins/kibana_legacy/public/angular/index.ts +++ b/src/plugins/kibana_legacy/public/angular/index.ts @@ -21,7 +21,6 @@ export { PromiseServiceCreator } from './promises'; // @ts-ignore export { watchMultiDecorator } from './watch_multi'; export * from './angular_config'; -export { ensureDefaultIndexPattern } from './ensure_default_index_pattern'; // @ts-ignore export { createTopNavDirective, createTopNavHelper, loadKbnTopNavDirectives } from './kbn_top_nav'; export { subscribeWithScope } from './subscribe_with_scope'; diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap b/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap index 39bd66ff71c61..ee97a5acfd3d2 100644 --- a/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap +++ b/src/plugins/kibana_react/public/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap @@ -17,27 +17,88 @@ exports[`is rendered 1`] = ` diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/_exit_full_screen_button.scss b/src/plugins/kibana_react/public/exit_full_screen_button/_exit_full_screen_button.scss index e810fe0ccdba6..a2e951cb5b775 100644 --- a/src/plugins/kibana_react/public/exit_full_screen_button/_exit_full_screen_button.scss +++ b/src/plugins/kibana_react/public/exit_full_screen_button/_exit_full_screen_button.scss @@ -4,66 +4,40 @@ */ .dshExitFullScreenButton { - height: $euiSizeXXL; - left: 0; - bottom: 0; + @include euiBottomShadow; + + left: $euiSizeS; + bottom: $euiSizeS; position: fixed; display: block; padding: 0; border: none; background: none; z-index: 5; + background: $euiColorFullShade; + padding: $euiSizeXS; + border-radius: $euiBorderRadius; + text-align: left; - &:hover, - &:focus { - transition: all $euiAnimSpeedExtraSlow $euiAnimSlightResistance; - z-index: 10 !important; /* 1 */ + &:hover { + background: $euiColorFullShade; - .dshExitFullScreenButton__text { - transition: all $euiAnimSpeedNormal $euiAnimSlightResistance; - transform: translateX(-$euiSize); + .dshExitFullScreenButton__icon { + color: $euiColorEmptyShade; } } } -.dshExitFullScreenButton__logo { - display: block; - // Just darken the background for all themes because the logo is always white - background-color: shade($euiColorPrimary, 25%); - height: $euiSizeXXL; - - // These numbers are very specific to the Kibana logo size - width: 92px; - background-image: url('ui/assets/images/kibana.svg'); - background-position: 8px 5px; - background-size: 72px 30px; - background-repeat: no-repeat; - - z-index: $euiZLevel1; +.dshExitFullScreenButton__title { + line-height: 1.2; + color: $euiColorEmptyShade; } -/** - * 1. Calc made to allow caret in text to peek out / animate. - */ - .dshExitFullScreenButton__text { - background: $euiColorPrimary; - color: $euiColorEmptyShade; - line-height: $euiSizeXXL; - display: inline-block; - font-size: $euiFontSizeS; - height: $euiSizeXXL; - position: absolute; - left: calc(100% + #{$euiSize}); /* 1 */ - top: 0px; - bottom: 0px; - white-space: nowrap; - padding: 0px $euiSizeXS 0px $euiSizeM; - transition: all .2s ease; - transform: translateX(-100%); - z-index: -1; - - .euiIcon { - margin-left: $euiSizeXS; - } + line-height: 1.2; + color: makeHighContrastColor($euiColorMediumShade, $euiColorFullShade); +} + +.dshExitFullScreenButton__icon { + color: makeHighContrastColor($euiColorMediumShade, $euiColorFullShade); } diff --git a/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx b/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx index 5ce508ec1ed5b..97fc02ac64e12 100644 --- a/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx +++ b/src/plugins/kibana_react/public/exit_full_screen_button/exit_full_screen_button.tsx @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import React, { PureComponent } from 'react'; import { EuiScreenReaderOnly, keyCodes } from '@elastic/eui'; -import { EuiIcon } from '@elastic/eui'; +import { EuiIcon, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; export interface ExitFullScreenButtonProps { onExitFullScreenMode: () => void; @@ -61,17 +61,40 @@ class ExitFullScreenButtonUi extends PureComponent { )} className="dshExitFullScreenButton" onClick={this.props.onExitFullScreenMode} + data-test-subj="exitFullScreenModeLogo" > - - - {i18n.translate('kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel', { - defaultMessage: 'Exit full screen', - })} - - + + + + + +
+ +

+ {i18n.translate( + 'kibana-react.exitFullScreenButton.exitFullScreenModeButtonTitle', + { + defaultMessage: 'Elastic Kibana', + } + )} +

+
+ +

+ {i18n.translate( + 'kibana-react.exitFullScreenButton.exitFullScreenModeButtonText', + { + defaultMessage: 'Exit full screen', + } + )} +

+
+
+
+ + + +
diff --git a/src/plugins/kibana_legacy/public/angular/ensure_default_index_pattern.tsx b/src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx similarity index 67% rename from src/plugins/kibana_legacy/public/angular/ensure_default_index_pattern.tsx rename to src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx index 1a3bb84ae7575..7992f650cb372 100644 --- a/src/plugins/kibana_legacy/public/angular/ensure_default_index_pattern.tsx +++ b/src/plugins/kibana_utils/public/history/ensure_default_index_pattern.tsx @@ -18,14 +18,13 @@ */ import { contains } from 'lodash'; -import { IRootScopeService } from 'angular'; import React from 'react'; -import ReactDOM from 'react-dom'; +import { History } from 'history'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n/react'; import { EuiCallOut } from '@elastic/eui'; import { CoreStart } from 'kibana/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { toMountPoint } from '../../../kibana_react/public'; let bannerId: string; let timeoutId: NodeJS.Timeout | undefined; @@ -39,18 +38,17 @@ let timeoutId: NodeJS.Timeout | undefined; * resolve to wait for the URL change to happen. */ export async function ensureDefaultIndexPattern( - newPlatform: CoreStart, + core: CoreStart, data: DataPublicPluginStart, - $rootScope: IRootScopeService, - kbnUrl: any + history: History ) { const patterns = await data.indexPatterns.getIds(); - let defaultId = newPlatform.uiSettings.get('defaultIndex'); + let defaultId = core.uiSettings.get('defaultIndex'); let defined = !!defaultId; const exists = contains(patterns, defaultId); if (defined && !exists) { - newPlatform.uiSettings.remove('defaultIndex'); + core.uiSettings.remove('defaultIndex'); defaultId = defined = false; } @@ -61,10 +59,9 @@ export async function ensureDefaultIndexPattern( // If there is any index pattern created, set the first as default if (patterns.length >= 1) { defaultId = patterns[0]; - newPlatform.uiSettings.set('defaultIndex', defaultId); + core.uiSettings.set('defaultIndex', defaultId); } else { - const canManageIndexPatterns = - newPlatform.application.capabilities.management.kibana.index_patterns; + const canManageIndexPatterns = core.application.capabilities.management.kibana.index_patterns; const redirectTarget = canManageIndexPatterns ? '/management/kibana/index_pattern' : '/home'; if (timeoutId) { @@ -73,31 +70,27 @@ export async function ensureDefaultIndexPattern( // Avoid being hostile to new users who don't have an index pattern setup yet // give them a friendly info message instead of a terse error message - bannerId = newPlatform.overlays.banners.replace(bannerId, (element: HTMLElement) => { - ReactDOM.render( - - - , - element - ); - return () => ReactDOM.unmountComponentAtNode(element); - }); + bannerId = core.overlays.banners.replace( + bannerId, + toMountPoint( + + ) + ); // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around timeoutId = setTimeout(() => { - newPlatform.overlays.banners.remove(bannerId); + core.overlays.banners.remove(bannerId); timeoutId = undefined; }, 15000); - kbnUrl.change(redirectTarget); - $rootScope.$digest(); + history.push(redirectTarget); // return never-resolving promise to stop resolving and wait for the url change return new Promise(() => {}); diff --git a/src/plugins/kibana_utils/public/history/index.ts b/src/plugins/kibana_utils/public/history/index.ts index bb13ea09f928a..1a73bbb6b04a1 100644 --- a/src/plugins/kibana_utils/public/history/index.ts +++ b/src/plugins/kibana_utils/public/history/index.ts @@ -19,3 +19,4 @@ export { removeQueryParam } from './remove_query_param'; export { redirectWhenMissing } from './redirect_when_missing'; +export { ensureDefaultIndexPattern } from './ensure_default_index_pattern'; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 47f90cbe2a627..1876e688c989a 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -73,5 +73,5 @@ export { StartSyncStateFnType, StopSyncStateFnType, } from './state_sync'; -export { removeQueryParam, redirectWhenMissing } from './history'; +export { removeQueryParam, redirectWhenMissing, ensureDefaultIndexPattern } from './history'; export { applyDiff } from './state_management/utils/diff_object'; diff --git a/test/functional/screenshots/baseline/area_chart.png b/test/functional/screenshots/baseline/area_chart.png index 2c2d599139100..1a381d61dd9f1 100644 Binary files a/test/functional/screenshots/baseline/area_chart.png and b/test/functional/screenshots/baseline/area_chart.png differ diff --git a/test/functional/screenshots/baseline/tsvb_dashboard.png b/test/functional/screenshots/baseline/tsvb_dashboard.png index d703be89b7460..f5ebccbcb96c6 100644 Binary files a/test/functional/screenshots/baseline/tsvb_dashboard.png and b/test/functional/screenshots/baseline/tsvb_dashboard.png differ diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 60a8d1fcbf229..1564eb94a6903 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -22,7 +22,7 @@ "xpack.infra": "plugins/infra", "xpack.ingestManager": "plugins/ingest_manager", "xpack.lens": "legacy/plugins/lens", - "xpack.licenseMgmt": "legacy/plugins/license_management", + "xpack.licenseMgmt": "plugins/license_management", "xpack.licensing": "plugins/licensing", "xpack.logstash": "legacy/plugins/logstash", "xpack.main": "legacy/plugins/xpack_main", diff --git a/x-pack/index.js b/x-pack/index.js index ab31d40c5d718..fb14b3dc10a4d 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -16,7 +16,6 @@ import { logstash } from './legacy/plugins/logstash'; import { beats } from './legacy/plugins/beats_management'; import { apm } from './legacy/plugins/apm'; import { maps } from './legacy/plugins/maps'; -import { licenseManagement } from './legacy/plugins/license_management'; import { indexManagement } from './legacy/plugins/index_management'; import { indexLifecycleManagement } from './legacy/plugins/index_lifecycle_management'; import { spaces } from './legacy/plugins/spaces'; @@ -52,7 +51,6 @@ module.exports = function(kibana) { apm(kibana), maps(kibana), canvas(kibana), - licenseManagement(kibana), indexManagement(kibana), indexLifecycleManagement(kibana), infra(kibana), diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx index 31fc4db8f1a2f..cff190cd98a11 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx @@ -209,7 +209,7 @@ export function MachineLearningFlyoutView({ {i18n.translate( 'xpack.apm.serviceDetails.enableAnomalyDetectionPanel.createNewJobButtonLabel', { - defaultMessage: 'Create new job' + defaultMessage: 'Create job' } )} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx new file mode 100644 index 0000000000000..d61dea80666a0 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act, render, wait } from '@testing-library/react'; +import cytoscape from 'cytoscape'; +import React, { FunctionComponent } from 'react'; +import { MockApmPluginContextWrapper } from '../../../utils/testHelpers'; +import { CytoscapeContext } from './Cytoscape'; +import { EmptyBanner } from './EmptyBanner'; + +const cy = cytoscape({}); + +const wrapper: FunctionComponent = ({ children }) => ( + + {children} + +); + +describe('EmptyBanner', () => { + describe('when cy is undefined', () => { + it('renders null', () => { + const noCytoscapeWrapper: FunctionComponent = ({ children }) => ( + + + {children} + + + ); + const component = render(, { + wrapper: noCytoscapeWrapper + }); + + expect(component.container.children).toHaveLength(0); + }); + }); + + describe('with no nodes', () => { + it('renders null', () => { + const component = render(, { + wrapper + }); + + expect(component.container.children).toHaveLength(0); + }); + }); + + describe('with one node', () => { + it('does not render null', async () => { + const component = render(, { wrapper }); + + await act(async () => { + cy.add({ data: { id: 'test id' } }); + await wait(() => { + expect(component.container.children.length).toBeGreaterThan(0); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx index 418430e37b21e..464bf166eb80f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx @@ -7,37 +7,70 @@ import { EuiCallOut } from '@elastic/eui'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink'; +import { CytoscapeContext } from './Cytoscape'; -const EmptyBannerCallOut = styled(EuiCallOut)` +const EmptyBannerContainer = styled.div` margin: ${lightTheme.gutterTypes.gutterSmall}; /* Add some extra margin so it displays to the right of the controls. */ - margin-left: calc( - ${lightTheme.gutterTypes.gutterLarge} + - ${lightTheme.gutterTypes.gutterExtraLarge} + left: calc( + ${lightTheme.gutterTypes.gutterExtraLarge} + + ${lightTheme.gutterTypes.gutterSmall} ); position: absolute; z-index: 1; `; export function EmptyBanner() { + const cy = useContext(CytoscapeContext); + const [nodeCount, setNodeCount] = useState(0); + + useEffect(() => { + const handler: cytoscape.EventHandler = event => + setNodeCount(event.cy.nodes().length); + + if (cy) { + cy.on('add remove', 'node', handler); + } + + return () => { + if (cy) { + cy.removeListener('add remove', 'node', handler); + } + }; + }, [cy]); + + // Only show if there's a single node. + if (!cy || nodeCount !== 1) { + return null; + } + + // Since we're absolutely positioned, we need to get the full width and + // subtract the space for controls and margins. + const width = + cy.width() - + parseInt(lightTheme.gutterTypes.gutterExtraLarge, 10) - + parseInt(lightTheme.gutterTypes.gutterLarge, 10); + return ( - - {i18n.translate('xpack.apm.serviceMap.emptyBanner.message', { - defaultMessage: - "We will map out connected services and external requests if we can detect them. Please make sure you're running the latest version of the APM agent." - })}{' '} - - {i18n.translate('xpack.apm.serviceMap.emptyBanner.docsLink', { - defaultMessage: 'Learn more in the docs' + + - + > + {i18n.translate('xpack.apm.serviceMap.emptyBanner.message', { + defaultMessage: + "We will map out connected services and external requests if we can detect them. Please make sure you're running the latest version of the APM agent." + })}{' '} + + {i18n.translate('xpack.apm.serviceMap.emptyBanner.docsLink', { + defaultMessage: 'Learn more in the docs' + })} + + + ); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx index 9213349a1492b..77f0b64ba0fb1 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.tsx @@ -6,10 +6,12 @@ import { EuiButton, - EuiEmptyPrompt, + EuiPanel, EuiFlexGroup, EuiFlexItem, - EuiPanel + EuiTitle, + EuiText, + EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; @@ -18,7 +20,8 @@ import { useKibanaUrl } from '../../../hooks/useKibanaUrl'; export function PlatinumLicensePrompt() { // Set the height to give it some top margin - const style = { height: '60vh' }; + const flexGroupStyle = { height: '60vh' }; + const flexItemStyle = { width: 600, textAlign: 'center' as const }; const licensePageUrl = useKibanaUrl( '/app/kibana', @@ -29,30 +32,41 @@ export function PlatinumLicensePrompt() { - - - - {i18n.translate( - 'xpack.apm.serviceMap.licensePromptButtonText', - { - defaultMessage: 'Start 30-day Platinum trial' - } - )} - - ]} - body={

{invalidLicenseMessage}

} - title={ + + + + +

{i18n.translate('xpack.apm.serviceMap.licensePromptTitle', { defaultMessage: 'Service maps is available in Platinum.' })}

- } - /> +
+ + +

{invalidLicenseMessage}

+
+ + + {i18n.translate('xpack.apm.serviceMap.licensePromptButtonText', { + defaultMessage: 'Start 30-day Platinum trial' + })} + +
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg index 9f7427f0e1001..da7f1a8fde45d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg @@ -1,127 +1,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 7bbb77a49c84b..93aa3d406028c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -15,6 +15,8 @@ import React, { useRef, useState } from 'react'; +import { EuiBetaBadge } from '@elastic/eui'; +import styled from 'styled-components'; import { isValidPlatinumLicense } from '../../../../../../../plugins/apm/common/service_map'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceMapAPIResponse } from '../../../../../../../plugins/apm/server/lib/service_map/get_service_map'; @@ -56,7 +58,12 @@ ${theme.euiColorLightShade}`, margin: `-${theme.gutterTypes.gutterLarge}`, marginTop: 0 }; - +const BetaBadgeContainer = styled.div` + right: ${theme.gutterTypes.gutterMedium}; + position: absolute; + top: ${theme.gutterTypes.gutterSmall}; + z-index: 1; /* The element containing the cytoscape canvas has z-index = 0. */ +`; const MAX_REQUESTS = 5; export function ServiceMap({ serviceName }: ServiceMapProps) { @@ -182,10 +189,22 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { style={cytoscapeDivStyle} > - {serviceName && renderedElements.current.length === 1 && ( - - )} + {serviceName && } + + + ) : ( diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap deleted file mode 100644 index e19958568b3be..0000000000000 --- a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap +++ /dev/null @@ -1,2826 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`UploadLicense should display a modal when license requires acknowledgement 1`] = ` - - - - - -
- -
- -

- - Upload your license - -

-
- -
- - - -
-
-
-
- -
-
-
- Confirm License Upload -
-
-
-
-
-
-
- Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. -
-
-
    -
  • - Watcher will be disabled -
  • -
-
-
-
-
-
-
- - -
-
-
-
-
-
- } - > - - } - confirmButtonText={ - - } - onCancel={[Function]} - onConfirm={[Function]} - title={ - - } - > - - - -
-
-
- -
- -
-
-
- Confirm License Upload -
-
-
-
-
-
-
- Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. -
-
-
    -
  • - Watcher will be disabled -
  • -
-
-
-
-
-
-
- - -
-
-
-
- } - onActivation={[Function]} - onDeactivation={[Function]} - persistentFocus={false} - > - -
- -
-
-
- Confirm License Upload -
-
-
-
-
-
-
- Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. -
-
-
    -
  • - Watcher will be disabled -
  • -
-
-
-
-
-
-
- - -
-
-
-
- } - onActivation={[Function]} - onDeactivation={[Function]} - persistentFocus={false} - /> - -
- - - - - -
- -
- -
- - Confirm License Upload - -
-
-
-
- -
-
- -
-
- -
- Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. -
-
- -
-
    -
  • - Watcher will be disabled -
  • -
-
-
-
-
-
-
-
-
- -
- - - - - - -
-
-
-
-
-
- - - - - - - -
-

- - Your license key is a JSON file with a signature attached. - -

-

- - - , - } - } - > - Uploading a license will replace your current - - license. - -

-
-
- -
- - -
- -
- -
- -
- - } - onChange={[Function]} - > - -
-
- - - -
-
-
- - -
- -
- -
- - -
- - -
- - -
- - - - -
- - - -
-
-
-
-
- -
- -
- - - - - -`; - -exports[`UploadLicense should display an error when ES says license is expired 1`] = ` - - - - - -
- -
- -

- - Upload your license - -

-
- -
- - -
-

- - Your license key is a JSON file with a signature attached. - -

-

- - - , - } - } - > - Uploading a license will replace your current - - license. - -

-
- - -
- - -
- - -
-
- - Please address the errors in your form. - -
- -
-
    -
  • - The supplied license has expired. -
  • -
-
-
-
-
-
- -
- -
- -
- - } - onChange={[Function]} - > - -
-
- - - -
-
-
- - -
- -
- -
- - -
- - -
- - -
- - - - -
- - - -
-
-
-
-
- -
- -
- - - - - -`; - -exports[`UploadLicense should display an error when ES says license is invalid 1`] = ` - - - - - -
- -
- -

- - Upload your license - -

-
- -
- - -
-

- - Your license key is a JSON file with a signature attached. - -

-

- - - , - } - } - > - Uploading a license will replace your current - - license. - -

-
- - -
- - -
- - -
-
- - Please address the errors in your form. - -
- -
-
    -
  • - The supplied license is not valid for this product. -
  • -
-
-
-
-
-
- -
- -
- -
- - } - onChange={[Function]} - > - -
-
- - - -
-
-
- - -
- -
- -
- - -
- - -
- - -
- - - - -
- - - -
-
-
-
-
- -
- -
- - - - - -`; - -exports[`UploadLicense should display an error when submitting invalid JSON 1`] = ` - - - - - -
- -
- -

- - Upload your license - -

-
- -
- - -
-

- - Your license key is a JSON file with a signature attached. - -

-

- - - , - } - } - > - Uploading a license will replace your current - - license. - -

-
- - -
- - -
- - -
-
- - Please address the errors in your form. - -
- -
-
    -
  • - Error encountered uploading license: Check your license file. -
  • -
-
-
-
-
-
- -
- -
- -
- - } - onChange={[Function]} - > - -
-
- - - -
-
-
- - -
- -
- -
- - -
- - -
- - -
- - - - -
- - - -
-
-
-
-
- -
- -
- - - - - -`; - -exports[`UploadLicense should display error when ES returns error 1`] = ` - - - - - -
- -
- -

- - Upload your license - -

-
- -
- - -
-

- - Your license key is a JSON file with a signature attached. - -

-

- - - , - } - } - > - Uploading a license will replace your current - - license. - -

-
- - -
- - -
- - -
-
- - Please address the errors in your form. - -
- -
-
    -
  • - Error encountered uploading license: Can not upgrade to a production license unless TLS is configured or security is disabled -
  • -
-
-
-
-
-
- -
- -
- -
- - } - onChange={[Function]} - > - -
-
- - - -
-
-
- - -
- -
- -
- - -
- - -
- - -
- - - - -
- - - -
-
-
-
-
- -
- -
- - - - - -`; diff --git a/x-pack/legacy/plugins/license_management/index.ts b/x-pack/legacy/plugins/license_management/index.ts deleted file mode 100644 index e9fbb56e9d6ac..0000000000000 --- a/x-pack/legacy/plugins/license_management/index.ts +++ /dev/null @@ -1,49 +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 { resolve } from 'path'; -import { PLUGIN } from './common/constants'; -import { plugin } from './server/np_ready'; - -export function licenseManagement(kibana: any) { - return new kibana.Plugin({ - id: PLUGIN.ID, - configPrefix: 'xpack.license_management', - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch'], - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/np_ready/application/index.scss'), - managementSections: ['plugins/license_management/legacy'], - injectDefaultVars(server: Legacy.Server) { - const config = server.config(); - return { - licenseManagementUiEnabled: config.get('xpack.license_management.ui.enabled'), - }; - }, - }, - 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(); - }, - init: (server: Legacy.Server) => { - plugin({} as any).setup(server.newPlatform.setup.core, { - ...server.newPlatform.setup.plugins, - __LEGACY: { - xpackMain: server.plugins.xpack_main, - elasticsearch: server.plugins.elasticsearch, - }, - }); - }, - }); -} diff --git a/x-pack/legacy/plugins/license_management/public/management_section.ts b/x-pack/legacy/plugins/license_management/public/management_section.ts deleted file mode 100644 index c7232649857e3..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/management_section.ts +++ /dev/null @@ -1,20 +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 { management } from 'ui/management'; -import chrome from 'ui/chrome'; -import { BASE_PATH, PLUGIN } from '../common/constants'; - -const licenseManagementUiEnabled = chrome.getInjected('licenseManagementUiEnabled'); - -if (licenseManagementUiEnabled) { - management.getSection('elasticsearch').register('license_management', { - visible: true, - display: PLUGIN.TITLE, - order: 99, - url: `#${BASE_PATH}home`, - }); -} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx b/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx deleted file mode 100644 index 49bb4ce984e48..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx +++ /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 React from 'react'; -import { Provider } from 'react-redux'; -import { HashRouter } from 'react-router-dom'; -import { render, unmountComponentAtNode } from 'react-dom'; -import * as history from 'history'; -import { DocLinksStart, HttpSetup, ToastsSetup, ChromeStart } from 'src/core/public'; - -import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; -// @ts-ignore -import { App } from './app.container'; -// @ts-ignore -import { licenseManagementStore } from './store'; - -import { setDocLinks } from './lib/docs_links'; -import { BASE_PATH } from '../../../common/constants'; -import { Breadcrumb } from './breadcrumbs'; - -interface AppDependencies { - element: HTMLElement; - chrome: ChromeStart; - - I18nContext: any; - legacy: { - xpackInfo: any; - refreshXpack: () => void; - MANAGEMENT_BREADCRUMB: Breadcrumb; - }; - - toasts: ToastsSetup; - docLinks: DocLinksStart; - http: HttpSetup; - telemetry?: TelemetryPluginSetup; -} - -export const boot = (deps: AppDependencies) => { - const { I18nContext, element, legacy, toasts, docLinks, http, chrome, telemetry } = deps; - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; - const securityDocumentationLink = `${esBase}/security-settings.html`; - - const initialState = { license: legacy.xpackInfo.get('license') }; - - setDocLinks({ securityDocumentationLink }); - - const services = { - legacy: { - refreshXpack: legacy.refreshXpack, - xPackInfo: legacy.xpackInfo, - }, - // So we can imperatively control the hash route - history: history.createHashHistory({ basename: BASE_PATH }), - toasts, - http, - chrome, - telemetry, - MANAGEMENT_BREADCRUMB: legacy.MANAGEMENT_BREADCRUMB, - }; - - const store = licenseManagementStore(initialState, services); - - render( - - - - - - - , - element - ); - - return () => unmountComponentAtNode(element); -}; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/breadcrumbs.ts b/x-pack/legacy/plugins/license_management/public/np_ready/application/breadcrumbs.ts deleted file mode 100644 index 2da04b22c0386..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/breadcrumbs.ts +++ /dev/null @@ -1,37 +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 { BASE_PATH } from '../../../common/constants'; - -export interface Breadcrumb { - text: string; - href: string; -} - -export function getDashboardBreadcrumbs(root: Breadcrumb) { - return [ - root, - { - text: i18n.translate('xpack.licenseMgmt.dashboard.breadcrumb', { - defaultMessage: 'License management', - }), - href: `#${BASE_PATH}home`, - }, - ]; -} - -export function getUploadBreadcrumbs(root: Breadcrumb) { - return [ - ...getDashboardBreadcrumbs(root), - { - text: i18n.translate('xpack.licenseMgmt.upload.breadcrumb', { - defaultMessage: 'Upload', - }), - }, - ]; -} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/set_breadcrumb.ts b/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/set_breadcrumb.ts deleted file mode 100644 index bcb4a907bdf88..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/set_breadcrumb.ts +++ /dev/null @@ -1,22 +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 { ThunkAction } from 'redux-thunk'; -import { ChromeStart } from 'src/core/public'; -import { getDashboardBreadcrumbs, getUploadBreadcrumbs, Breadcrumb } from '../../breadcrumbs'; - -export const setBreadcrumb = ( - section: 'dashboard' | 'upload' -): ThunkAction => ( - dispatch, - getState, - { chrome, MANAGEMENT_BREADCRUMB } -) => { - if (section === 'upload') { - chrome.setBreadcrumbs(getUploadBreadcrumbs(MANAGEMENT_BREADCRUMB)); - } else { - chrome.setBreadcrumbs(getDashboardBreadcrumbs(MANAGEMENT_BREADCRUMB)); - } -}; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts b/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts deleted file mode 100644 index 60876c9b638d1..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; -import { XPackMainPlugin } from '../../../xpack_main/server/xpack_main'; -import { PLUGIN } from '../../common/constants'; -import { Breadcrumb } from './application/breadcrumbs'; -export interface Plugins { - telemetry: TelemetryPluginSetup; - __LEGACY: { - xpackInfo: XPackMainPlugin; - refreshXpack: () => void; - MANAGEMENT_BREADCRUMB: Breadcrumb; - }; -} - -export class LicenseManagementUIPlugin implements Plugin { - setup({ application, notifications, http }: CoreSetup, { __LEGACY, telemetry }: Plugins) { - application.register({ - id: PLUGIN.ID, - title: PLUGIN.TITLE, - async mount( - { - core: { - docLinks, - i18n: { Context: I18nContext }, - chrome, - }, - }, - { element } - ) { - const { boot } = await import('./application'); - return boot({ - legacy: { ...__LEGACY }, - I18nContext, - toasts: notifications.toasts, - docLinks, - http, - element, - chrome, - telemetry, - }); - }, - }); - } - start(core: CoreStart, plugins: any) {} - stop() {} -} diff --git a/x-pack/legacy/plugins/license_management/public/register_route.ts b/x-pack/legacy/plugins/license_management/public/register_route.ts deleted file mode 100644 index f9258f68c555a..0000000000000 --- a/x-pack/legacy/plugins/license_management/public/register_route.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { App } from 'src/core/public'; - -/* Legacy Imports */ -import { npSetup, npStart } from 'ui/new_platform'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; -import chrome from 'ui/chrome'; -import routes from 'ui/routes'; -// @ts-ignore -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import { plugin } from './np_ready'; -import { BASE_PATH } from '../common/constants'; - -const licenseManagementUiEnabled = chrome.getInjected('licenseManagementUiEnabled'); - -if (licenseManagementUiEnabled) { - /* - This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular - from destroying scope when route changes and both old route and new route are this same route. - */ - const manageAngularLifecycle = ($scope: any, $route: any, unmount: () => void) => { - const lastRoute = $route.current; - const deregister = $scope.$on('$locationChangeSuccess', () => { - const currentRoute = $route.current; - // if templates are the same we are on the same route - if (lastRoute.$$route.template === currentRoute.$$route.template) { - // this prevents angular from destroying scope - $route.current = lastRoute; - } - }); - $scope.$on('$destroy', () => { - if (deregister) { - deregister(); - } - unmount(); - }); - }; - - const template = ` -
-
`; - - routes.when(`${BASE_PATH}:view?`, { - template, - controllerAs: 'licenseManagement', - controller: class LicenseManagementController { - constructor($injector: any, $rootScope: any, $scope: any, $route: any) { - $scope.$$postDigest(() => { - const element = document.getElementById('licenseReactRoot')!; - - const refreshXpack = async () => { - await xpackInfo.refresh($injector); - }; - - plugin({} as any).setup( - { - ...npSetup.core, - application: { - ...npSetup.core.application, - async register(app: App) { - const unmountApp = await app.mount({ ...npStart } as any, { - element, - appBasePath: '', - onAppLeave: () => undefined, - // TODO: adapt to use Core's ScopedHistory - history: {} as any, - }); - manageAngularLifecycle($scope, $route, unmountApp as any); - }, - }, - }, - { - telemetry: (npSetup.plugins as any).telemetry, - __LEGACY: { xpackInfo, refreshXpack, MANAGEMENT_BREADCRUMB }, - } - ); - }); - } - } as any, - } as any); -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/lib/start_trial.ts b/x-pack/legacy/plugins/license_management/server/np_ready/lib/start_trial.ts deleted file mode 100644 index 3569085d413ca..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/lib/start_trial.ts +++ /dev/null @@ -1,47 +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 { KibanaRequest } from 'src/core/server'; -import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch'; - -export async function canStartTrial( - req: KibanaRequest, - elasticsearch: ElasticsearchPlugin -) { - const { callWithRequest } = elasticsearch.getCluster('admin'); - const options = { - method: 'GET', - path: '/_license/trial_status', - }; - try { - const response = await callWithRequest(req as any, 'transport.request', options); - return response.eligible_to_start_trial; - } catch (error) { - return error.body; - } -} - -export async function startTrial( - req: KibanaRequest, - elasticsearch: ElasticsearchPlugin, - xpackInfo: any -) { - const { callWithRequest } = elasticsearch.getCluster('admin'); - const options = { - method: 'POST', - path: '/_license/start_trial?acknowledge=true', - }; - try { - const response = await callWithRequest(req as any, 'transport.request', options); - const { trial_was_started: trialWasStarted } = response; - if (trialWasStarted) { - await xpackInfo.refreshNow(); - } - return response; - } catch (error) { - return error.body; - } -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/plugin.ts b/x-pack/legacy/plugins/license_management/server/np_ready/plugin.ts deleted file mode 100644 index 9f065cf98d715..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/plugin.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Plugin, CoreSetup } from 'src/core/server'; -import { Dependencies, Server } from './types'; - -import { - registerLicenseRoute, - registerStartTrialRoutes, - registerStartBasicRoute, - registerPermissionsRoute, -} from './routes/api/license'; - -export class LicenseManagementServerPlugin implements Plugin { - setup({ http }: CoreSetup, { __LEGACY }: Dependencies) { - const xpackInfo = __LEGACY.xpackMain.info; - const router = http.createRouter(); - - const server: Server = { - router, - }; - - const legacy = { plugins: __LEGACY }; - - registerLicenseRoute(server, legacy, xpackInfo); - registerStartTrialRoutes(server, legacy, xpackInfo); - registerStartBasicRoute(server, legacy, xpackInfo); - registerPermissionsRoute(server, legacy, xpackInfo); - } - start() {} - stop() {} -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_permissions_route.ts b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_permissions_route.ts deleted file mode 100644 index 0f6c343d04fcd..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_permissions_route.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getPermissions } from '../../../lib/permissions'; -import { Legacy, Server } from '../../../types'; - -export function registerPermissionsRoute(server: Server, legacy: Legacy, xpackInfo: any) { - server.router.post( - { path: '/api/license/permissions', validate: false }, - async (ctx, request, response) => { - if (!xpackInfo) { - // xpackInfo is updated via poll, so it may not be available until polling has begun. - // In this rare situation, tell the client the service is temporarily unavailable. - return response.customError({ statusCode: 503, body: 'Security info unavailable' }); - } - - try { - return response.ok({ - body: await getPermissions(request, legacy.plugins.elasticsearch, xpackInfo), - }); - } catch (e) { - return response.internalError({ body: e }); - } - } - ); -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_basic_route.ts b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_basic_route.ts deleted file mode 100644 index ee7ac8602104b..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_basic_route.ts +++ /dev/null @@ -1,27 +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 { startBasic } from '../../../lib/start_basic'; -import { Legacy, Server } from '../../../types'; - -export function registerStartBasicRoute(server: Server, legacy: Legacy, xpackInfo: any) { - server.router.post( - { - path: '/api/license/start_basic', - validate: { query: schema.object({ acknowledge: schema.string() }) }, - }, - async (ctx, request, response) => { - try { - return response.ok({ - body: await startBasic(request, legacy.plugins.elasticsearch, xpackInfo), - }); - } catch (e) { - return response.internalError({ body: e }); - } - } - ); -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_trial_routes.ts b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_trial_routes.ts deleted file mode 100644 index d93f13eba363a..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_trial_routes.ts +++ /dev/null @@ -1,34 +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 { canStartTrial, startTrial } from '../../../lib/start_trial'; -import { Legacy, Server } from '../../../types'; - -export function registerStartTrialRoutes(server: Server, legacy: Legacy, xpackInfo: any) { - server.router.get( - { path: '/api/license/start_trial', validate: false }, - async (ctx, request, response) => { - try { - return response.ok({ body: await canStartTrial(request, legacy.plugins.elasticsearch) }); - } catch (e) { - return response.internalError({ body: e }); - } - } - ); - - server.router.post( - { path: '/api/license/start_trial', validate: false }, - async (ctx, request, response) => { - try { - return response.ok({ - body: await startTrial(request, legacy.plugins.elasticsearch, xpackInfo), - }); - } catch (e) { - return response.internalError({ body: e }); - } - } - ); -} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/types.ts b/x-pack/legacy/plugins/license_management/server/np_ready/types.ts deleted file mode 100644 index 0e66946ec1cc6..0000000000000 --- a/x-pack/legacy/plugins/license_management/server/np_ready/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { IRouter } from 'src/core/server'; -import { XPackMainPlugin } from '../../../xpack_main/server/xpack_main'; -import { ElasticsearchPlugin } from '../../../../../../src/legacy/core_plugins/elasticsearch'; - -export interface Dependencies { - __LEGACY: { - xpackMain: XPackMainPlugin; - elasticsearch: ElasticsearchPlugin; - }; -} - -export interface Server { - router: IRouter; -} - -export interface Legacy { - plugins: Dependencies['__LEGACY']; -} diff --git a/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts b/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts new file mode 100644 index 0000000000000..3281fb5892eac --- /dev/null +++ b/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +// Global map state passed to every layer. +export type MapFilters = { + buffer: unknown; + extent: unknown; + filters: unknown[]; + query: unknown; + refreshTimerLastTriggeredAt: string; + timeFilters: unknown; + zoom: number; +}; + +export type VectorLayerRequestMeta = MapFilters & { + applyGlobalQuery: boolean; + fieldNames: string[]; + geogridPrecision: number; + sourceQuery: unknown; + sourceMeta: unknown; +}; + +export type ESSearchSourceResponseMeta = { + areResultsTrimmed?: boolean; + sourceType?: string; + + // top hits meta + areEntitiesTrimmed?: boolean; + entityCount?: number; + totalEntities?: number; +}; + +// Partial because objects are justified downstream in constructors +export type DataMeta = Partial & Partial; + +export type DataRequestDescriptor = { + dataId: string; + dataMetaAtStart?: DataMeta; + dataRequestToken?: symbol; + data?: object; + dataMeta?: DataMeta; +}; diff --git a/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts b/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts index ce0743ba2baed..2f45c525828db 100644 --- a/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts +++ b/x-pack/legacy/plugins/maps/common/descriptor_types.d.ts @@ -5,7 +5,8 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { AGG_TYPE, GRID_RESOLUTION, RENDER_AS, SORT_ORDER } from './constants'; +import { DataRequestDescriptor } from './data_request_descriptor_types'; +import { AGG_TYPE, GRID_RESOLUTION, RENDER_AS, SORT_ORDER, SCALING_TYPES } from './constants'; export type AbstractSourceDescriptor = { id?: string; @@ -49,7 +50,7 @@ export type ESSearchSourceDescriptor = AbstractESSourceDescriptor & { tooltipProperties?: string[]; sortField?: string; sortOrder?: SORT_ORDER; - useTopHits?: boolean; + scalingType: SCALING_TYPES; topHitsSplitField?: string; topHitsSize?: number; }; @@ -93,14 +94,6 @@ export type JoinDescriptor = { right: ESTermSourceDescriptor; }; -export type DataRequestDescriptor = { - dataId: string; - dataMetaAtStart: object; - dataRequestToken: symbol; - data: object; - dataMeta: object; -}; - export type LayerDescriptor = { __dataRequests?: DataRequestDescriptor[]; __isInErrorState?: boolean; diff --git a/x-pack/legacy/plugins/maps/common/migrations/scaling_type.test.ts b/x-pack/legacy/plugins/maps/common/migrations/scaling_type.test.ts new file mode 100644 index 0000000000000..4fbb1ef4c55ed --- /dev/null +++ b/x-pack/legacy/plugins/maps/common/migrations/scaling_type.test.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 { migrateUseTopHitsToScalingType } from './scaling_type'; + +describe('migrateUseTopHitsToScalingType', () => { + test('Should handle missing layerListJSON attribute', () => { + const attributes = { + title: 'my map', + }; + expect(migrateUseTopHitsToScalingType({ attributes })).toEqual({ + title: 'my map', + }); + }); + + test('Should migrate useTopHits: true to scalingType TOP_HITS for ES documents sources', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'ES_SEARCH', + useTopHits: true, + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(migrateUseTopHitsToScalingType({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"sourceDescriptor":{"type":"ES_SEARCH","scalingType":"TOP_HITS"}}]', + }); + }); + + test('Should migrate useTopHits: false to scalingType LIMIT for ES documents sources', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'ES_SEARCH', + useTopHits: false, + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(migrateUseTopHitsToScalingType({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"sourceDescriptor":{"type":"ES_SEARCH","scalingType":"LIMIT"}}]', + }); + }); + + test('Should set scalingType to LIMIT when useTopHits is not set', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'ES_SEARCH', + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(migrateUseTopHitsToScalingType({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"sourceDescriptor":{"type":"ES_SEARCH","scalingType":"LIMIT"}}]', + }); + }); +}); diff --git a/x-pack/legacy/plugins/maps/common/migrations/scaling_type.ts b/x-pack/legacy/plugins/maps/common/migrations/scaling_type.ts new file mode 100644 index 0000000000000..5823ddd6b42e3 --- /dev/null +++ b/x-pack/legacy/plugins/maps/common/migrations/scaling_type.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 _ from 'lodash'; +import { ES_SEARCH, SCALING_TYPES } from '../constants'; +import { LayerDescriptor, ESSearchSourceDescriptor } from '../descriptor_types'; +import { MapSavedObjectAttributes } from '../../../../../plugins/maps/common/map_saved_object_type'; + +function isEsDocumentSource(layerDescriptor: LayerDescriptor) { + const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type'); + return sourceType === ES_SEARCH; +} + +export function migrateUseTopHitsToScalingType({ + attributes, +}: { + attributes: MapSavedObjectAttributes; +}): MapSavedObjectAttributes { + if (!attributes || !attributes.layerListJSON) { + return attributes; + } + + const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON); + layerList.forEach((layerDescriptor: LayerDescriptor) => { + if (isEsDocumentSource(layerDescriptor)) { + const sourceDescriptor = layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor; + sourceDescriptor.scalingType = _.get(layerDescriptor, 'sourceDescriptor.useTopHits', false) + ? SCALING_TYPES.TOP_HITS + : SCALING_TYPES.LIMIT; + // @ts-ignore useTopHits no longer in type definition but that does not mean its not in live data + // hence the entire point of this method + delete sourceDescriptor.useTopHits; + } + }); + + return { + ...attributes, + layerListJSON: JSON.stringify(layerList), + }; +} diff --git a/x-pack/legacy/plugins/maps/migrations.js b/x-pack/legacy/plugins/maps/migrations.js index 9622f6ba63fac..6a1f5bc937497 100644 --- a/x-pack/legacy/plugins/maps/migrations.js +++ b/x-pack/legacy/plugins/maps/migrations.js @@ -10,6 +10,7 @@ import { topHitsTimeToSort } from './common/migrations/top_hits_time_to_sort'; import { moveApplyGlobalQueryToSources } from './common/migrations/move_apply_global_query'; import { addFieldMetaOptions } from './common/migrations/add_field_meta_options'; import { migrateSymbolStyleDescriptor } from './common/migrations/migrate_symbol_style_descriptor'; +import { migrateUseTopHitsToScalingType } from './common/migrations/scaling_type'; export const migrations = { map: { @@ -48,11 +49,12 @@ export const migrations = { }; }, '7.7.0': doc => { - const attributes = migrateSymbolStyleDescriptor(doc); + const attributesPhase1 = migrateSymbolStyleDescriptor(doc); + const attributesPhase2 = migrateUseTopHitsToScalingType({ attributes: attributesPhase1 }); return { ...doc, - attributes, + attributes: attributesPhase2, }; }, }, diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts new file mode 100644 index 0000000000000..418f2880c1077 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.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. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import { DataMeta, MapFilters } from '../../common/data_request_descriptor_types'; + +export type SyncContext = { + startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void; + stopLoading(dataId: string, requestToken: symbol, data: unknown, meta: DataMeta): void; + onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void; + updateSourceData(newData: unknown): void; + isRequestStillActive(dataId: string, requestToken: symbol): boolean; + registerCancelCallback(requestToken: symbol, callback: () => void): void; + dataFilters: MapFilters; +}; diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.js b/x-pack/legacy/plugins/maps/public/actions/map_actions.js index 7a1e5e5266246..415630d9f730b 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.js +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.js @@ -649,13 +649,14 @@ export function onDataLoadError(layerId, dataId, requestToken, errorMessage) { }; } -export function updateSourceProp(layerId, propName, value) { +export function updateSourceProp(layerId, propName, value, newLayerType) { return async dispatch => { dispatch({ type: UPDATE_SOURCE_PROP, layerId, propName, value, + newLayerType, }); await dispatch(clearMissingStyleProperties(layerId)); dispatch(syncDataForLayer(layerId)); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js index 94e855fc6708f..60bbaa9825db7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js @@ -16,8 +16,6 @@ import { EuiTextColor, EuiTextAlign, EuiButtonEmpty, - EuiFormRow, - EuiSwitch, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -80,14 +78,6 @@ export class FilterEditor extends Component { this._close(); }; - _onFilterByMapBoundsChange = event => { - this.props.updateSourceProp( - this.props.layer.getId(), - 'filterByMapBounds', - event.target.checked - ); - }; - _onApplyGlobalQueryChange = applyGlobalQuery => { this.props.updateSourceProp(this.props.layer.getId(), 'applyGlobalQuery', applyGlobalQuery); }; @@ -182,22 +172,6 @@ export class FilterEditor extends Component { } render() { - let filterByBoundsSwitch; - if (this.props.layer.getSource().isFilterByMapBoundsConfigurable()) { - filterByBoundsSwitch = ( - - - - ); - } - return ( @@ -217,8 +191,6 @@ export class FilterEditor extends Component { - {filterByBoundsSwitch} - { dispatch(fitToLayerExtent(layerId)); }, - updateSourceProp: (id, propName, value) => dispatch(updateSourceProp(id, propName, value)), + updateSourceProp: (id, propName, value, newLayerType) => + dispatch(updateSourceProp(id, propName, value, newLayerType)), }; } diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js index 755d4bb6b323a..1b269e388bea0 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js @@ -99,8 +99,8 @@ export class LayerPanel extends React.Component { } } - _onSourceChange = ({ propName, value }) => { - this.props.updateSourceProp(this.props.selectedLayer.getId(), propName, value); + _onSourceChange = ({ propName, value, newLayerType }) => { + this.props.updateSourceProp(this.props.selectedLayer.getId(), propName, value, newLayerType); }; _renderFilterSection() { diff --git a/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts b/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts new file mode 100644 index 0000000000000..b35eeedfa44fa --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts @@ -0,0 +1,261 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { VectorLayer } from './vector_layer'; +import { IVectorStyle, VectorStyle } from './styles/vector/vector_style'; +// @ts-ignore +import { getDefaultDynamicProperties, VECTOR_STYLES } from './styles/vector/vector_style_defaults'; +import { IDynamicStyleProperty } from './styles/vector/properties/dynamic_style_property'; +import { IStyleProperty } from './styles/vector/properties/style_property'; +import { + COUNT_PROP_LABEL, + COUNT_PROP_NAME, + ES_GEO_GRID, + LAYER_TYPE, + AGG_TYPE, + SOURCE_DATA_ID_ORIGIN, + RENDER_AS, + STYLE_TYPE, +} from '../../common/constants'; +import { ESGeoGridSource } from './sources/es_geo_grid_source/es_geo_grid_source'; +// @ts-ignore +import { canSkipSourceUpdate } from './util/can_skip_fetch'; +import { IVectorLayer, VectorLayerArguments } from './vector_layer'; +import { IESSource } from './sources/es_source'; +import { IESAggSource } from './sources/es_agg_source'; +import { ISource } from './sources/source'; +import { SyncContext } from '../actions/map_actions'; +import { DataRequestAbortError } from './util/data_request'; + +const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID'; + +function getAggType(dynamicProperty: IDynamicStyleProperty): AGG_TYPE { + return dynamicProperty.isOrdinal() ? AGG_TYPE.AVG : AGG_TYPE.TERMS; +} + +function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle): IESAggSource { + const clusterSourceDescriptor = ESGeoGridSource.createDescriptor({ + indexPatternId: documentSource.getIndexPatternId(), + geoField: documentSource.getGeoFieldName(), + requestType: RENDER_AS.POINT, + }); + clusterSourceDescriptor.metrics = [ + { + type: AGG_TYPE.COUNT, + label: COUNT_PROP_LABEL, + }, + ...documentStyle.getDynamicPropertiesArray().map(dynamicProperty => { + return { + type: getAggType(dynamicProperty), + field: dynamicProperty.getFieldName(), + }; + }), + ]; + clusterSourceDescriptor.id = documentSource.getId(); + return new ESGeoGridSource(clusterSourceDescriptor, documentSource.getInspectorAdapters()); +} + +function getClusterStyleDescriptor( + documentStyle: IVectorStyle, + clusterSource: IESAggSource +): unknown { + const defaultDynamicProperties = getDefaultDynamicProperties(); + const clusterStyleDescriptor: any = { + ...documentStyle.getDescriptor(), + properties: { + [VECTOR_STYLES.LABEL_TEXT]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...defaultDynamicProperties[VECTOR_STYLES.LABEL_TEXT].options, + field: { + name: COUNT_PROP_NAME, + origin: SOURCE_DATA_ID_ORIGIN, + }, + }, + }, + [VECTOR_STYLES.ICON_SIZE]: { + type: STYLE_TYPE.DYNAMIC, + options: { + ...defaultDynamicProperties[VECTOR_STYLES.ICON_SIZE].options, + field: { + name: COUNT_PROP_NAME, + origin: SOURCE_DATA_ID_ORIGIN, + }, + }, + }, + }, + }; + documentStyle.getAllStyleProperties().forEach((styleProperty: IStyleProperty) => { + const styleName = styleProperty.getStyleName(); + if ( + [VECTOR_STYLES.LABEL_TEXT, VECTOR_STYLES.ICON_SIZE].includes(styleName) && + (!styleProperty.isDynamic() || !styleProperty.isComplete()) + ) { + // Do not migrate static label and icon size properties to provide unique cluster styling out of the box + return; + } + + if (styleProperty.isDynamic()) { + const options = (styleProperty as IDynamicStyleProperty).getOptions(); + const field = + options && options.field && options.field.name + ? { + ...options.field, + name: clusterSource.getAggKey( + getAggType(styleProperty as IDynamicStyleProperty), + options.field.name + ), + } + : undefined; + clusterStyleDescriptor.properties[styleName] = { + type: STYLE_TYPE.DYNAMIC, + options: { + ...options, + field, + }, + }; + } else { + clusterStyleDescriptor.properties[styleName] = { + type: STYLE_TYPE.STATIC, + options: { ...styleProperty.getOptions() }, + }; + } + }); + + return clusterStyleDescriptor; +} + +export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { + static type = LAYER_TYPE.BLENDED_VECTOR; + + static createDescriptor(options: VectorLayerArguments, mapColors: string[]) { + const layerDescriptor = VectorLayer.createDescriptor(options, mapColors); + layerDescriptor.type = BlendedVectorLayer.type; + return layerDescriptor; + } + + private readonly _isClustered: boolean; + private readonly _clusterSource: IESAggSource; + private readonly _clusterStyle: IVectorStyle; + private readonly _documentSource: IESSource; + private readonly _documentStyle: IVectorStyle; + + constructor(options: VectorLayerArguments) { + super(options); + + this._documentSource = this._source as IESSource; // VectorLayer constructor sets _source as document source + this._documentStyle = this._style; // VectorLayer constructor sets _style as document source + + this._clusterSource = getClusterSource(this._documentSource, this._documentStyle); + const clusterStyleDescriptor = getClusterStyleDescriptor( + this._documentStyle, + this._clusterSource + ); + this._clusterStyle = new VectorStyle(clusterStyleDescriptor, this._clusterSource, this); + + let isClustered = false; + const sourceDataRequest = this.getSourceDataRequest(); + if (sourceDataRequest) { + const requestMeta = sourceDataRequest.getMeta(); + if (requestMeta && requestMeta.sourceType && requestMeta.sourceType === ES_GEO_GRID) { + isClustered = true; + } + } + this._isClustered = isClustered; + } + + destroy() { + if (this._documentSource) { + this._documentSource.destroy(); + } + if (this._clusterSource) { + this._clusterSource.destroy(); + } + } + + async getDisplayName(source: ISource) { + const displayName = await super.getDisplayName(source); + return this._isClustered + ? i18n.translate('xpack.maps.blendedVectorLayer.clusteredLayerName', { + defaultMessage: 'Clustered {displayName}', + values: { displayName }, + }) + : displayName; + } + + isJoinable() { + return false; + } + + getJoins() { + return []; + } + + getSource() { + return this._isClustered ? this._clusterSource : this._documentSource; + } + + getSourceForEditing() { + // Layer is based on this._documentSource + // this._clusterSource is a derived source for rendering only. + // Regardless of this._activeSource, this._documentSource should always be displayed in the editor + return this._documentSource; + } + + getCurrentStyle() { + return this._isClustered ? this._clusterStyle : this._documentStyle; + } + + getStyleForEditing() { + return this._documentStyle; + } + + async syncData(syncContext: SyncContext) { + const dataRequestId = ACTIVE_COUNT_DATA_ID; + const requestToken = Symbol(`layer-active-count:${this.getId()}`); + const searchFilters = this._getSearchFilters( + syncContext.dataFilters, + this.getSource(), + this.getCurrentStyle() + ); + const canSkipFetch = await canSkipSourceUpdate({ + source: this.getSource(), + prevDataRequest: this.getDataRequest(dataRequestId), + nextMeta: searchFilters, + }); + if (canSkipFetch) { + return; + } + + let isSyncClustered; + try { + syncContext.startLoading(dataRequestId, requestToken, searchFilters); + const searchSource = await this._documentSource.makeSearchSource(searchFilters, 0); + const resp = await searchSource.fetch(); + const maxResultWindow = await this._documentSource.getMaxResultWindow(); + isSyncClustered = resp.hits.total > maxResultWindow; + syncContext.stopLoading(dataRequestId, requestToken, { isSyncClustered }, searchFilters); + } catch (error) { + if (!(error instanceof DataRequestAbortError)) { + syncContext.onLoadError(dataRequestId, requestToken, error.message); + } + return; + } + + let activeSource; + let activeStyle; + if (isSyncClustered) { + activeSource = this._clusterSource; + activeStyle = this._clusterStyle; + } else { + activeSource = this._documentSource; + activeStyle = this._documentStyle; + } + + super._syncData(syncContext, activeSource, activeStyle); + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js index 29223d6a67c6b..ef78b5afe3a3a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js @@ -32,7 +32,7 @@ export class HeatmapLayer extends VectorLayer { } _getPropKeyOfSelectedMetric() { - const metricfields = this._source.getMetricFields(); + const metricfields = this.getSource().getMetricFields(); return metricfields[0].getName(); } @@ -84,11 +84,11 @@ export class HeatmapLayer extends VectorLayer { } this.syncVisibilityWithMb(mbMap, heatmapLayerId); - this._style.setMBPaintProperties({ + this.getCurrentStyle().setMBPaintProperties({ mbMap, layerId: heatmapLayerId, propertyName: SCALED_PROPERTY_NAME, - resolution: this._source.getGridResolution(), + resolution: this.getSource().getGridResolution(), }); mbMap.setPaintProperty(heatmapLayerId, 'heatmap-opacity', this.getAlpha()); mbMap.setLayerZoomRange(heatmapLayerId, this._descriptor.minZoom, this._descriptor.maxZoom); @@ -103,7 +103,7 @@ export class HeatmapLayer extends VectorLayer { } renderLegendDetails() { - const metricFields = this._source.getMetricFields(); - return this._style.renderLegendDetails(metricFields[0]); + const metricFields = this.getSource().getMetricFields(); + return this.getCurrentStyle().renderLegendDetails(metricFields[0]); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.d.ts b/x-pack/legacy/plugins/maps/public/layers/layer.d.ts index eebbaac7d4f97..777566298e607 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/layer.d.ts @@ -5,9 +5,17 @@ */ import { LayerDescriptor } from '../../common/descriptor_types'; import { ISource } from './sources/source'; +import { DataRequest } from './util/data_request'; +import { SyncContext } from '../actions/map_actions'; export interface ILayer { - getDisplayName(): Promise; + getDataRequest(id: string): DataRequest | undefined; + getDisplayName(source?: ISource): Promise; + getId(): string; + getSourceDataRequest(): DataRequest | undefined; + getSource(): ISource; + getSourceForEditing(): ISource; + syncData(syncContext: SyncContext): Promise; } export interface ILayerArguments { @@ -17,5 +25,11 @@ export interface ILayerArguments { export class AbstractLayer implements ILayer { constructor(layerArguments: ILayerArguments); - getDisplayName(): Promise; + getDataRequest(id: string): DataRequest | undefined; + getDisplayName(source?: ISource): Promise; + getId(): string; + getSourceDataRequest(): DataRequest | undefined; + getSource(): ISource; + getSourceForEditing(): ISource; + syncData(syncContext: SyncContext): Promise; } diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/legacy/plugins/maps/public/layers/layer.js index 5c9532a3841f3..d162e342dfd1a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/layer.js @@ -63,7 +63,7 @@ export class AbstractLayer { clonedDescriptor.id = uuid(); const displayName = await this.getDisplayName(); clonedDescriptor.label = `Clone of ${displayName}`; - clonedDescriptor.sourceDescriptor = this._source.cloneDescriptor(); + clonedDescriptor.sourceDescriptor = this.getSource().cloneDescriptor(); if (clonedDescriptor.joins) { clonedDescriptor.joins.forEach(joinDescriptor => { // right.id is uuid used to track requests in inspector @@ -78,28 +78,31 @@ export class AbstractLayer { } isJoinable() { - return this._source.isJoinable(); + return this.getSource().isJoinable(); } supportsElasticsearchFilters() { - return this._source.isESSource(); + return this.getSource().isESSource(); } async supportsFitToBounds() { - return await this._source.supportsFitToBounds(); + return await this.getSource().supportsFitToBounds(); } - async getDisplayName() { + async getDisplayName(source) { if (this._descriptor.label) { return this._descriptor.label; } - return (await this._source.getDisplayName()) || `Layer ${this._descriptor.id}`; + const sourceDisplayName = source + ? await source.getDisplayName() + : await this.getSource().getDisplayName(); + return sourceDisplayName || `Layer ${this._descriptor.id}`; } async getAttributions() { if (!this.hasErrors()) { - return await this._source.getAttributions(); + return await this.getSource().getAttributions(); } return []; } @@ -191,6 +194,10 @@ export class AbstractLayer { return this._source; } + getSourceForEditing() { + return this._source; + } + isVisible() { return this._descriptor.visible; } @@ -226,12 +233,16 @@ export class AbstractLayer { return this._style; } + getStyleForEditing() { + return this._style; + } + async getImmutableSourceProperties() { - return this._source.getImmutableProperties(); + return this.getSource().getImmutableProperties(); } renderSourceSettingsEditor = ({ onChange }) => { - return this._source.renderSourceSettingsEditor({ onChange }); + return this.getSourceForEditing().renderSourceSettingsEditor({ onChange }); }; getPrevRequestToken(dataId) { @@ -319,10 +330,11 @@ export class AbstractLayer { } renderStyleEditor({ onStyleDescriptorChange }) { - if (!this._style) { + const style = this.getStyleForEditing(); + if (!style) { return null; } - return this._style.renderEditor({ layer: this, onStyleDescriptorChange }); + return style.renderEditor({ layer: this, onStyleDescriptorChange }); } getIndexPatternIds() { @@ -333,10 +345,6 @@ export class AbstractLayer { return []; } - async getFields() { - return []; - } - syncVisibilityWithMb(mbMap, mbLayerId) { mbMap.setLayoutProperty(mbLayerId, 'visibility', this.isVisible() ? 'visible' : 'none'); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts index 48e90b6c41d51..3f596cea1ae39 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts @@ -6,10 +6,23 @@ import { AbstractESAggSource } from '../es_agg_source'; import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types'; -import { GRID_RESOLUTION } from '../../../../common/constants'; +import { GRID_RESOLUTION, RENDER_AS } from '../../../../common/constants'; export class ESGeoGridSource extends AbstractESAggSource { + static createDescriptor({ + indexPatternId, + geoField, + requestType, + resolution, + }: { + indexPatternId: string; + geoField: string; + requestType: RENDER_AS; + resolution?: GRID_RESOLUTION; + }): ESGeoGridSourceDescriptor; + constructor(sourceDescriptor: ESGeoGridSourceDescriptor, inspectorAdapters: unknown); + getGridResolution(): GRID_RESOLUTION; getGeoGridPrecision(zoom: number): number; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js index 3b3e8004ded05..5ad202a02ae6d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -75,7 +75,7 @@ export class ESGeoGridSource extends AbstractESAggSource { renderSourceSettingsEditor({ onChange }) { return ( { @@ -325,6 +325,7 @@ export class ESGeoGridSource extends AbstractESAggSource { }, meta: { areResultsTrimmed: false, + sourceType: ES_GEO_GRID, }, }; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js index 53536b11aaca6..8e1145c531f9e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js @@ -64,7 +64,7 @@ export class ESPewPewSource extends AbstractESAggSource { renderSourceSettingsEditor({ onChange }) { return ( - + + + +
+ +
+
+ + + + @@ -112,7 +152,7 @@ exports[`should enable sort order select when sort field provided 1`] = `
`; -exports[`should render top hits form when useTopHits is true 1`] = ` +exports[`should render top hits form when scaling type is TOP_HITS 1`] = ` - + + + +
+ +
+
+ + + + + - + + + +
+ +
+
+ + + + diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts index 5d8188f19f4ea..0a4e48a195ec6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts @@ -8,5 +8,5 @@ import { AbstractESSource } from '../es_source'; import { ESSearchSourceDescriptor } from '../../../../common/descriptor_types'; export class ESSearchSource extends AbstractESSource { - constructor(sourceDescriptor: ESSearchSourceDescriptor, inspectorAdapters: unknown); + constructor(sourceDescriptor: Partial, inspectorAdapters: unknown); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 7f0e870760512..440b9aa89a945 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -11,6 +11,8 @@ import uuid from 'uuid/v4'; import { VECTOR_SHAPE_TYPES } from '../vector_feature_types'; import { AbstractESSource } from '../es_source'; import { SearchSource } from '../../../kibana_services'; +import { VectorStyle } from '../../styles/vector/vector_style'; +import { VectorLayer } from '../../vector_layer'; import { hitsToGeoJson } from '../../../elasticsearch_geo_utils'; import { CreateSourceEditor } from './create_source_editor'; import { UpdateSourceEditor } from './update_source_editor'; @@ -19,11 +21,13 @@ import { ES_GEO_FIELD_TYPE, DEFAULT_MAX_BUCKETS_LIMIT, SORT_ORDER, + SCALING_TYPES, } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { getSourceFields } from '../../../index_pattern_util'; import { loadIndexSettings } from './load_index_settings'; +import { BlendedVectorLayer } from '../../blended_vector_layer'; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants'; import { ESDocField } from '../../fields/es_doc_field'; @@ -99,7 +103,7 @@ export class ESSearchSource extends AbstractESSource { tooltipProperties: _.get(descriptor, 'tooltipProperties', []), sortField: _.get(descriptor, 'sortField', ''), sortOrder: _.get(descriptor, 'sortOrder', SORT_ORDER.DESC), - useTopHits: _.get(descriptor, 'useTopHits', false), + scalingType: _.get(descriptor, 'scalingType', SCALING_TYPES.LIMIT), topHitsSplitField: descriptor.topHitsSplitField, topHitsSize: _.get(descriptor, 'topHitsSize', 1), }, @@ -111,6 +115,32 @@ export class ESSearchSource extends AbstractESSource { ); } + createDefaultLayer(options, mapColors) { + if (this._descriptor.scalingType === SCALING_TYPES.CLUSTERS) { + const layerDescriptor = BlendedVectorLayer.createDescriptor( + { + sourceDescriptor: this._descriptor, + ...options, + }, + mapColors + ); + const style = new VectorStyle(layerDescriptor.style, this); + return new BlendedVectorLayer({ + layerDescriptor: layerDescriptor, + source: this, + style, + }); + } + + const layerDescriptor = this._createDefaultLayerDescriptor(options, mapColors); + const style = new VectorStyle(layerDescriptor.style, this); + return new VectorLayer({ + layerDescriptor: layerDescriptor, + source: this, + style, + }); + } + createField({ fieldName }) { return new ESDocField({ fieldName, @@ -122,12 +152,14 @@ export class ESSearchSource extends AbstractESSource { return ( @@ -157,7 +189,7 @@ export class ESSearchSource extends AbstractESSource { } async getImmutableProperties() { - let indexPatternTitle = this._descriptor.indexPatternId; + let indexPatternTitle = this.getIndexPatternId(); let geoFieldType = ''; try { const indexPattern = await this.getIndexPattern(); @@ -239,7 +271,7 @@ export class ESSearchSource extends AbstractESSource { shard_size: DEFAULT_MAX_BUCKETS_LIMIT, }; - const searchSource = await this._makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(searchFilters, 0); searchSource.setField('aggs', { totalEntities: { cardinality: addFieldToDSL(cardinalityAgg, topHitsSplitField), @@ -300,7 +332,7 @@ export class ESSearchSource extends AbstractESSource { ); const initialSearchContext = { docvalue_fields: docValueFields }; // Request fields in docvalue_fields insted of _source - const searchSource = await this._makeSearchSource( + const searchSource = await this.makeSearchSource( searchFilters, maxResultWindow, initialSearchContext @@ -332,8 +364,8 @@ export class ESSearchSource extends AbstractESSource { } _isTopHits() { - const { useTopHits, topHitsSplitField } = this._descriptor; - return !!(useTopHits && topHitsSplitField); + const { scalingType, topHitsSplitField } = this._descriptor; + return !!(scalingType === SCALING_TYPES.TOP_HITS && topHitsSplitField); } _hasSort() { @@ -341,6 +373,12 @@ export class ESSearchSource extends AbstractESSource { return !!sortField && !!sortOrder; } + async getMaxResultWindow() { + const indexPattern = await this.getIndexPattern(); + const indexSettings = await loadIndexSettings(indexPattern.title); + return indexSettings.maxResultWindow; + } + async getGeoJsonWithMeta(layerName, searchFilters, registerCancelCallback) { const indexPattern = await this.getIndexPattern(); @@ -383,7 +421,7 @@ export class ESSearchSource extends AbstractESSource { return { data: featureCollection, - meta, + meta: { ...meta, sourceType: ES_SEARCH }, }; } @@ -442,11 +480,9 @@ export class ESSearchSource extends AbstractESSource { } isFilterByMapBounds() { - return _.get(this._descriptor, 'filterByMapBounds', false); - } - - isFilterByMapBoundsConfigurable() { - return true; + return this._descriptor.scalingType === SCALING_TYPES.CLUSTER + ? true + : this._descriptor.filterByMapBounds; } async getLeftJoinFields() { @@ -533,7 +569,7 @@ export class ESSearchSource extends AbstractESSource { return { sortField: this._descriptor.sortField, sortOrder: this._descriptor.sortOrder, - useTopHits: this._descriptor.useTopHits, + scalingType: this._descriptor.scalingType, topHitsSplitField: this._descriptor.topHitsSplitField, topHitsSize: this._descriptor.topHitsSize, }; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts index 1e10923cea1d0..59120e221ca49 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts @@ -7,7 +7,7 @@ jest.mock('ui/new_platform'); import { ESSearchSource } from './es_search_source'; import { VectorLayer } from '../../vector_layer'; -import { ES_SEARCH } from '../../../../common/constants'; +import { ES_SEARCH, SCALING_TYPES } from '../../../../common/constants'; import { ESSearchSourceDescriptor } from '../../../../common/descriptor_types'; const descriptor: ESSearchSourceDescriptor = { @@ -15,6 +15,7 @@ const descriptor: ESSearchSourceDescriptor = { id: '1234', indexPatternId: 'myIndexPattern', geoField: 'myLocation', + scalingType: SCALING_TYPES.LIMIT, }; describe('ES Search Source', () => { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js index 52702c1f4ecc7..b85cca113cf98 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js @@ -14,6 +14,7 @@ import { EuiPanel, EuiSpacer, EuiHorizontalRule, + EuiRadioGroup, } from '@elastic/eui'; import { SingleFieldSelect } from '../../../components/single_field_select'; import { TooltipSelector } from '../../../components/tooltip_selector'; @@ -22,7 +23,13 @@ import { indexPatternService } from '../../../kibana_services'; import { i18n } from '@kbn/i18n'; import { getTermsFields, getSourceFields } from '../../../index_pattern_util'; import { ValidatedRange } from '../../../components/validated_range'; -import { DEFAULT_MAX_INNER_RESULT_WINDOW, SORT_ORDER } from '../../../../common/constants'; +import { + DEFAULT_MAX_INNER_RESULT_WINDOW, + DEFAULT_MAX_RESULT_WINDOW, + SORT_ORDER, + SCALING_TYPES, + LAYER_TYPE, +} from '../../../../common/constants'; import { ESDocField } from '../../fields/es_doc_field'; import { FormattedMessage } from '@kbn/i18n/react'; import { loadIndexSettings } from './load_index_settings'; @@ -35,7 +42,7 @@ export class UpdateSourceEditor extends Component { tooltipFields: PropTypes.arrayOf(PropTypes.object).isRequired, sortField: PropTypes.string, sortOrder: PropTypes.string.isRequired, - useTopHits: PropTypes.bool.isRequired, + scalingType: PropTypes.string.isRequired, topHitsSplitField: PropTypes.string, topHitsSize: PropTypes.number.isRequired, source: PropTypes.object, @@ -46,6 +53,8 @@ export class UpdateSourceEditor extends Component { termFields: null, sortFields: null, maxInnerResultWindow: DEFAULT_MAX_INNER_RESULT_WINDOW, + maxResultWindow: DEFAULT_MAX_RESULT_WINDOW, + supportsClustering: false, }; componentDidMount() { @@ -61,9 +70,9 @@ export class UpdateSourceEditor extends Component { async loadIndexSettings() { try { const indexPattern = await indexPatternService.get(this.props.indexPatternId); - const { maxInnerResultWindow } = await loadIndexSettings(indexPattern.title); + const { maxInnerResultWindow, maxResultWindow } = await loadIndexSettings(indexPattern.title); if (this._isMounted) { - this.setState({ maxInnerResultWindow }); + this.setState({ maxInnerResultWindow, maxResultWindow }); } } catch (err) { return; @@ -88,6 +97,16 @@ export class UpdateSourceEditor extends Component { return; } + let geoField; + try { + geoField = await this.props.getGeoField(); + } catch (err) { + if (this._isMounted) { + this.setState({ loadError: err.message }); + } + return; + } + if (!this._isMounted) { return; } @@ -102,6 +121,7 @@ export class UpdateSourceEditor extends Component { }); this.setState({ + supportsClustering: geoField.aggregatable, sourceFields: sourceFields, termFields: getTermsFields(indexPattern.fields), //todo change term fields to use fields sortFields: indexPattern.fields.filter( @@ -113,8 +133,14 @@ export class UpdateSourceEditor extends Component { this.props.onChange({ propName: 'tooltipProperties', value: propertyNames }); }; - onUseTopHitsChange = event => { - this.props.onChange({ propName: 'useTopHits', value: event.target.checked }); + _onScalingTypeChange = optionId => { + const layerType = + optionId === SCALING_TYPES.CLUSTERS ? LAYER_TYPE.BLENDED_VECTOR : LAYER_TYPE.VECTOR; + this.props.onChange({ propName: 'scalingType', value: optionId, newLayerType: layerType }); + }; + + _onFilterByMapBoundsChange = event => { + this.props.onChange({ propName: 'filterByMapBounds', value: event.target.checked }); }; onTopHitsSplitFieldChange = topHitsSplitField => { @@ -133,29 +159,7 @@ export class UpdateSourceEditor extends Component { this.props.onChange({ propName: 'topHitsSize', value: size }); }; - renderTopHitsForm() { - const topHitsSwitch = ( - - - - ); - - if (!this.props.useTopHits) { - return topHitsSwitch; - } - + _renderTopHitsForm() { let sizeSlider; if (this.props.topHitsSplitField) { sizeSlider = ( @@ -183,7 +187,6 @@ export class UpdateSourceEditor extends Component { return ( - {topHitsSwitch} +
+ ); + } + + _renderScalingPanel() { + const scalingOptions = [ + { + id: SCALING_TYPES.LIMIT, + label: i18n.translate('xpack.maps.source.esSearch.limitScalingLabel', { + defaultMessage: 'Limit results to {maxResultWindow}.', + values: { maxResultWindow: this.state.maxResultWindow }, + }), + }, + { + id: SCALING_TYPES.TOP_HITS, + label: i18n.translate('xpack.maps.source.esSearch.useTopHitsLabel', { + defaultMessage: 'Show top hits per entity.', + }), + }, + ]; + if (this.state.supportsClustering) { + scalingOptions.push({ + id: SCALING_TYPES.CLUSTERS, + label: i18n.translate('xpack.maps.source.esSearch.clusterScalingLabel', { + defaultMessage: 'Show clusters when results exceed {maxResultWindow}.', + values: { maxResultWindow: this.state.maxResultWindow }, + }), + }); + } - - {this.renderTopHitsForm()} + let filterByBoundsSwitch; + if (this.props.scalingType !== SCALING_TYPES.CLUSTERS) { + filterByBoundsSwitch = ( + + + + ); + } + + let scalingForm = null; + if (this.props.scalingType === SCALING_TYPES.TOP_HITS) { + scalingForm = ( + + + {this._renderTopHitsForm()} + + ); + } + + return ( + + +
+ +
+
+ + + + + + + + {filterByBoundsSwitch} + + {scalingForm}
); } @@ -302,6 +379,9 @@ export class UpdateSourceEditor extends Component { {this._renderSortPanel()} + + {this._renderScalingPanel()} +
); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js index badfba7665dfd..e8a845c4b1669 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js @@ -16,6 +16,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { UpdateSourceEditor } from './update_source_editor'; +import { SCALING_TYPES } from '../../../../common/constants'; const defaultProps = { indexPatternId: 'indexPattern1', @@ -23,7 +24,7 @@ const defaultProps = { filterByMapBounds: true, tooltipFields: [], sortOrder: 'DESC', - useTopHits: false, + scalingType: SCALING_TYPES.LIMIT, topHitsSplitField: 'trackId', topHitsSize: 1, }; @@ -40,8 +41,10 @@ test('should enable sort order select when sort field provided', async () => { expect(component).toMatchSnapshot(); }); -test('should render top hits form when useTopHits is true', async () => { - const component = shallow(); +test('should render top hits form when scaling type is TOP_HITS', async () => { + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts index 25c4fae89f024..963a30c7413e8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts @@ -6,12 +6,31 @@ import { AbstractVectorSource } from './vector_source'; import { IVectorSource } from './vector_source'; -import { IndexPattern } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern, SearchSource } from '../../../../../../../src/plugins/data/public'; +import { VectorLayerRequestMeta } from '../../../common/data_request_descriptor_types'; export interface IESSource extends IVectorSource { + getId(): string; getIndexPattern(): Promise; + getIndexPatternId(): string; + getGeoFieldName(): string; + getMaxResultWindow(): Promise; + makeSearchSource( + searchFilters: VectorLayerRequestMeta, + limit: number, + initialSearchContext?: object + ): Promise; } export class AbstractESSource extends AbstractVectorSource implements IESSource { + getId(): string; getIndexPattern(): Promise; + getIndexPatternId(): string; + getGeoFieldName(): string; + getMaxResultWindow(): Promise; + makeSearchSource( + searchFilters: VectorLayerRequestMeta, + limit: number, + initialSearchContext?: object + ): Promise; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 1552db277e609..c5bf9a8be75bd 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -35,6 +35,10 @@ export class AbstractESSource extends AbstractVectorSource { ); } + getId() { + return this._descriptor.id; + } + isFieldAware() { return true; } @@ -48,12 +52,12 @@ export class AbstractESSource extends AbstractVectorSource { } getIndexPatternIds() { - return [this._descriptor.indexPatternId]; + return [this.getIndexPatternId()]; } getQueryableIndexPatternIds() { if (this.getApplyGlobalQuery()) { - return [this._descriptor.indexPatternId]; + return [this.getIndexPatternId()]; } return []; } @@ -106,7 +110,7 @@ export class AbstractESSource extends AbstractVectorSource { } } - async _makeSearchSource(searchFilters, limit, initialSearchContext) { + async makeSearchSource(searchFilters, limit, initialSearchContext) { const indexPattern = await this.getIndexPattern(); const isTimeAware = await this.isTimeAware(); const applyGlobalQuery = _.get(searchFilters, 'applyGlobalQuery', true); @@ -143,7 +147,7 @@ export class AbstractESSource extends AbstractVectorSource { } async getBoundsForFilters({ sourceQuery, query, timeFilters, filters, applyGlobalQuery }) { - const searchSource = await this._makeSearchSource( + const searchSource = await this.makeSearchSource( { sourceQuery, query, timeFilters, filters, applyGlobalQuery }, 0 ); @@ -190,19 +194,27 @@ export class AbstractESSource extends AbstractVectorSource { } } + getIndexPatternId() { + return this._descriptor.indexPatternId; + } + + getGeoFieldName() { + return this._descriptor.geoField; + } + async getIndexPattern() { if (this.indexPattern) { return this.indexPattern; } try { - this.indexPattern = await indexPatternService.get(this._descriptor.indexPatternId); + this.indexPattern = await indexPatternService.get(this.getIndexPatternId()); return this.indexPattern; } catch (error) { throw new Error( i18n.translate('xpack.maps.source.esSource.noIndexPatternErrorMessage', { defaultMessage: `Unable to find Index pattern for id: {indexPatternId}`, - values: { indexPatternId: this._descriptor.indexPatternId }, + values: { indexPatternId: this.getIndexPatternId() }, }) ); } @@ -219,7 +231,7 @@ export class AbstractESSource extends AbstractVectorSource { } } - async _getGeoField() { + _getGeoField = async () => { const indexPattern = await this.getIndexPattern(); const geoField = indexPattern.fields.getByName(this._descriptor.geoField); if (!geoField) { @@ -231,7 +243,7 @@ export class AbstractESSource extends AbstractVectorSource { ); } return geoField; - } + }; async getDisplayName() { try { @@ -239,7 +251,7 @@ export class AbstractESSource extends AbstractVectorSource { return indexPattern.title; } catch (error) { // Unable to load index pattern, just return id as display name - return this._descriptor.indexPatternId; + return this.getIndexPatternId(); } } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js index c12b4befc0684..3ce0fb58aba19 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js @@ -51,10 +51,6 @@ export class ESTermSource extends AbstractESAggSource { return _.has(this._descriptor, 'indexPatternId') && _.has(this._descriptor, 'term'); } - getIndexPatternIds() { - return [this._descriptor.indexPatternId]; - } - getTermField() { return this._termField; } @@ -90,7 +86,7 @@ export class ESTermSource extends AbstractESAggSource { } const indexPattern = await this.getIndexPattern(); - const searchSource = await this._makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(searchFilters, 0); const termsField = getField(indexPattern, this._termField.getName()); const termsAgg = { size: DEFAULT_MAX_BUCKETS_LIMIT }; searchSource.setField('aggs', { @@ -126,7 +122,7 @@ export class ESTermSource extends AbstractESAggSource { async getDisplayName() { //no need to localize. this is never rendered. - return `es_table ${this._descriptor.indexPatternId}`; + return `es_table ${this.getIndexPatternId()}`; } async filterAndFormatPropertiesToHtml(properties) { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts index b5b34efabda0a..2ca18e47a4bf9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts @@ -10,10 +10,14 @@ import { ILayer } from '../layer'; export interface ISource { createDefaultLayer(): ILayer; getDisplayName(): Promise; + destroy(): void; + getInspectorAdapters(): object; } export class AbstractSource implements ISource { constructor(sourceDescriptor: AbstractSourceDescriptor, inspectorAdapters: object); createDefaultLayer(): ILayer; getDisplayName(): Promise; + destroy(): void; + getInspectorAdapters(): object; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts index 7de3fe1823cb7..14fc23751ac1a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts @@ -7,13 +7,9 @@ import { AbstractSource, ISource } from './source'; import { IField } from '../fields/field'; +import { ESSearchSourceResponseMeta } from '../../../common/data_request_descriptor_types'; -export type GeoJsonFetchMeta = { - areResultsTrimmed: boolean; - areEntitiesTrimmed?: boolean; - entityCount?: number; - totalEntities?: number; -}; +export type GeoJsonFetchMeta = ESSearchSourceResponseMeta; export type GeoJsonWithMeta = { data: unknown; // geojson feature collection diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index 0f74dd605c8f1..7ff1c735c8613 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -98,10 +98,6 @@ export class AbstractVectorSource extends AbstractSource { return false; } - isFilterByMapBoundsConfigurable() { - return false; - } - isBoundsAware() { return false; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js index 8e05cf287efa6..acc26e5fce699 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js @@ -69,7 +69,7 @@ export class VectorStyleEditor extends Component { }; //These are all fields (only used for text labeling) - const fields = await this.props.layer.getFields(); + const fields = await this.props.layer.getStyleEditorFields(); const fieldPromises = fields.map(getFieldMeta); const fieldsArrayAll = await Promise.all(fieldPromises); if (!this._isMounted || _.isEqual(fieldsArrayAll, this.state.fields)) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js index f74deb17fff7c..5b5028f68f08c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js @@ -71,7 +71,7 @@ class MockLayer { return new MockStyle(); } - findDataRequestById() { + getDataRequest() { return null; } } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts index f4c487b28757e..25063944b8891 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts @@ -7,13 +7,17 @@ import { IStyleProperty } from './style_property'; import { FIELD_ORIGIN } from '../../../../../common/constants'; -import { FieldMetaOptions } from '../../../../../common/style_property_descriptor_types'; +import { + FieldMetaOptions, + DynamicStylePropertyOptions, +} from '../../../../../common/style_property_descriptor_types'; import { IField } from '../../../fields/field'; import { IVectorLayer } from '../../../vector_layer'; import { IVectorSource } from '../../../sources/vector_source'; import { CategoryFieldMeta, RangeFieldMeta } from '../../../../../common/descriptor_types'; export interface IDynamicStyleProperty extends IStyleProperty { + getOptions(): DynamicStylePropertyOptions; getFieldMetaOptions(): FieldMetaOptions; getField(): IField | undefined; getFieldName(): string; @@ -22,6 +26,7 @@ export interface IDynamicStyleProperty extends IStyleProperty { getRangeFieldMeta(): RangeFieldMeta; getCategoryFieldMeta(): CategoryFieldMeta; isFieldMetaEnabled(): boolean; + isOrdinal(): boolean; supportsFieldMeta(): boolean; getFieldMetaRequest(): Promise; supportsMbFeatureState(): boolean; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index 030d3a2a1ef87..68e06bacfa7b7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -62,7 +62,7 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return rangeFieldMetaFromLocalFeatures; } - const styleMetaDataRequest = this._layer.findDataRequestById(dataRequestId); + const styleMetaDataRequest = this._layer.getDataRequest(dataRequestId); if (!styleMetaDataRequest || !styleMetaDataRequest.hasData()) { return rangeFieldMetaFromLocalFeatures; } @@ -87,7 +87,7 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return categoryFieldMetaFromLocalFeatures; } - const styleMetaDataRequest = this._layer.findDataRequestById(dataRequestId); + const styleMetaDataRequest = this._layer.getDataRequest(dataRequestId); if (!styleMetaDataRequest || !styleMetaDataRequest.hasData()) { return categoryFieldMetaFromLocalFeatures; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.d.ts b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.d.ts new file mode 100644 index 0000000000000..ac84a3b6447d2 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.d.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 { IStyleProperty } from './properties/style_property'; +import { IDynamicStyleProperty } from './properties/dynamic_style_property'; +import { IVectorLayer } from '../../vector_layer'; +import { IVectorSource } from '../../sources/vector_source'; + +export interface IVectorStyle { + getAllStyleProperties(): IStyleProperty[]; + getDescriptor(): object; + getDynamicPropertiesArray(): IDynamicStyleProperty[]; +} + +export class VectorStyle implements IVectorStyle { + constructor(descriptor: unknown, source: IVectorSource, layer: IVectorLayer); + + getAllStyleProperties(): IStyleProperty[]; + getDescriptor(): object; + getDynamicPropertiesArray(): IDynamicStyleProperty[]; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 1c8ff3e205a38..6ad60e15f10e1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -123,7 +123,7 @@ export class VectorStyle extends AbstractStyle { ); } - _getAllStyleProperties() { + getAllStyleProperties() { return [ this._symbolizeAsStyleProperty, this._iconStyleProperty, @@ -164,7 +164,7 @@ export class VectorStyle extends AbstractStyle { }); const styleProperties = {}; - this._getAllStyleProperties().forEach(styleProperty => { + this.getAllStyleProperties().forEach(styleProperty => { styleProperties[styleProperty.getStyleName()] = styleProperty; }); @@ -339,7 +339,7 @@ export class VectorStyle extends AbstractStyle { } getDynamicPropertiesArray() { - const styleProperties = this._getAllStyleProperties(); + const styleProperties = this.getAllStyleProperties(); return styleProperties.filter( styleProperty => styleProperty.isDynamic() && styleProperty.isComplete() ); @@ -390,7 +390,7 @@ export class VectorStyle extends AbstractStyle { return null; } - const formattersDataRequest = this._layer.findDataRequestById(dataRequestId); + const formattersDataRequest = this._layer.getDataRequest(dataRequestId); if (!formattersDataRequest || !formattersDataRequest.hasData()) { return null; } diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js index 8bc397dd98b56..dd2cf79318d8e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.js @@ -16,7 +16,7 @@ import chrome from 'ui/chrome'; export const MIN_SIZE = 1; export const MAX_SIZE = 64; -export const DEFAULT_MIN_SIZE = 4; +export const DEFAULT_MIN_SIZE = 7; // Make default large enough to fit default label size export const DEFAULT_MAX_SIZE = 32; export const DEFAULT_SIGMA = 3; export const DEFAULT_LABEL_SIZE = 14; diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.js b/x-pack/legacy/plugins/maps/public/layers/tile_layer.js index b35adcad976c3..aa2619e96f834 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tile_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/tile_layer.js @@ -30,7 +30,7 @@ export class TileLayer extends AbstractLayer { const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`); startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataFilters); try { - const url = await this._source.getUrlTemplate(); + const url = await this.getSource().getUrlTemplate(); stopLoading(SOURCE_DATA_ID_ORIGIN, requestToken, url, {}); } catch (error) { onLoadError(SOURCE_DATA_ID_ORIGIN, requestToken, error.message); diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts b/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts index 065fbd79d9789..0ec9385194cc0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts +++ b/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts @@ -32,6 +32,14 @@ class MockTileSource implements ITMSSource { async getUrlTemplate(): Promise { return 'template/{x}/{y}/{z}.png'; } + + destroy(): void { + // no-op + } + + getInspectorAdapters(): object { + return {}; + } } describe('TileLayer', () => { diff --git a/x-pack/legacy/plugins/maps/public/layers/util/data_request.js b/x-pack/legacy/plugins/maps/public/layers/util/data_request.ts similarity index 61% rename from x-pack/legacy/plugins/maps/public/layers/util/data_request.js rename to x-pack/legacy/plugins/maps/public/layers/util/data_request.ts index 3a6c10a9f07a6..e361574194628 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/data_request.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/data_request.ts @@ -3,42 +3,47 @@ * 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 _ from 'lodash'; +import { DataRequestDescriptor, DataMeta } from '../../../common/data_request_descriptor_types'; export class DataRequest { - constructor(descriptor) { + private readonly _descriptor: DataRequestDescriptor; + + constructor(descriptor: DataRequestDescriptor) { this._descriptor = { ...descriptor, }; } - getData() { + getData(): object | undefined { return this._descriptor.data; } - isLoading() { + isLoading(): boolean { return !!this._descriptor.dataRequestToken; } - getMeta() { + getMeta(): DataMeta { return this.hasData() ? _.get(this._descriptor, 'dataMeta', {}) : _.get(this._descriptor, 'dataMetaAtStart', {}); } - hasData() { + hasData(): boolean { return !!this._descriptor.data; } - hasDataOrRequestInProgress() { - return this._descriptor.data || this._descriptor.dataRequestToken; + hasDataOrRequestInProgress(): boolean { + return this.hasData() || this.isLoading(); } - getDataId() { + getDataId(): string { return this._descriptor.dataId; } - getRequestToken() { + getRequestToken(): symbol | undefined { return this._descriptor.dataRequestToken; } } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts b/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts index 748b2fd1d782c..77e8ab768cd00 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts @@ -8,20 +8,43 @@ import { AbstractLayer } from './layer'; import { IVectorSource } from './sources/vector_source'; import { VectorLayerDescriptor } from '../../common/descriptor_types'; +import { MapFilters, VectorLayerRequestMeta } from '../../common/data_request_descriptor_types'; import { ILayer } from './layer'; import { IJoin } from './joins/join'; +import { IVectorStyle } from './styles/vector/vector_style'; +import { IField } from './fields/field'; +import { SyncContext } from '../actions/map_actions'; type VectorLayerArguments = { source: IVectorSource; + joins: IJoin[]; layerDescriptor: VectorLayerDescriptor; }; export interface IVectorLayer extends ILayer { + getFields(): Promise; + getStyleEditorFields(): Promise; getValidJoins(): IJoin[]; } export class VectorLayer extends AbstractLayer implements IVectorLayer { + static createDescriptor( + options: VectorLayerArguments, + mapColors: string[] + ): VectorLayerDescriptor; + + protected readonly _source: IVectorSource; + protected readonly _style: IVectorStyle; + constructor(options: VectorLayerArguments); + getFields(): Promise; + getStyleEditorFields(): Promise; getValidJoins(): IJoin[]; + _getSearchFilters( + dataFilters: MapFilters, + source: IVectorSource, + style: IVectorStyle + ): VectorLayerRequestMeta; + _syncData(syncContext: SyncContext, source: IVectorSource, style: IVectorStyle): Promise; } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js index 70bba3d91c723..6b89554546330 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.js @@ -58,7 +58,7 @@ export class VectorLayer extends AbstractLayer { constructor({ layerDescriptor, source, joins = [] }) { super({ layerDescriptor, source }); this._joins = joins; - this._style = new VectorStyle(this._descriptor.style, this._source, this); + this._style = new VectorStyle(this._descriptor.style, source, this); } getStyle() { @@ -66,10 +66,10 @@ export class VectorLayer extends AbstractLayer { } destroy() { - if (this._source) { - this._source.destroy(); + if (this.getSource()) { + this.getSource().destroy(); } - this._joins.forEach(joinSource => { + this.getJoins().forEach(joinSource => { joinSource.destroy(); }); } @@ -79,7 +79,7 @@ export class VectorLayer extends AbstractLayer { } getValidJoins() { - return this._joins.filter(join => { + return this.getJoins().filter(join => { return join.hasCompleteConfig(); }); } @@ -119,7 +119,7 @@ export class VectorLayer extends AbstractLayer { } if ( - this._joins.length && + this.getJoins().length && !featureCollection.features.some(feature => feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]) ) { return { @@ -131,11 +131,11 @@ export class VectorLayer extends AbstractLayer { } const sourceDataRequest = this.getSourceDataRequest(); - const { tooltipContent, areResultsTrimmed } = this._source.getSourceTooltipContent( + const { tooltipContent, areResultsTrimmed } = this.getSource().getSourceTooltipContent( sourceDataRequest ); return { - icon: this._style.getIcon(), + icon: this.getCurrentStyle().getIcon(), tooltipContent: tooltipContent, areResultsTrimmed: areResultsTrimmed, }; @@ -146,11 +146,11 @@ export class VectorLayer extends AbstractLayer { } async hasLegendDetails() { - return this._style.hasLegendDetails(); + return this.getCurrentStyle().hasLegendDetails(); } renderLegendDetails() { - return this._style.renderLegendDetails(); + return this.getCurrentStyle().renderLegendDetails(); } _getBoundsBasedOnData() { @@ -175,17 +175,22 @@ export class VectorLayer extends AbstractLayer { } async getBounds(dataFilters) { - const isStaticLayer = !this._source.isBoundsAware() || !this._source.isFilterByMapBounds(); + const isStaticLayer = + !this.getSource().isBoundsAware() || !this.getSource().isFilterByMapBounds(); if (isStaticLayer) { return this._getBoundsBasedOnData(); } - const searchFilters = this._getSearchFilters(dataFilters); - return await this._source.getBoundsForFilters(searchFilters); + const searchFilters = this._getSearchFilters( + dataFilters, + this.getSource(), + this.getCurrentStyle() + ); + return await this.getSource().getBoundsForFilters(searchFilters); } async getLeftJoinFields() { - return await this._source.getLeftJoinFields(); + return await this.getSource().getLeftJoinFields(); } _getJoinFields() { @@ -198,12 +203,17 @@ export class VectorLayer extends AbstractLayer { } async getFields() { - const sourceFields = await this._source.getFields(); + const sourceFields = await this.getSource().getFields(); + return [...sourceFields, ...this._getJoinFields()]; + } + + async getStyleEditorFields() { + const sourceFields = await this.getSourceForEditing().getFields(); return [...sourceFields, ...this._getJoinFields()]; } getIndexPatternIds() { - const indexPatternIds = this._source.getIndexPatternIds(); + const indexPatternIds = this.getSource().getIndexPatternIds(); this.getValidJoins().forEach(join => { indexPatternIds.push(...join.getIndexPatternIds()); }); @@ -211,17 +221,13 @@ export class VectorLayer extends AbstractLayer { } getQueryableIndexPatternIds() { - const indexPatternIds = this._source.getQueryableIndexPatternIds(); + const indexPatternIds = this.getSource().getQueryableIndexPatternIds(); this.getValidJoins().forEach(join => { indexPatternIds.push(...join.getQueryableIndexPatternIds()); }); return indexPatternIds; } - findDataRequestById(sourceDataId) { - return this._dataRequests.find(dataRequest => dataRequest.getDataId() === sourceDataId); - } - async _syncJoin({ join, startLoading, @@ -239,7 +245,7 @@ export class VectorLayer extends AbstractLayer { sourceQuery: joinSource.getWhereQuery(), applyGlobalQuery: joinSource.getApplyGlobalQuery(), }; - const prevDataRequest = this.findDataRequestById(sourceDataId); + const prevDataRequest = this.getDataRequest(sourceDataId); const canSkipFetch = await canSkipSourceUpdate({ source: joinSource, @@ -281,30 +287,30 @@ export class VectorLayer extends AbstractLayer { } } - async _syncJoins(syncContext) { + async _syncJoins(syncContext, style) { const joinSyncs = this.getValidJoins().map(async join => { - await this._syncJoinStyleMeta(syncContext, join); - await this._syncJoinFormatters(syncContext, join); + await this._syncJoinStyleMeta(syncContext, join, style); + await this._syncJoinFormatters(syncContext, join, style); return this._syncJoin({ join, ...syncContext }); }); return await Promise.all(joinSyncs); } - _getSearchFilters(dataFilters) { + _getSearchFilters(dataFilters, source, style) { const fieldNames = [ - ...this._source.getFieldNames(), - ...this._style.getSourceFieldNames(), + ...source.getFieldNames(), + ...style.getSourceFieldNames(), ...this.getValidJoins().map(join => join.getLeftField().getName()), ]; return { ...dataFilters, fieldNames: _.uniq(fieldNames).sort(), - geogridPrecision: this._source.getGeoGridPrecision(dataFilters.zoom), + geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom), sourceQuery: this.getQuery(), - applyGlobalQuery: this._source.getApplyGlobalQuery(), - sourceMeta: this._source.getSyncMeta(), + applyGlobalQuery: source.getApplyGlobalQuery(), + sourceMeta: source.getSyncMeta(), }; } @@ -347,20 +353,21 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSource({ - startLoading, - stopLoading, - onLoadError, - registerCancelCallback, - dataFilters, - isRequestStillActive, - }) { + async _syncSource(syncContext, source, style) { + const { + startLoading, + stopLoading, + onLoadError, + registerCancelCallback, + dataFilters, + isRequestStillActive, + } = syncContext; const dataRequestId = SOURCE_DATA_ID_ORIGIN; const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); - const searchFilters = this._getSearchFilters(dataFilters); + const searchFilters = this._getSearchFilters(dataFilters, source, style); const prevDataRequest = this.getSourceDataRequest(); const canSkipFetch = await canSkipSourceUpdate({ - source: this._source, + source, prevDataRequest, nextMeta: searchFilters, }); @@ -373,8 +380,8 @@ export class VectorLayer extends AbstractLayer { try { startLoading(dataRequestId, requestToken, searchFilters); - const layerName = await this.getDisplayName(); - const { data: sourceFeatureCollection, meta } = await this._source.getGeoJsonWithMeta( + const layerName = await this.getDisplayName(source); + const { data: sourceFeatureCollection, meta } = await source.getGeoJsonWithMeta( layerName, searchFilters, registerCancelCallback.bind(null, requestToken), @@ -398,16 +405,17 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSourceStyleMeta(syncContext) { - if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { + async _syncSourceStyleMeta(syncContext, source, style) { + if (this.getCurrentStyle().constructor.type !== LAYER_STYLE_TYPE.VECTOR) { return; } return this._syncStyleMeta({ - source: this._source, + source, + style, sourceQuery: this.getQuery(), dataRequestId: SOURCE_META_ID_ORIGIN, - dynamicStyleProps: this._style.getDynamicPropertiesArray().filter(dynamicStyleProp => { + dynamicStyleProps: style.getDynamicPropertiesArray().filter(dynamicStyleProp => { return ( dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE && dynamicStyleProp.isFieldMetaEnabled() @@ -417,28 +425,32 @@ export class VectorLayer extends AbstractLayer { }); } - async _syncJoinStyleMeta(syncContext, join) { + async _syncJoinStyleMeta(syncContext, join, style) { const joinSource = join.getRightJoinSource(); return this._syncStyleMeta({ source: joinSource, + style, sourceQuery: joinSource.getWhereQuery(), dataRequestId: join.getSourceMetaDataRequestId(), - dynamicStyleProps: this._style.getDynamicPropertiesArray().filter(dynamicStyleProp => { - const matchingField = joinSource.getMetricFieldForName( - dynamicStyleProp.getField().getName() - ); - return ( - dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && - !!matchingField && - dynamicStyleProp.isFieldMetaEnabled() - ); - }), + dynamicStyleProps: this.getCurrentStyle() + .getDynamicPropertiesArray() + .filter(dynamicStyleProp => { + const matchingField = joinSource.getMetricFieldForName( + dynamicStyleProp.getField().getName() + ); + return ( + dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && + !!matchingField && + dynamicStyleProp.isFieldMetaEnabled() + ); + }), ...syncContext, }); } async _syncStyleMeta({ source, + style, sourceQuery, dataRequestId, dynamicStyleProps, @@ -459,10 +471,10 @@ export class VectorLayer extends AbstractLayer { const nextMeta = { dynamicStyleFields: _.uniq(dynamicStyleFields).sort(), sourceQuery, - isTimeAware: this._style.isTimeAware() && (await source.isTimeAware()), + isTimeAware: this.getCurrentStyle().isTimeAware() && (await source.isTimeAware()), timeFilters: dataFilters.timeFilters, }; - const prevDataRequest = this.findDataRequestById(dataRequestId); + const prevDataRequest = this.getDataRequest(dataRequestId); const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }); if (canSkipFetch) { return; @@ -471,10 +483,10 @@ export class VectorLayer extends AbstractLayer { const requestToken = Symbol(`layer-${this.getId()}-${dataRequestId}`); try { startLoading(dataRequestId, requestToken, nextMeta); - const layerName = await this.getDisplayName(); + const layerName = await this.getDisplayName(source); const styleMeta = await source.loadStylePropsMeta( layerName, - this._style, + style, dynamicStyleProps, registerCancelCallback, nextMeta @@ -487,15 +499,15 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSourceFormatters(syncContext) { - if (this._style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { + async _syncSourceFormatters(syncContext, source, style) { + if (style.constructor.type !== LAYER_STYLE_TYPE.VECTOR) { return; } return this._syncFormatters({ - source: this._source, + source, dataRequestId: SOURCE_FORMATTERS_ID_ORIGIN, - fields: this._style + fields: style .getDynamicPropertiesArray() .filter(dynamicStyleProp => { return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE; @@ -507,12 +519,12 @@ export class VectorLayer extends AbstractLayer { }); } - async _syncJoinFormatters(syncContext, join) { + async _syncJoinFormatters(syncContext, join, style) { const joinSource = join.getRightJoinSource(); return this._syncFormatters({ source: joinSource, dataRequestId: join.getSourceFormattersDataRequestId(), - fields: this._style + fields: style .getDynamicPropertiesArray() .filter(dynamicStyleProp => { const matchingField = joinSource.getMetricFieldForName( @@ -538,7 +550,7 @@ export class VectorLayer extends AbstractLayer { const nextMeta = { fieldNames: _.uniq(fieldNames).sort(), }; - const prevDataRequest = this.findDataRequestById(dataRequestId); + const prevDataRequest = this.getDataRequest(dataRequestId); const canSkipUpdate = canSkipFormattersUpdate({ prevDataRequest, nextMeta }); if (canSkipUpdate) { return; @@ -565,13 +577,27 @@ export class VectorLayer extends AbstractLayer { } async syncData(syncContext) { + this._syncData(syncContext, this.getSource(), this.getCurrentStyle()); + } + + // TLDR: Do not call getSource or getCurrentStyle in syncData flow. Use 'source' and 'style' arguments instead. + // + // 1) State is contained in the redux store. Layer instance state is readonly. + // 2) Even though data request descriptor updates trigger new instances for rendering, + // syncing data executes on a single object instance. Syncing data can not use updated redux store state. + // + // Blended layer data syncing branches on the source/style depending on whether clustering is used or not. + // Given 1 above, which source/style to use can not be stored in Layer instance state. + // Given 2 above, which source/style to use can not be pulled from data request state. + // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. + async _syncData(syncContext, source, style) { if (!this.isVisible() || !this.showAtZoomLevel(syncContext.dataFilters.zoom)) { return; } - await this._syncSourceStyleMeta(syncContext); - await this._syncSourceFormatters(syncContext); - const sourceResult = await this._syncSource(syncContext); + await this._syncSourceStyleMeta(syncContext, source, style); + await this._syncSourceFormatters(syncContext, source, style); + const sourceResult = await this._syncSource(syncContext, source, style); if ( !sourceResult.featureCollection || !sourceResult.featureCollection.features.length || @@ -580,7 +606,7 @@ export class VectorLayer extends AbstractLayer { return; } - const joinStates = await this._syncJoins(syncContext); + const joinStates = await this._syncJoins(syncContext, style); await this._performInnerJoins(sourceResult, joinStates, syncContext.updateSourceData); } @@ -596,7 +622,7 @@ export class VectorLayer extends AbstractLayer { if (!featureCollection) { if (featureCollectionOnMap) { - this._style.clearFeatureState(featureCollectionOnMap, mbMap, this.getId()); + this.getCurrentStyle().clearFeatureState(featureCollectionOnMap, mbMap, this.getId()); } mbGeoJSONSource.setData(EMPTY_FEATURE_COLLECTION); return; @@ -605,7 +631,7 @@ export class VectorLayer extends AbstractLayer { // "feature-state" data expressions are not supported with layout properties. // To work around this limitation, // scaled layout properties (like icon-size) must fall back to geojson property values :( - const hasGeoJsonProperties = this._style.setFeatureStateAndStyleProps( + const hasGeoJsonProperties = this.getCurrentStyle().setFeatureStateAndStyleProps( featureCollection, mbMap, this.getId() @@ -626,7 +652,7 @@ export class VectorLayer extends AbstractLayer { // Point layers symbolized as icons only contain a single mapbox layer. let markerLayerId; let textLayerId; - if (this._style.arePointsSymbolizedAsCircles()) { + if (this.getCurrentStyle().arePointsSymbolizedAsCircles()) { markerLayerId = pointLayerId; textLayerId = this._getMbTextLayerId(); if (symbolLayer) { @@ -680,13 +706,13 @@ export class VectorLayer extends AbstractLayer { mbMap.setFilter(textLayerId, filterExpr); } - this._style.setMBPaintPropertiesForPoints({ + this.getCurrentStyle().setMBPaintPropertiesForPoints({ alpha: this.getAlpha(), mbMap, pointLayerId, }); - this._style.setMBPropertiesForLabelText({ + this.getCurrentStyle().setMBPropertiesForLabelText({ alpha: this.getAlpha(), mbMap, textLayerId, @@ -711,13 +737,13 @@ export class VectorLayer extends AbstractLayer { mbMap.setFilter(symbolLayerId, filterExpr); } - this._style.setMBSymbolPropertiesForPoints({ + this.getCurrentStyle().setMBSymbolPropertiesForPoints({ alpha: this.getAlpha(), mbMap, symbolLayerId, }); - this._style.setMBPropertiesForLabelText({ + this.getCurrentStyle().setMBPropertiesForLabelText({ alpha: this.getAlpha(), mbMap, textLayerId: symbolLayerId, @@ -745,7 +771,7 @@ export class VectorLayer extends AbstractLayer { paint: {}, }); } - this._style.setMBPaintProperties({ + this.getCurrentStyle().setMBPaintProperties({ alpha: this.getAlpha(), mbMap, fillLayerId, @@ -830,9 +856,13 @@ export class VectorLayer extends AbstractLayer { for (let i = 0; i < tooltipsFromSource.length; i++) { const tooltipProperty = tooltipsFromSource[i]; const matchingJoins = []; - for (let j = 0; j < this._joins.length; j++) { - if (this._joins[j].getLeftField().getName() === tooltipProperty.getPropertyKey()) { - matchingJoins.push(this._joins[j]); + for (let j = 0; j < this.getJoins().length; j++) { + if ( + this.getJoins() + [j].getLeftField() + .getName() === tooltipProperty.getPropertyKey() + ) { + matchingJoins.push(this.getJoins()[j]); } } if (matchingJoins.length) { @@ -842,18 +872,22 @@ export class VectorLayer extends AbstractLayer { } async getPropertiesForTooltip(properties) { - let allTooltips = await this._source.filterAndFormatPropertiesToHtml(properties); + let allTooltips = await this.getSource().filterAndFormatPropertiesToHtml(properties); this._addJoinsToSourceTooltips(allTooltips); - for (let i = 0; i < this._joins.length; i++) { - const propsFromJoin = await this._joins[i].filterAndFormatPropertiesForTooltip(properties); + for (let i = 0; i < this.getJoins().length; i++) { + const propsFromJoin = await this.getJoins()[i].filterAndFormatPropertiesForTooltip( + properties + ); allTooltips = [...allTooltips, ...propsFromJoin]; } return allTooltips; } canShowTooltip() { - return this.isVisible() && (this._source.canFormatFeatureProperties() || this._joins.length); + return ( + this.isVisible() && (this.getSource().canFormatFeatureProperties() || this.getJoins().length) + ); } getFeatureById(id) { diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js index b09ccdc3af8ba..44987fd3e78f0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js +++ b/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js @@ -48,7 +48,7 @@ export class VectorTileLayer extends TileLayer { return; } - const nextMeta = { tileLayerId: this._source.getTileLayerId() }; + const nextMeta = { tileLayerId: this.getSource().getTileLayerId() }; const canSkipSync = this._canSkipSync({ prevDataRequest: this.getSourceDataRequest(), nextMeta, @@ -60,7 +60,7 @@ export class VectorTileLayer extends TileLayer { const requestToken = Symbol(`layer-source-refresh:${this.getId()} - source`); try { startLoading(SOURCE_DATA_ID_ORIGIN, requestToken, dataFilters); - const styleAndSprites = await this._source.getVectorStyleSheetAndSpriteMeta(isRetina()); + const styleAndSprites = await this.getSource().getVectorStyleSheetAndSpriteMeta(isRetina()); const spriteSheetImageData = await loadSpriteSheetImageData(styleAndSprites.spriteMeta.png); const data = { ...styleAndSprites, @@ -78,7 +78,7 @@ export class VectorTileLayer extends TileLayer { _generateMbSourceIdPrefix() { const DELIMITTER = '___'; - return `${this.getId()}${DELIMITTER}${this._source.getTileLayerId()}${DELIMITTER}`; + return `${this.getId()}${DELIMITTER}${this.getSource().getTileLayerId()}${DELIMITTER}`; } _generateMbSourceId(name) { @@ -141,7 +141,7 @@ export class VectorTileLayer extends TileLayer { } _makeNamespacedImageId(imageId) { - const prefix = this._source.getSpriteNamespacePrefix() + '/'; + const prefix = this.getSource().getSpriteNamespacePrefix() + '/'; return prefix + imageId; } diff --git a/x-pack/legacy/plugins/maps/public/meta.js b/x-pack/legacy/plugins/maps/public/meta.js index c5cfb582976c1..4d81785ff7a0a 100644 --- a/x-pack/legacy/plugins/maps/public/meta.js +++ b/x-pack/legacy/plugins/maps/public/meta.js @@ -9,6 +9,7 @@ import { EMS_FILES_CATALOGUE_PATH, EMS_TILES_CATALOGUE_PATH, EMS_GLYPHS_PATH, + EMS_APP_NAME, } from '../common/constants'; import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; @@ -56,7 +57,8 @@ export function getEMSClient() { emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: chrome.getInjected('kbnPkgVersion'), + appVersion: chrome.getInjected('kbnPkgVersion'), + appName: EMS_APP_NAME, tileApiUrl, fileApiUrl, landingPageUrl: chrome.getInjected('emsLandingPageUrl'), diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js index e5eaf8870aa77..79d890bc21f14 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js @@ -10,6 +10,7 @@ import { TileLayer } from '../layers/tile_layer'; import { VectorTileLayer } from '../layers/vector_tile_layer'; import { VectorLayer } from '../layers/vector_layer'; import { HeatmapLayer } from '../layers/heatmap_layer'; +import { BlendedVectorLayer } from '../layers/blended_vector_layer'; import { ALL_SOURCES } from '../layers/sources/all_sources'; import { timefilter } from 'ui/timefilter'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -40,6 +41,8 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) { return new VectorTileLayer({ layerDescriptor, source }); case HeatmapLayer.type: return new HeatmapLayer({ layerDescriptor, source }); + case BlendedVectorLayer.type: + return new BlendedVectorLayer({ layerDescriptor, source }); default: throw new Error(`Unrecognized layerType ${layerDescriptor.type}`); } diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js index 5ec40a57ebc7f..ef2e23e51a092 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js @@ -5,6 +5,7 @@ */ jest.mock('../layers/vector_layer', () => {}); +jest.mock('../layers/blended_vector_layer', () => {}); jest.mock('../layers/heatmap_layer', () => {}); jest.mock('../layers/vector_tile_layer', () => {}); jest.mock('../layers/sources/all_sources', () => {}); diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js index 757750dbb0813..7ca659148449f 100644 --- a/x-pack/legacy/plugins/maps/server/routes.js +++ b/x-pack/legacy/plugins/maps/server/routes.js @@ -5,6 +5,7 @@ */ import { + EMS_APP_NAME, EMS_CATALOGUE_PATH, EMS_FILES_API_PATH, EMS_FILES_CATALOGUE_PATH, @@ -38,7 +39,8 @@ export function initRoutes(server, licenseUid) { if (mapConfig.includeElasticMapsService) { emsClient = new EMSClient({ language: i18n.getLocale(), - kbnVersion: serverConfig.get('pkg.version'), + appVersion: serverConfig.get('pkg.version'), + appName: EMS_APP_NAME, fileApiUrl: mapConfig.emsFileApiUrl, tileApiUrl: mapConfig.emsTileApiUrl, landingPageUrl: mapConfig.emsLandingPageUrl, diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts index a6db36d8f64e7..ed47cdc62a1b6 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts @@ -16,7 +16,7 @@ import { KibanaServices } from '../../../lib/kibana'; import { CASES_CONFIGURE_URL } from '../constants'; import { ApiProps } from '../types'; import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils'; -import { CaseConfigure, PatchConnectorProps } from './types'; +import { CaseConfigure } from './types'; export const fetchConnectors = async ({ signal }: ApiProps): Promise => { const response = await KibanaServices.get().http.fetch( @@ -79,20 +79,3 @@ export const patchCaseConfigure = async ( decodeCaseConfigureResponse(response) ); }; - -export const patchConfigConnector = async ({ - connectorId, - config, - signal, -}: PatchConnectorProps): Promise => { - const response = await KibanaServices.get().http.fetch( - `${CASES_CONFIGURE_URL}/connectors/${connectorId}`, - { - method: 'PATCH', - body: JSON.stringify(config), - signal, - } - ); - - return response; -}; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/types.ts b/x-pack/legacy/plugins/siem/public/containers/case/configure/types.ts index 840828307163c..fc7aaa3643d77 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/types.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ElasticUser, ApiProps } from '../types'; +import { ElasticUser } from '../types'; import { ActionType, - CasesConnectorConfiguration, CasesConfigurationMaps, CaseField, ClosureType, @@ -33,11 +32,6 @@ export interface CaseConfigure { version: string; } -export interface PatchConnectorProps extends ApiProps { - connectorId: string; - config: CasesConnectorConfiguration; -} - export interface CCMapsCombinedActionAttributes extends CasesConfigurationMaps { actionType?: ActionType; } diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx index f905ebe756d7d..d31dcdbee2a14 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx @@ -8,14 +8,13 @@ import { useState, useEffect, useCallback } from 'react'; import { useStateToaster, errorToToaster } from '../../../components/toasters'; import * as i18n from '../translations'; -import { fetchConnectors, patchConfigConnector } from './api'; -import { CasesConfigurationMapping, Connector } from './types'; +import { fetchConnectors } from './api'; +import { Connector } from './types'; export interface ReturnConnectors { loading: boolean; connectors: Connector[]; refetchConnectors: () => void; - updateConnector: (connectorId: string, mappings: CasesConfigurationMapping[]) => unknown; } export const useConnectors = (): ReturnConnectors => { @@ -53,55 +52,6 @@ export const useConnectors = (): ReturnConnectors => { }; }, []); - const updateConnector = useCallback( - (connectorId: string, mappings: CasesConfigurationMapping[]) => { - if (connectorId === 'none') { - return; - } - - let didCancel = false; - const abortCtrl = new AbortController(); - const update = async () => { - try { - setLoading(true); - await patchConfigConnector({ - connectorId, - config: { - cases_configuration: { - mapping: mappings.map(m => ({ - source: m.source, - target: m.target, - action_type: m.actionType, - })), - }, - }, - signal: abortCtrl.signal, - }); - if (!didCancel) { - setLoading(false); - refetchConnectors(); - } - } catch (error) { - if (!didCancel) { - setLoading(false); - refetchConnectors(); - errorToToaster({ - title: i18n.ERROR_TITLE, - error: error.body && error.body.message ? new Error(error.body.message) : error, - dispatchToaster, - }); - } - } - }; - update(); - return () => { - didCancel = true; - abortCtrl.abort(); - }; - }, - [] - ); - useEffect(() => { refetchConnectors(); }, []); @@ -110,6 +60,5 @@ export const useConnectors = (): ReturnConnectors => { loading, connectors, refetchConnectors, - updateConnector, }; }; diff --git a/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx b/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx index 877757df30fb3..8e947fbc0f9bb 100644 --- a/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx +++ b/x-pack/legacy/plugins/siem/public/lib/connectors/servicenow.tsx @@ -87,6 +87,10 @@ export function getActionType(): ActionTypeModel { const ServiceNowConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { + /* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally. + * If we do, errors will be shown the first time the flyout is open even though the user did not + * interact with the form. Also, we would like to show errors for empty fields provided by the user. + /*/ const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config; const { username, password } = action.secrets; @@ -153,7 +157,7 @@ const ServiceNowConnectorFields: React.FunctionComponent { sort: { field: queryParams.sortField, direction: queryParams.sortOrder }, }; const euiBasicTableSelectionProps = useMemo>( - () => ({ - selectable: (item: Case) => true, - onSelectionChange: setSelectedCases, - }), + () => ({ onSelectionChange: setSelectedCases }), [selectedCases] ); const isCasesLoading = useMemo( @@ -305,6 +302,7 @@ export const AllCases = React.memo(() => { {i18n.SHOWING_SELECTED_CASES(selectedCases.length)} { { closePopover(); deleteCasesAction(selectedCaseIds); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx index 8754c0404d40b..15d6cf7cf7317 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.test.tsx @@ -7,16 +7,30 @@ import React from 'react'; import { mount } from 'enzyme'; import { CaseComponent } from './'; -import * as apiHook from '../../../../containers/case/use_update_case'; +import * as updateHook from '../../../../containers/case/use_update_case'; +import * as deleteHook from '../../../../containers/case/use_delete_cases'; import { caseProps, data } from './__mock__'; import { TestProviders } from '../../../../mock'; describe('CaseView ', () => { + const handleOnDeleteConfirm = jest.fn(); + const handleToggleModal = jest.fn(); + const dispatchResetIsDeleted = jest.fn(); const updateCaseProperty = jest.fn(); + /* eslint-disable no-console */ + // Silence until enzyme fixed to use ReactTestUtils.act() + const originalError = console.error; + beforeAll(() => { + console.error = jest.fn(); + }); + afterAll(() => { + console.error = originalError; + }); + /* eslint-enable no-console */ beforeEach(() => { jest.resetAllMocks(); - jest.spyOn(apiHook, 'useUpdateCase').mockReturnValue({ + jest.spyOn(updateHook, 'useUpdateCase').mockReturnValue({ caseData: data, isLoading: false, isError: false, @@ -119,4 +133,46 @@ describe('CaseView ', () => { .prop('source') ).toEqual(data.comments[0].comment); }); + + it('toggle delete modal and cancel', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeFalsy(); + + wrapper + .find( + '[data-test-subj="case-view-actions"] button[data-test-subj="property-actions-ellipses"]' + ) + .first() + .simulate('click'); + wrapper.find('button[data-test-subj="property-actions-trash"]').simulate('click'); + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy(); + wrapper.find('button[data-test-subj="confirmModalCancelButton"]').simulate('click'); + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeFalsy(); + }); + + it('toggle delete modal and confirm', () => { + jest.spyOn(deleteHook, 'useDeleteCases').mockReturnValue({ + dispatchResetIsDeleted, + handleToggleModal, + handleOnDeleteConfirm, + isLoading: false, + isError: false, + isDeleted: false, + isDisplayConfirmDeleteModal: true, + }); + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="confirm-delete-case-modal"]').exists()).toBeTruthy(); + wrapper.find('button[data-test-subj="confirmModalConfirmButton"]').simulate('click'); + expect(handleOnDeleteConfirm.mock.calls[0][0]).toEqual([caseProps.caseId]); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx index 5ff542d208905..82216e88a091e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/case_view/index.tsx @@ -216,7 +216,7 @@ export const CaseComponent = React.memo(({ caseId, initialData }) => onChange={toggleStatusCase} /> - +
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx index 55b256b66b72b..bb0c50b3b193a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useCallback } from 'react'; +import React from 'react'; import { EuiDescribedFormGroup, EuiFormRow, @@ -18,12 +18,7 @@ import styled from 'styled-components'; import { ConnectorsDropdown } from './connectors_dropdown'; import * as i18n from './translations'; -import { - ActionsConnectorsContextProvider, - ConnectorAddFlyout, -} from '../../../../../../../../plugins/triggers_actions_ui/public'; import { Connector } from '../../../../containers/case/configure/types'; -import { useKibana } from '../../../../lib/kibana'; const EuiFormRowExtended = styled(EuiFormRow)` .euiFormRow__labelWrapper { @@ -38,41 +33,26 @@ interface Props { disabled: boolean; isLoading: boolean; onChangeConnector: (id: string) => void; - refetchConnectors: () => void; selectedConnector: string; + handleShowAddFlyout: () => void; } -const actionTypes = [ - { - id: '.servicenow', - name: 'ServiceNow', - enabled: true, - }, -]; - const ConnectorsComponent: React.FC = ({ connectors, disabled, isLoading, onChangeConnector, - refetchConnectors, selectedConnector, + handleShowAddFlyout, }) => { - const { http, triggers_actions_ui, notifications, application } = useKibana().services; - const [addFlyoutVisible, setAddFlyoutVisibility] = useState(false); - - const handleShowFlyout = useCallback(() => setAddFlyoutVisibility(true), []); - const dropDownLabel = ( {i18n.INCIDENT_MANAGEMENT_SYSTEM_LABEL} - {i18n.ADD_NEW_CONNECTOR} + {i18n.ADD_NEW_CONNECTOR} ); - const reloadConnectors = useCallback(async () => refetchConnectors(), []); - return ( <> = ({ /> - - - ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx index da715fb66953f..b3c424bef6a7a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx @@ -9,8 +9,18 @@ import styled, { css } from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSpacer, EuiCallOut } from '@elastic/eui'; import { noop, isEmpty } from 'lodash/fp'; +import { useKibana } from '../../../../lib/kibana'; import { useConnectors } from '../../../../containers/case/configure/use_connectors'; import { useCaseConfigure } from '../../../../containers/case/configure/use_configure'; +import { + ActionsConnectorsContextProvider, + ConnectorAddFlyout, + ConnectorEditFlyout, +} from '../../../../../../../../plugins/triggers_actions_ui/public'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ActionConnectorTableItem } from '../../../../../../../../plugins/triggers_actions_ui/public/types'; + import { ClosureType, CasesConfigurationMapping, @@ -40,8 +50,25 @@ const initialState: State = { mapping: null, }; +const actionTypes = [ + { + id: '.servicenow', + name: 'ServiceNow', + enabled: true, + }, +]; + const ConfigureCasesComponent: React.FC = () => { + const { http, triggers_actions_ui, notifications, application } = useKibana().services; + const [connectorIsValid, setConnectorIsValid] = useState(true); + const [addFlyoutVisible, setAddFlyoutVisibility] = useState(false); + const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); + const [editedConnectorItem, setEditedConnectorItem] = useState( + null + ); + + const handleShowAddFlyout = useCallback(() => setAddFlyoutVisibility(true), []); const [{ connectorId, closureType, mapping }, dispatch] = useReducer( configureCasesReducer(), @@ -73,20 +100,18 @@ const ConfigureCasesComponent: React.FC = () => { setConnectorId, setClosureType, }); - const { - loading: isLoadingConnectors, - connectors, - refetchConnectors, - updateConnector, - } = useConnectors(); + const { loading: isLoadingConnectors, connectors, refetchConnectors } = useConnectors(); + // ActionsConnectorsContextProvider reloadConnectors prop expects a Promise. + // TODO: Fix it if reloadConnectors type change. + const reloadConnectors = useCallback(async () => refetchConnectors(), []); const isLoadingAny = isLoadingConnectors || persistLoading || loadingCaseConfigure; + const updateConnectorDisabled = isLoadingAny || !connectorIsValid || connectorId === 'none'; const handleSubmit = useCallback( // TO DO give a warning/error to user when field are not mapped so they have chance to do it () => { persistCaseConfigure({ connectorId, closureType }); - updateConnector(connectorId, mapping ?? []); }, [connectorId, closureType, mapping] ); @@ -124,6 +149,14 @@ const ConfigureCasesComponent: React.FC = () => { } }, [connectors, connectorId]); + useEffect(() => { + if (!isLoadingConnectors && connectorId !== 'none') { + setEditedConnectorItem( + connectors.find(c => c.id === connectorId) as ActionConnectorTableItem + ); + } + }, [connectors, connectorId]); + return ( {!connectorIsValid && ( @@ -139,7 +172,7 @@ const ConfigureCasesComponent: React.FC = () => { disabled={persistLoading || isLoadingConnectors} isLoading={isLoadingConnectors} onChangeConnector={setConnectorId} - refetchConnectors={refetchConnectors} + handleShowAddFlyout={handleShowAddFlyout} selectedConnector={connectorId} /> @@ -152,15 +185,11 @@ const ConfigureCasesComponent: React.FC = () => { @@ -194,6 +223,29 @@ const ConfigureCasesComponent: React.FC = () => { + + + {editedConnectorItem && ( + + )} + ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx index 10c8f6b938023..2600a9f4e13ac 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx @@ -4,8 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiDescribedFormGroup } from '@elastic/eui'; +import React, { useCallback } from 'react'; +import styled from 'styled-components'; + +import { + EuiDescribedFormGroup, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiButtonEmpty, +} from '@elastic/eui'; import * as i18n from './translations'; @@ -14,18 +22,44 @@ import { CasesConfigurationMapping } from '../../../../containers/case/configure interface MappingProps { disabled: boolean; + updateConnectorDisabled: boolean; mapping: CasesConfigurationMapping[] | null; onChangeMapping: (newMapping: CasesConfigurationMapping[]) => void; + setEditFlyoutVisibility: React.Dispatch>; } -const MappingComponent: React.FC = ({ disabled, mapping, onChangeMapping }) => ( - {i18n.FIELD_MAPPING_TITLE}} - description={i18n.FIELD_MAPPING_DESC} - > - - -); +const EuiButtonEmptyExtended = styled(EuiButtonEmpty)` + font-size: 12px; + height: 24px; +`; + +const MappingComponent: React.FC = ({ + disabled, + updateConnectorDisabled, + mapping, + onChangeMapping, + setEditFlyoutVisibility, +}) => { + const onClick = useCallback(() => setEditFlyoutVisibility(true), []); + + return ( + {i18n.FIELD_MAPPING_TITLE}} + description={i18n.FIELD_MAPPING_DESC} + > + + + + + {i18n.UPDATE_CONNECTOR} + + + + + + + ); +}; export const Mapping = React.memo(MappingComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts index d24921a636082..dd9bf82fb0b0d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/translations.ts @@ -186,3 +186,7 @@ export const FIELD_MAPPING_FIELD_COMMENTS = i18n.translate( defaultMessage: 'Comments', } ); + +export const UPDATE_CONNECTOR = i18n.translate('xpack.siem.case.configureCases.updateConnector', { + defaultMessage: 'Update connector', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/confirm_delete_case/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/confirm_delete_case/index.tsx index dff36a6dac571..5755258b36388 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/confirm_delete_case/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/confirm_delete_case/index.tsx @@ -32,6 +32,7 @@ const ConfirmDeleteCaseModalComp: React.FC = ({ buttonColor="danger" cancelButtonText={i18n.CANCEL} confirmButtonText={isPlural ? i18n.DELETE_CASES : i18n.DELETE_CASE} + data-test-subj="confirm-delete-case-modal" defaultFocusedButton="confirm" onCancel={onCancel} onConfirm={onConfirm} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx index 3b9af8349437e..20712c3c5a815 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx @@ -72,6 +72,10 @@ export const Create = React.memo(() => { } }, [form]); + const handleSetIsCancel = useCallback(() => { + setIsCancel(true); + }, [isCancel]); + if (caseData != null && caseData.id) { return ; } @@ -137,7 +141,12 @@ export const Create = React.memo(() => { responsive={false} > - setIsCancel(true)} iconType="cross"> + {i18n.CANCEL} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx index 7fe5b6f5f8794..01ccf3c510b60 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/property_actions/index.tsx @@ -13,9 +13,12 @@ export interface PropertyActionButtonProps { label: string; } +const ComponentId = 'property-actions'; + const PropertyActionButton = React.memo( ({ onClick, iconType, label }) => ( (({ propertyActio }, []); return ( - + (({ propertyActio isOpen={showActions} closePopover={onClosePopover} > - + {propertyActions.map((action, key) => ( { + let mockEcsDataClone = cloneDeep(mockEcsData); + beforeEach(() => { + mockEcsDataClone = cloneDeep(mockEcsData); + }); + describe('getStringOrStringArray', () => { + test('it should correctly return a string array', () => { + const value = getStringArray('x', { + x: 'The nickname of the developer we all :heart:', + }); + expect(value).toEqual(['The nickname of the developer we all :heart:']); + }); + + test('it should correctly return a string array with a single element', () => { + const value = getStringArray('x', { + x: ['The nickname of the developer we all :heart:'], + }); + expect(value).toEqual(['The nickname of the developer we all :heart:']); + }); + + test('it should correctly return a string array with two elements of strings', () => { + const value = getStringArray('x', { + x: ['The nickname of the developer we all :heart:', 'We are all made of stars'], + }); + expect(value).toEqual([ + 'The nickname of the developer we all :heart:', + 'We are all made of stars', + ]); + }); + + test('it should correctly return a string array with deep elements', () => { + const value = getStringArray('x.y.z', { + x: { y: { z: 'zed' } }, + }); + expect(value).toEqual(['zed']); + }); + + test('it should correctly return a string array with a non-existent value', () => { + const value = getStringArray('non.existent', { + x: { y: { z: 'zed' } }, + }); + expect(value).toEqual([]); + }); + + test('it should trace an error if the value is not a string', () => { + const mockConsole: Console = ({ trace: jest.fn() } as unknown) as Console; + const value = getStringArray('a', { a: 5 }, mockConsole); + expect(value).toEqual([]); + expect( + mockConsole.trace + ).toHaveBeenCalledWith( + 'Data type that is not a string or string array detected:', + 5, + 'when trying to access field:', + 'a', + 'from data object of:', + { a: 5 } + ); + }); + + test('it should trace an error if the value is an array of mixed values', () => { + const mockConsole: Console = ({ trace: jest.fn() } as unknown) as Console; + const value = getStringArray('a', { a: ['hi', 5] }, mockConsole); + expect(value).toEqual([]); + expect( + mockConsole.trace + ).toHaveBeenCalledWith( + 'Data type that is not a string or string array detected:', + ['hi', 5], + 'when trying to access field:', + 'a', + 'from data object of:', + { a: ['hi', 5] } + ); + }); + }); + + describe('replaceTemplateFieldFromQuery', () => { + test('given an empty query string this returns an empty query string', () => { + const replacement = replaceTemplateFieldFromQuery('', mockEcsDataClone[0]); + expect(replacement).toEqual(''); + }); + + test('given a query string with spaces this returns an empty query string', () => { + const replacement = replaceTemplateFieldFromQuery(' ', mockEcsDataClone[0]); + expect(replacement).toEqual(''); + }); + + test('it should replace a query with a template value such as apache from a mock template', () => { + const replacement = replaceTemplateFieldFromQuery( + 'host.name: placeholdertext', + mockEcsDataClone[0] + ); + expect(replacement).toEqual('host.name: apache'); + }); + + test('it should replace a template field with an ECS value that is not an array', () => { + mockEcsDataClone[0].host!.name = ('apache' as unknown) as string[]; // very unsafe cast for this test case + const replacement = replaceTemplateFieldFromQuery('host.name: *', mockEcsDataClone[0]); + expect(replacement).toEqual('host.name: *'); + }); + + test('it should NOT replace a query with a template value that is not part of the template fields array', () => { + const replacement = replaceTemplateFieldFromQuery( + 'user.id: placeholdertext', + mockEcsDataClone[0] + ); + expect(replacement).toEqual('user.id: placeholdertext'); + }); + }); + + describe('replaceTemplateFieldFromMatchFilters', () => { + test('given an empty query filter this will return an empty filter', () => { + const replacement = replaceTemplateFieldFromMatchFilters([], mockEcsDataClone[0]); + expect(replacement).toEqual([]); + }); + + test('given a query filter this will return that filter with the placeholder replaced', () => { + const filters: Filter[] = [ + { + meta: { + type: 'phrase', + key: 'host.name', + alias: 'alias', + disabled: false, + negate: false, + params: { query: 'Braden' }, + }, + query: { match_phrase: { 'host.name': 'Braden' } }, + }, + ]; + const replacement = replaceTemplateFieldFromMatchFilters(filters, mockEcsDataClone[0]); + const expected: Filter[] = [ + { + meta: { + type: 'phrase', + key: 'host.name', + alias: 'alias', + disabled: false, + negate: false, + params: { query: 'apache' }, + }, + query: { match_phrase: { 'host.name': 'apache' } }, + }, + ]; + expect(replacement).toEqual(expected); + }); + + test('given a query filter with a value not in the templateFields, this will NOT replace the placeholder value', () => { + const filters: Filter[] = [ + { + meta: { + type: 'phrase', + key: 'user.id', + alias: 'alias', + disabled: false, + negate: false, + params: { query: 'Evan' }, + }, + query: { match_phrase: { 'user.id': 'Evan' } }, + }, + ]; + const replacement = replaceTemplateFieldFromMatchFilters(filters, mockEcsDataClone[0]); + const expected: Filter[] = [ + { + meta: { + type: 'phrase', + key: 'user.id', + alias: 'alias', + disabled: false, + negate: false, + params: { query: 'Evan' }, + }, + query: { match_phrase: { 'user.id': 'Evan' } }, + }, + ]; + expect(replacement).toEqual(expected); + }); + }); + + describe('reformatDataProviderWithNewValue', () => { + test('it should replace a query with a template value such as apache from a mock data provider', () => { + const mockDataProvider: DataProvider = mockDataProviders[0]; + mockDataProvider.queryMatch.field = 'host.name'; + mockDataProvider.id = 'Braden'; + mockDataProvider.name = 'Braden'; + mockDataProvider.queryMatch.value = 'Braden'; + const replacement = reformatDataProviderWithNewValue(mockDataProvider, mockEcsDataClone[0]); + expect(replacement).toEqual({ + id: 'apache', + name: 'apache', + enabled: true, + excluded: false, + kqlQuery: '', + queryMatch: { + field: 'host.name', + value: 'apache', + operator: ':', + displayField: undefined, + displayValue: undefined, + }, + and: [], + }); + }); + + test('it should replace a query with a template value such as apache from a mock data provider using a string in the data provider', () => { + mockEcsDataClone[0].host!.name = ('apache' as unknown) as string[]; // very unsafe cast for this test case + const mockDataProvider: DataProvider = mockDataProviders[0]; + mockDataProvider.queryMatch.field = 'host.name'; + mockDataProvider.id = 'Braden'; + mockDataProvider.name = 'Braden'; + mockDataProvider.queryMatch.value = 'Braden'; + const replacement = reformatDataProviderWithNewValue(mockDataProvider, mockEcsDataClone[0]); + expect(replacement).toEqual({ + id: 'apache', + name: 'apache', + enabled: true, + excluded: false, + kqlQuery: '', + queryMatch: { + field: 'host.name', + value: 'apache', + operator: ':', + displayField: undefined, + displayValue: undefined, + }, + and: [], + }); + }); + + test('it should NOT replace a query with a template value that is not part of a template such as user.id', () => { + const mockDataProvider: DataProvider = mockDataProviders[0]; + mockDataProvider.queryMatch.field = 'user.id'; + mockDataProvider.id = 'my-id'; + mockDataProvider.name = 'Rebecca'; + mockDataProvider.queryMatch.value = 'Rebecca'; + const replacement = reformatDataProviderWithNewValue(mockDataProvider, mockEcsDataClone[0]); + expect(replacement).toEqual({ + id: 'my-id', + name: 'Rebecca', + enabled: true, + excluded: false, + kqlQuery: '', + queryMatch: { + field: 'user.id', + value: 'Rebecca', + operator: ':', + displayField: undefined, + displayValue: undefined, + }, + and: [], + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/helpers.ts index 715d98ed33694..e8c9c2e3cf6c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/helpers.ts @@ -17,6 +17,11 @@ interface FindValueToChangeInQuery { valueToChange: string; } +/** + * Fields that will be replaced with the template strings from a a saved timeline template. + * This is used for the signals detection engine feature when you save a timeline template + * and are the fields you can replace when creating a template. + */ const templateFields = [ 'host.name', 'host.hostname', @@ -32,6 +37,36 @@ const templateFields = [ 'process.name', ]; +/** + * This will return an unknown as a string array if it exists from an unknown data type and a string + * that represents the path within the data object the same as lodash's "get". If the value is non-existent + * we will return an empty array. If it is a non string value then this will log a trace to the console + * that it encountered an error and return an empty array. + * @param field string of the field to access + * @param data The unknown data that is typically a ECS value to get the value + * @param localConsole The local console which can be sent in to make this pure (for tests) or use the default console + */ +export const getStringArray = (field: string, data: unknown, localConsole = console): string[] => { + const value: unknown | undefined = get(field, data); + if (value == null) { + return []; + } else if (typeof value === 'string') { + return [value]; + } else if (Array.isArray(value) && value.every(element => typeof element === 'string')) { + return value; + } else { + localConsole.trace( + 'Data type that is not a string or string array detected:', + value, + 'when trying to access field:', + field, + 'from data object of:', + data + ); + return []; + } +}; + export const findValueToChangeInQuery = ( keuryNode: KueryNode, valueToChange: FindValueToChangeInQuery[] = [] @@ -66,31 +101,33 @@ export const findValueToChangeInQuery = ( ); }; -export const replaceTemplateFieldFromQuery = (query: string, ecsData: Ecs) => { +export const replaceTemplateFieldFromQuery = (query: string, ecsData: Ecs): string => { if (query.trim() !== '') { const valueToChange = findValueToChangeInQuery(esKuery.fromKueryExpression(query)); return valueToChange.reduce((newQuery, vtc) => { - const newValue = get(vtc.field, ecsData); - if (newValue != null) { - return newQuery.replace(vtc.valueToChange, newValue); + const newValue = getStringArray(vtc.field, ecsData); + if (newValue.length) { + return newQuery.replace(vtc.valueToChange, newValue[0]); + } else { + return newQuery; } - return newQuery; }, query); + } else { + return ''; } - return ''; }; -export const replaceTemplateFieldFromMatchFilters = (filters: Filter[], ecsData: Ecs) => +export const replaceTemplateFieldFromMatchFilters = (filters: Filter[], ecsData: Ecs): Filter[] => filters.map(filter => { if ( filter.meta.type === 'phrase' && filter.meta.key != null && templateFields.includes(filter.meta.key) ) { - const newValue = get(filter.meta.key, ecsData); - if (newValue != null) { - filter.meta.params = { query: newValue }; - filter.query = { match_phrase: { [filter.meta.key]: newValue } }; + const newValue = getStringArray(filter.meta.key, ecsData); + if (newValue.length) { + filter.meta.params = { query: newValue[0] }; + filter.query = { match_phrase: { [filter.meta.key]: newValue[0] } }; } } return filter; @@ -101,11 +138,11 @@ export const reformatDataProviderWithNewValue = { if (templateFields.includes(dataProvider.queryMatch.field)) { - const newValue = get(dataProvider.queryMatch.field, ecsData); - if (newValue != null) { - dataProvider.id = dataProvider.id.replace(dataProvider.name, newValue); - dataProvider.name = newValue; - dataProvider.queryMatch.value = newValue; + const newValue = getStringArray(dataProvider.queryMatch.field, ecsData); + if (newValue.length) { + dataProvider.id = dataProvider.id.replace(dataProvider.name, newValue[0]); + dataProvider.name = newValue[0]; + dataProvider.queryMatch.value = newValue[0]; dataProvider.queryMatch.displayField = undefined; dataProvider.queryMatch.displayValue = undefined; } @@ -116,8 +153,8 @@ export const reformatDataProviderWithNewValue = - dataProviders.map((dataProvider: DataProvider) => { +): DataProvider[] => + dataProviders.map(dataProvider => { const newDataProvider = reformatDataProviderWithNewValue(dataProvider, ecsData); if (newDataProvider.and != null && !isEmpty(newDataProvider.and)) { newDataProvider.and = newDataProvider.and.map(andDataProvider => diff --git a/x-pack/legacy/plugins/triggers_actions_ui/index.ts b/x-pack/legacy/plugins/triggers_actions_ui/index.ts index e871573b266a7..eb74290c84682 100644 --- a/x-pack/legacy/plugins/triggers_actions_ui/index.ts +++ b/x-pack/legacy/plugins/triggers_actions_ui/index.ts @@ -24,18 +24,11 @@ export function triggersActionsUI(kibana: any) { return Joi.object() .keys({ enabled: Joi.boolean().default(true), - createAlertUiEnabled: Joi.boolean().default(false), }) .default(); }, uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), - injectDefaultVars(server: Legacy.Server) { - const serverConfig = server.config(); - return { - createAlertUiEnabled: serverConfig.get('xpack.triggers_actions_ui.createAlertUiEnabled'), - }; - }, }, }); } diff --git a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts index f09c795977831..61197d6dc373d 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts @@ -4,6 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -export enum REST_API_URLS { +export enum API_URLS { + INDEX_PATTERN = `/api/uptime/index_pattern`, INDEX_STATUS = '/api/uptime/index_status', + MONITOR_LOCATIONS = `/api/uptime/monitor/locations`, + MONITOR_DURATION = `/api/uptime/monitor/duration`, + MONITOR_DETAILS = `/api/uptime/monitor/details`, + MONITOR_SELECTED = `/api/uptime/monitor/selected`, + MONITOR_STATUS = `/api/uptime/monitor/status`, + PINGS = '/api/uptime/pings', + PING_HISTOGRAM = `/api/uptime/ping/histogram`, + SNAPSHOT_COUNT = `/api/uptime/snapshot/count`, + FILTERS = `/api/uptime/filters`, } diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx index 08421cb56d14c..ac8ff13d1edce 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx @@ -8,9 +8,10 @@ import React, { useEffect } from 'react'; import { connect } from 'react-redux'; import { useUrlParams } from '../../../hooks'; import { AppState } from '../../../state'; -import { fetchSnapshotCount } from '../../../state/actions'; +import { getSnapshotCountAction } from '../../../state/actions'; import { SnapshotComponent } from '../../functional/snapshot'; import { Snapshot as SnapshotType } from '../../../../common/runtime_types'; +import { SnapShotQueryParams } from '../../../state/api'; /** * Props expected from parent components. @@ -37,7 +38,7 @@ interface StoreProps { * for this component's life cycle */ interface DispatchProps { - loadSnapshotCount: typeof fetchSnapshotCount; + loadSnapshotCount: typeof getSnapshotCountAction; } /** @@ -57,7 +58,7 @@ export const Container: React.FC = ({ const { dateRangeStart, dateRangeEnd, statusFilter } = getUrlParams(); useEffect(() => { - loadSnapshotCount(dateRangeStart, dateRangeEnd, esKuery, statusFilter); + loadSnapshotCount({ dateRangeStart, dateRangeEnd, filters: esKuery, statusFilter }); }, [dateRangeStart, dateRangeEnd, esKuery, lastRefresh, loadSnapshotCount, statusFilter]); return ; }; @@ -81,13 +82,8 @@ const mapStateToProps = ({ * @param dispatch redux-provided action dispatcher */ const mapDispatchToProps = (dispatch: any) => ({ - loadSnapshotCount: ( - dateRangeStart: string, - dateRangeEnd: string, - filters?: string, - statusFilter?: string - ): DispatchProps => { - return dispatch(fetchSnapshotCount(dateRangeStart, dateRangeEnd, filters, statusFilter)); + loadSnapshotCount: (params: SnapShotQueryParams): DispatchProps => { + return dispatch(getSnapshotCountAction(params)); }, }); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx index 8c670b485cc56..ceeaa7026059f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx @@ -7,9 +7,9 @@ import React, { useEffect } from 'react'; import { connect } from 'react-redux'; import { AppState } from '../../../state'; -import { getMonitorDetails } from '../../../state/selectors'; +import { monitorDetailsSelector } from '../../../state/selectors'; import { MonitorDetailsActionPayload } from '../../../state/actions/types'; -import { fetchMonitorDetails } from '../../../state/actions/monitor'; +import { getMonitorDetailsAction } from '../../../state/actions/monitor'; import { MonitorListDrawerComponent } from '../../functional/monitor_list/monitor_list_drawer/monitor_list_drawer'; import { useUrlParams } from '../../../hooks'; import { MonitorSummary } from '../../../../common/graphql/types'; @@ -18,7 +18,7 @@ import { MonitorDetails } from '../../../../common/runtime_types/monitor'; interface ContainerProps { summary: MonitorSummary; monitorDetails: MonitorDetails; - loadMonitorDetails: typeof fetchMonitorDetails; + loadMonitorDetails: typeof getMonitorDetailsAction; } const Container: React.FC = ({ summary, loadMonitorDetails, monitorDetails }) => { @@ -38,12 +38,12 @@ const Container: React.FC = ({ summary, loadMonitorDetails, moni }; const mapStateToProps = (state: AppState, { summary }: any) => ({ - monitorDetails: getMonitorDetails(state, summary), + monitorDetails: monitorDetailsSelector(state, summary), }); const mapDispatchToProps = (dispatch: any) => ({ loadMonitorDetails: (actionPayload: MonitorDetailsActionPayload) => - dispatch(fetchMonitorDetails(actionPayload)), + dispatch(getMonitorDetailsAction(actionPayload)), }); export const MonitorListDrawer = connect(mapStateToProps, mapDispatchToProps)(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx index b2b555d32a3c7..456fa2b30bca8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx @@ -8,9 +8,9 @@ import React, { useContext, useEffect } from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { AppState } from '../../../state'; -import { selectMonitorLocations, selectMonitorStatus } from '../../../state/selectors'; +import { monitorLocationsSelector, selectMonitorStatus } from '../../../state/selectors'; import { MonitorStatusBarComponent } from '../../functional/monitor_status_details/monitor_status_bar'; -import { getMonitorStatus, getSelectedMonitor } from '../../../state/actions'; +import { getMonitorStatusAction, getSelectedMonitorAction } from '../../../state/actions'; import { useUrlParams } from '../../../hooks'; import { Ping } from '../../../../common/graphql/types'; import { MonitorLocations } from '../../../../common/runtime_types/monitor'; @@ -57,20 +57,20 @@ const Container: React.FC = ({ const mapStateToProps = (state: AppState, ownProps: OwnProps) => ({ monitorStatus: selectMonitorStatus(state), - monitorLocations: selectMonitorLocations(state, ownProps.monitorId), + monitorLocations: monitorLocationsSelector(state, ownProps.monitorId), }); const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ loadMonitorStatus: (dateStart: string, dateEnd: string, monitorId: string) => { dispatch( - getMonitorStatus({ + getMonitorStatusAction({ monitorId, dateStart, dateEnd, }) ); dispatch( - getSelectedMonitor({ + getSelectedMonitorAction({ monitorId, }) ); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx index 6929e3bd64c4d..3ced251dfab8c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx @@ -9,8 +9,8 @@ import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { useUrlParams } from '../../../hooks'; import { AppState } from '../../../state'; -import { selectMonitorLocations } from '../../../state/selectors'; -import { fetchMonitorLocations, MonitorLocationsPayload } from '../../../state/actions/monitor'; +import { monitorLocationsSelector } from '../../../state/selectors'; +import { getMonitorLocationsAction, MonitorLocationsPayload } from '../../../state/actions/monitor'; import { MonitorStatusDetailsComponent } from '../../functional/monitor_status_details'; import { MonitorLocations } from '../../../../common/runtime_types'; import { UptimeRefreshContext } from '../../../contexts'; @@ -24,7 +24,7 @@ interface StoreProps { } interface DispatchProps { - loadMonitorLocations: typeof fetchMonitorLocations; + loadMonitorLocations: typeof getMonitorLocationsAction; } type Props = OwnProps & StoreProps & DispatchProps; @@ -48,12 +48,12 @@ export const Container: React.FC = ({ ); }; const mapStateToProps = (state: AppState, { monitorId }: OwnProps) => ({ - monitorLocations: selectMonitorLocations(state, monitorId), + monitorLocations: monitorLocationsSelector(state, monitorId), }); const mapDispatchToProps = (dispatch: Dispatch) => ({ loadMonitorLocations: (params: MonitorLocationsPayload) => { - dispatch(fetchMonitorLocations(params)); + dispatch(getMonitorLocationsAction(params)); }, }); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/parameterize_values.test.ts.snap b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/parameterize_values.test.ts.snap deleted file mode 100644 index 39c28a87f5e71..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/parameterize_values.test.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`parameterizeValues parameterizes provided values for multiple fields 1`] = `"foo=bar&foo=baz&bar=foo&bar=baz"`; - -exports[`parameterizeValues parameterizes the provided values for a given field name 1`] = `"foo=bar&foo=baz"`; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/get_api_path.test.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/get_api_path.test.ts deleted file mode 100644 index c111008fdc3d1..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/get_api_path.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getApiPath } from '../get_api_path'; - -describe('getApiPath', () => { - it('returns a path with basePath when provided', () => { - const result = getApiPath('/api/foo/bar', '/somebasepath'); - expect(result).toEqual('/somebasepath/api/foo/bar'); - }); - - it('returns a valid path when no basePath present', () => { - const result = getApiPath('/api/foo/bar'); - expect(result).toEqual('/api/foo/bar'); - }); - - it('returns a valid path when an empty string is supplied as basePath', () => { - const result = getApiPath('/api/foo/bar', ''); - expect(result).toEqual('/api/foo/bar'); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/parameterize_values.test.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/parameterize_values.test.ts deleted file mode 100644 index e550a1a6397e3..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/parameterize_values.test.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 { parameterizeValues } from '../parameterize_values'; - -describe('parameterizeValues', () => { - let params: URLSearchParams; - - beforeEach(() => { - params = new URLSearchParams(); - }); - - it('parameterizes the provided values for a given field name', () => { - parameterizeValues(params, { foo: ['bar', 'baz'] }); - expect(params.toString()).toMatchSnapshot(); - }); - - it('parameterizes provided values for multiple fields', () => { - parameterizeValues(params, { foo: ['bar', 'baz'], bar: ['foo', 'baz'] }); - expect(params.toString()).toMatchSnapshot(); - }); - - it('returns an empty string when there are no values provided', () => { - parameterizeValues(params, { foo: [] }); - expect(params.toString()).toBe(''); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/index.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/index.ts index ef191ce32e532..e2aa4a2b3d429 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/index.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/index.ts @@ -7,9 +7,7 @@ export { combineFiltersAndUserSearch } from './combine_filters_and_user_search'; export { convertMicrosecondsToMilliseconds } from './convert_measurements'; export * from './observability_integration'; -export { getApiPath } from './get_api_path'; export { getChartDateLabel } from './charts'; -export { parameterizeValues } from './parameterize_values'; export { seriesHasDownValues } from './series_has_down_values'; export { stringifyKueries } from './stringify_kueries'; export { UptimeUrlParams, getSupportedUrlParams } from './url_params'; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/parameterize_values.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/parameterize_values.ts deleted file mode 100644 index 4c9fa6838c2ed..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/parameterize_values.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const parameterizeValues = ( - params: URLSearchParams, - obj: Record -): void => { - Object.keys(obj).forEach(key => { - obj[key].forEach(val => { - params.append(key, val); - }); - }); -}; diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 18c4927af0797..b9d29ed017a05 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -17,7 +17,7 @@ import { MonitorStatusDetails } from '../components/connected'; import { Ping } from '../../common/graphql/types'; import { AppState } from '../state'; import { selectSelectedMonitor } from '../state/selectors'; -import { getSelectedMonitor } from '../state/actions'; +import { getSelectedMonitorAction } from '../state/actions'; import { PageHeader } from './page_header'; interface StateProps { @@ -102,7 +102,7 @@ const mapDispatchToProps: MapDispatchToPropsFunction = (dispa return { dispatchGetMonitorStatus: (monitorId: string) => { dispatch( - getSelectedMonitor({ + getSelectedMonitorAction({ monitorId, }) ); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts index cf4525a08e43c..30ea8e71265e0 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/monitor.ts @@ -4,108 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ +import { createAction } from 'redux-actions'; import { MonitorDetailsActionPayload } from './types'; import { MonitorError } from '../../../common/runtime_types'; import { MonitorLocations } from '../../../common/runtime_types'; import { QueryParams } from './types'; -export const FETCH_MONITOR_DETAILS = 'FETCH_MONITOR_DETAILS'; -export const FETCH_MONITOR_DETAILS_SUCCESS = 'FETCH_MONITOR_DETAILS_SUCCESS'; -export const FETCH_MONITOR_DETAILS_FAIL = 'FETCH_MONITOR_DETAILS_FAIL'; - -export const FETCH_MONITOR_LOCATIONS = 'FETCH_MONITOR_LOCATIONS'; -export const FETCH_MONITOR_LOCATIONS_SUCCESS = 'FETCH_MONITOR_LOCATIONS_SUCCESS'; -export const FETCH_MONITOR_LOCATIONS_FAIL = 'FETCH_MONITOR_LOCATIONS_FAIL'; - -export interface MonitorDetailsState { - monitorId: string; - error: MonitorError; -} - -interface GetMonitorDetailsAction { - type: typeof FETCH_MONITOR_DETAILS; - payload: MonitorDetailsActionPayload; -} - -interface GetMonitorDetailsSuccessAction { - type: typeof FETCH_MONITOR_DETAILS_SUCCESS; - payload: MonitorDetailsState; -} - -interface GetMonitorDetailsFailAction { - type: typeof FETCH_MONITOR_DETAILS_FAIL; - payload: any; -} - export interface MonitorLocationsPayload extends QueryParams { monitorId: string; } -interface GetMonitorLocationsAction { - type: typeof FETCH_MONITOR_LOCATIONS; - payload: MonitorLocationsPayload; -} - -interface GetMonitorLocationsSuccessAction { - type: typeof FETCH_MONITOR_LOCATIONS_SUCCESS; - payload: MonitorLocations; -} - -interface GetMonitorLocationsFailAction { - type: typeof FETCH_MONITOR_LOCATIONS_FAIL; - payload: any; -} - -export function fetchMonitorDetails(payload: MonitorDetailsActionPayload): GetMonitorDetailsAction { - return { - type: FETCH_MONITOR_DETAILS, - payload, - }; -} - -export function fetchMonitorDetailsSuccess( - monitorDetailsState: MonitorDetailsState -): GetMonitorDetailsSuccessAction { - return { - type: FETCH_MONITOR_DETAILS_SUCCESS, - payload: monitorDetailsState, - }; -} - -export function fetchMonitorDetailsFail(error: any): GetMonitorDetailsFailAction { - return { - type: FETCH_MONITOR_DETAILS_FAIL, - payload: error, - }; -} - -export function fetchMonitorLocations(payload: MonitorLocationsPayload): GetMonitorLocationsAction { - return { - type: FETCH_MONITOR_LOCATIONS, - payload, - }; -} - -export function fetchMonitorLocationsSuccess( - monitorLocationsState: MonitorLocations -): GetMonitorLocationsSuccessAction { - return { - type: FETCH_MONITOR_LOCATIONS_SUCCESS, - payload: monitorLocationsState, - }; -} - -export function fetchMonitorLocationsFail(error: any): GetMonitorLocationsFailAction { - return { - type: FETCH_MONITOR_LOCATIONS_FAIL, - payload: error, - }; +export interface MonitorDetailsState { + monitorId: string; + error: MonitorError; } -export type MonitorActionTypes = - | GetMonitorDetailsAction - | GetMonitorDetailsSuccessAction - | GetMonitorDetailsFailAction - | GetMonitorLocationsAction - | GetMonitorLocationsSuccessAction - | GetMonitorLocationsFailAction; +export const getMonitorDetailsAction = createAction( + 'GET_MONITOR_DETAILS' +); +export const getMonitorDetailsActionSuccess = createAction( + 'GET_MONITOR_DETAILS_SUCCESS' +); +export const getMonitorDetailsActionFail = createAction('GET_MONITOR_DETAILS_FAIL'); + +export const getMonitorLocationsAction = createAction( + 'GET_MONITOR_LOCATIONS' +); +export const getMonitorLocationsActionSuccess = createAction( + 'GET_MONITOR_LOCATIONS_SUCCESS' +); +export const getMonitorLocationsActionFail = createAction('GET_MONITOR_LOCATIONS_FAIL'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts index db103f6cb780e..7917628abf7da 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts @@ -5,11 +5,12 @@ */ import { createAction } from 'redux-actions'; import { QueryParams } from './types'; +import { Ping } from '../../../common/graphql/types'; -export const getSelectedMonitor = createAction<{ monitorId: string }>('GET_SELECTED_MONITOR'); -export const getSelectedMonitorSuccess = createAction('GET_SELECTED_MONITOR_SUCCESS'); -export const getSelectedMonitorFail = createAction('GET_SELECTED_MONITOR_FAIL'); +export const getSelectedMonitorAction = createAction<{ monitorId: string }>('GET_SELECTED_MONITOR'); +export const getSelectedMonitorActionSuccess = createAction('GET_SELECTED_MONITOR_SUCCESS'); +export const getSelectedMonitorActionFail = createAction('GET_SELECTED_MONITOR_FAIL'); -export const getMonitorStatus = createAction('GET_MONITOR_STATUS'); -export const getMonitorStatusSuccess = createAction('GET_MONITOR_STATUS_SUCCESS'); -export const getMonitorStatusFail = createAction('GET_MONITOR_STATUS_FAIL'); +export const getMonitorStatusAction = createAction('GET_MONITOR_STATUS'); +export const getMonitorStatusActionSuccess = createAction('GET_MONITOR_STATUS_SUCCESS'); +export const getMonitorStatusActionFail = createAction('GET_MONITOR_STATUS_FAIL'); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts index 57d2b4ce38204..e819a553e61f5 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/snapshot.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { createAction } from 'redux-actions'; import { Snapshot } from '../../../common/runtime_types'; -export const FETCH_SNAPSHOT_COUNT = 'FETCH_SNAPSHOT_COUNT'; -export const FETCH_SNAPSHOT_COUNT_FAIL = 'FETCH_SNAPSHOT_COUNT_FAIL'; -export const FETCH_SNAPSHOT_COUNT_SUCCESS = 'FETCH_SNAPSHOT_COUNT_SUCCESS'; - export interface GetSnapshotPayload { dateRangeStart: string; dateRangeEnd: string; @@ -17,47 +14,6 @@ export interface GetSnapshotPayload { statusFilter?: string; } -interface GetSnapshotCountFetchAction { - type: typeof FETCH_SNAPSHOT_COUNT; - payload: GetSnapshotPayload; -} - -interface GetSnapshotCountSuccessAction { - type: typeof FETCH_SNAPSHOT_COUNT_SUCCESS; - payload: Snapshot; -} - -interface GetSnapshotCountFailAction { - type: typeof FETCH_SNAPSHOT_COUNT_FAIL; - payload: Error; -} - -export type SnapshotActionTypes = - | GetSnapshotCountFetchAction - | GetSnapshotCountSuccessAction - | GetSnapshotCountFailAction; - -export const fetchSnapshotCount = ( - dateRangeStart: string, - dateRangeEnd: string, - filters?: string, - statusFilter?: string -): GetSnapshotCountFetchAction => ({ - type: FETCH_SNAPSHOT_COUNT, - payload: { - dateRangeStart, - dateRangeEnd, - filters, - statusFilter, - }, -}); - -export const fetchSnapshotCountFail = (error: Error): GetSnapshotCountFailAction => ({ - type: FETCH_SNAPSHOT_COUNT_FAIL, - payload: error, -}); - -export const fetchSnapshotCountSuccess = (snapshot: Snapshot) => ({ - type: FETCH_SNAPSHOT_COUNT_SUCCESS, - payload: snapshot, -}); +export const getSnapshotCountAction = createAction('GET_SNAPSHOT_COUNT'); +export const getSnapshotCountActionSuccess = createAction('GET_SNAPSHOT_COUNT_SUCCESS'); +export const getSnapshotCountActionFail = createAction('GET_SNAPSHOT_COUNT_FAIL'); diff --git a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap b/x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap index 0d2392390c7e4..1cd2aae446519 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap +++ b/x-pack/legacy/plugins/uptime/public/state/api/__tests__/__snapshots__/snapshot.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`snapshot API throws when server response doesn't correspond to expected type 1`] = ` -[Error: Invalid value undefined supplied to : { down: number, total: number, up: number }/down: number -Invalid value undefined supplied to : { down: number, total: number, up: number }/total: number -Invalid value undefined supplied to : { down: number, total: number, up: number }/up: number] +Object { + "foo": "bar", +} `; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts b/x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts index e9b1391a23e32..66b376c3ac36f 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/__tests__/snapshot.test.ts @@ -5,17 +5,19 @@ */ import { fetchSnapshotCount } from '../snapshot'; +import { apiService } from '../utils'; +import { HttpFetchError } from '../../../../../../../../src/core/public/http/http_fetch_error'; describe('snapshot API', () => { - let fetchMock: jest.SpyInstance>>; - let mockResponse: Partial; + let fetchMock: jest.SpyInstance>; + let mockResponse: Partial; beforeEach(() => { - fetchMock = jest.spyOn(window, 'fetch'); - mockResponse = { - ok: true, - json: () => new Promise(r => r({ up: 3, down: 12, total: 15 })), - }; + apiService.http = { + get: jest.fn(), + } as any; + fetchMock = jest.spyOn(apiService.http, 'get'); + mockResponse = { up: 3, down: 12, total: 15 }; }); afterEach(() => { @@ -25,49 +27,43 @@ describe('snapshot API', () => { it('calls url with expected params and returns response body on 200', async () => { fetchMock.mockReturnValue(new Promise(r => r(mockResponse))); const resp = await fetchSnapshotCount({ - basePath: '', dateRangeStart: 'now-15m', dateRangeEnd: 'now', filters: 'monitor.id:"auto-http-0X21EE76EAC459873F"', statusFilter: 'up', }); - expect(fetchMock).toHaveBeenCalledWith( - '/api/uptime/snapshot/count?dateRangeStart=now-15m&dateRangeEnd=now&filters=monitor.id%3A%22auto-http-0X21EE76EAC459873F%22&statusFilter=up' - ); + expect(fetchMock).toHaveBeenCalledWith('/api/uptime/snapshot/count', { + query: { + dateRangeEnd: 'now', + dateRangeStart: 'now-15m', + filters: 'monitor.id:"auto-http-0X21EE76EAC459873F"', + statusFilter: 'up', + }, + }); expect(resp).toEqual({ up: 3, down: 12, total: 15 }); }); it(`throws when server response doesn't correspond to expected type`, async () => { - mockResponse = { ok: true, json: () => new Promise(r => r({ foo: 'bar' })) }; + mockResponse = { foo: 'bar' }; fetchMock.mockReturnValue(new Promise(r => r(mockResponse))); - let error: Error | undefined; - try { - await fetchSnapshotCount({ - basePath: '', - dateRangeStart: 'now-15m', - dateRangeEnd: 'now', - filters: 'monitor.id: baz', - statusFilter: 'up', - }); - } catch (e) { - error = e; - } - expect(error).toMatchSnapshot(); + const result = await fetchSnapshotCount({ + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + filters: 'monitor.id: baz', + statusFilter: 'up', + }); + + expect(result).toMatchSnapshot(); }); it('throws an error when response is not ok', async () => { - mockResponse = { ok: false, statusText: 'There was an error fetching your data.' }; - fetchMock.mockReturnValue(new Promise(r => r(mockResponse))); - let error: Error | undefined; - try { - await fetchSnapshotCount({ - basePath: '', - dateRangeStart: 'now-15m', - dateRangeEnd: 'now', - }); - } catch (e) { - error = e; - } - expect(error).toEqual(new Error('There was an error fetching your data.')); + mockResponse = new HttpFetchError('There was an error fetching your data.', 'error', {} as any); + fetchMock.mockReturnValue(mockResponse); + const result = await fetchSnapshotCount({ + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + }); + + expect(result).toEqual(new Error('There was an error fetching your data.')); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts b/x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts index 2669376d728ab..1eecbc75c5bf4 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/index_pattern.ts @@ -4,18 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getApiPath } from '../../lib/helper'; +import { API_URLS } from '../../../common/constants'; +import { apiService } from './utils'; -interface APIParams { - basePath: string; -} - -export const fetchIndexPattern = async ({ basePath }: APIParams) => { - const url = getApiPath(`/api/uptime/index_pattern`, basePath); - - const response = await fetch(url); - if (!response.ok) { - throw new Error(response.statusText); - } - return await response.json(); +export const fetchIndexPattern = async () => { + return await apiService.get(API_URLS.INDEX_PATTERN); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts index 9c531b3406a7c..0e33ab617777a 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/index_status.ts @@ -4,28 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PathReporter } from 'io-ts/lib/PathReporter'; -import { isRight } from 'fp-ts/lib/Either'; -import { getApiPath } from '../../lib/helper'; -import { REST_API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; import { StatesIndexStatus, StatesIndexStatusType } from '../../../common/runtime_types'; +import { apiService } from './utils'; -interface ApiRequest { - basePath: string; -} - -export const fetchIndexStatus = async ({ basePath }: ApiRequest): Promise => { - const url = getApiPath(REST_API_URLS.INDEX_STATUS, basePath); - - const response = await fetch(url); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - const decoded = StatesIndexStatusType.decode(responseData); - PathReporter.report(decoded); - if (isRight(decoded)) { - return decoded.right; - } - throw PathReporter.report(decoded); +export const fetchIndexStatus = async (): Promise => { + return await apiService.get(API_URLS.INDEX_STATUS, undefined, StatesIndexStatusType); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts index 80fd311c3ec7e..b36eccca98da9 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts @@ -4,71 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PathReporter } from 'io-ts/lib/PathReporter'; -import { getApiPath } from '../../lib/helper'; import { BaseParams } from './types'; -import { - MonitorDetailsType, - MonitorDetails, - MonitorLocations, - MonitorLocationsType, -} from '../../../common/runtime_types'; +import { MonitorDetailsType, MonitorLocationsType } from '../../../common/runtime_types'; import { QueryParams } from '../actions/types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; interface ApiRequest { monitorId: string; - basePath: string; } export type MonitorQueryParams = BaseParams & ApiRequest; export const fetchMonitorDetails = async ({ monitorId, - basePath, dateStart, dateEnd, -}: MonitorQueryParams): Promise => { - const url = getApiPath(`/api/uptime/monitor/details`, basePath); +}: MonitorQueryParams) => { const params = { monitorId, dateStart, dateEnd, }; - const urlParams = new URLSearchParams(params).toString(); - const response = await fetch(`${url}?${urlParams}`); - - if (!response.ok) { - throw new Error(response.statusText); - } - return response.json().then(data => { - PathReporter.report(MonitorDetailsType.decode(data)); - return data; - }); + return await apiService.get(API_URLS.MONITOR_DETAILS, params, MonitorDetailsType); }; type ApiParams = QueryParams & ApiRequest; -export const fetchMonitorLocations = async ({ - monitorId, - basePath, - dateStart, - dateEnd, -}: ApiParams): Promise => { - const url = getApiPath(`/api/uptime/monitor/locations`, basePath); - +export const fetchMonitorLocations = async ({ monitorId, dateStart, dateEnd }: ApiParams) => { const params = { dateStart, dateEnd, monitorId, }; - const urlParams = new URLSearchParams(params).toString(); - const response = await fetch(`${url}?${urlParams}`); - - if (!response.ok) { - throw new Error(response.statusText); - } - return response.json().then(data => { - PathReporter.report(MonitorLocationsType.decode(data)); - return data; - }); + return await apiService.get(API_URLS.MONITOR_LOCATIONS, params, MonitorLocationsType); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts index 44e797457e5fd..daf725119fcf3 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts @@ -4,29 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { stringify } from 'query-string'; - -import { getApiPath } from '../../lib/helper'; import { BaseParams } from './types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; -export const fetchMonitorDuration = async ({ - basePath, - monitorId, - dateStart, - dateEnd, -}: BaseParams) => { - const url = getApiPath(`/api/uptime/monitor/duration`, basePath); - - const params = { +export const fetchMonitorDuration = async ({ monitorId, dateStart, dateEnd }: BaseParams) => { + const queryParams = { monitorId, dateStart, dateEnd, }; - const urlParams = stringify(params); - const response = await fetch(`${url}?${urlParams}`); - if (!response.ok) { - throw new Error(response.statusText); - } - return await response.json(); + return await apiService.get(API_URLS.MONITOR_DURATION, queryParams); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts index 936e864b75619..0f7608ba57ea7 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts @@ -4,46 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getApiPath } from '../../lib/helper'; import { QueryParams } from '../actions/types'; import { Ping } from '../../../common/graphql/types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; export interface APIParams { - basePath: string; monitorId: string; } -export const fetchSelectedMonitor = async ({ basePath, monitorId }: APIParams): Promise => { - const url = getApiPath(`/api/uptime/monitor/selected`, basePath); - const params = { +export const fetchSelectedMonitor = async ({ monitorId }: APIParams): Promise => { + const queryParams = { monitorId, }; - const urlParams = new URLSearchParams(params).toString(); - const response = await fetch(`${url}?${urlParams}`); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - return responseData; + + return await apiService.get(API_URLS.MONITOR_SELECTED, queryParams); }; export const fetchMonitorStatus = async ({ - basePath, monitorId, dateStart, dateEnd, -}: QueryParams & APIParams): Promise => { - const url = getApiPath(`/api/uptime/monitor/status`, basePath); - const params = { +}: QueryParams): Promise => { + const queryParams = { monitorId, dateStart, dateEnd, }; - const urlParams = new URLSearchParams(params).toString(); - const response = await fetch(`${url}?${urlParams}`); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - return responseData; + + return await apiService.get(API_URLS.MONITOR_STATUS, queryParams); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts b/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts index c3ef62fa88dcf..9943bc27f11f0 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts @@ -4,18 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; -import { isRight } from 'fp-ts/lib/Either'; import { GetOverviewFiltersPayload } from '../actions/overview_filters'; -import { getApiPath, parameterizeValues } from '../../lib/helper'; import { OverviewFiltersType } from '../../../common/runtime_types'; - -type ApiRequest = GetOverviewFiltersPayload & { - basePath: string; -}; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; export const fetchOverviewFilters = async ({ - basePath, dateRangeStart, dateRangeEnd, search, @@ -23,30 +17,16 @@ export const fetchOverviewFilters = async ({ locations, ports, tags, -}: ApiRequest) => { - const url = getApiPath(`/api/uptime/filters`, basePath); - - const params = new URLSearchParams({ +}: GetOverviewFiltersPayload) => { + const queryParams = { dateRangeStart, dateRangeEnd, - }); - - if (search) { - params.append('search', search); - } - - parameterizeValues(params, { schemes, locations, ports, tags }); - - const response = await fetch(`${url}?${params.toString()}`); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - const decoded = OverviewFiltersType.decode(responseData); - - ThrowReporter.report(decoded); - if (isRight(decoded)) { - return decoded.right; - } - throw new Error('`getOverviewFilters` response did not correspond to expected type'); + schemes, + locations, + ports, + tags, + search, + }; + + return await apiService.get(API_URLS.FILTERS, queryParams, OverviewFiltersType); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index c61bf42c8c90e..df71cc8d67bd0 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -4,32 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { stringify } from 'query-string'; -import { getApiPath } from '../../lib/helper'; import { APIFn } from './types'; import { GetPingHistogramParams, HistogramResult } from '../../../common/types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; export const fetchPingHistogram: APIFn = async ({ - basePath, monitorId, dateStart, dateEnd, statusFilter, filters, }) => { - const url = getApiPath(`/api/uptime/ping/histogram`, basePath); - const params = { + const queryParams = { dateStart, dateEnd, ...(monitorId && { monitorId }), ...(statusFilter && { statusFilter }), ...(filters && { filters }), }; - const urlParams = stringify(params, { sort: false }); - const response = await fetch(`${url}?${urlParams}`); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - return responseData; + + return await apiService.get(API_URLS.PING_HISTOGRAM, queryParams); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts index cbfe00a4a8746..e663d0241d688 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ThrowReporter } from 'io-ts/lib/ThrowReporter'; -import { isRight } from 'fp-ts/lib/Either'; -import { getApiPath } from '../../lib/helper'; import { SnapshotType, Snapshot } from '../../../common/runtime_types'; +import { apiService } from './utils'; +import { API_URLS } from '../../../common/constants/rest_api'; -interface ApiRequest { - basePath: string; +export interface SnapShotQueryParams { dateRangeStart: string; dateRangeEnd: string; filters?: string; @@ -18,29 +16,17 @@ interface ApiRequest { } export const fetchSnapshotCount = async ({ - basePath, dateRangeStart, dateRangeEnd, filters, statusFilter, -}: ApiRequest): Promise => { - const url = getApiPath(`/api/uptime/snapshot/count`, basePath); - const params = { +}: SnapShotQueryParams): Promise => { + const queryParams = { dateRangeStart, dateRangeEnd, ...(filters && { filters }), ...(statusFilter && { statusFilter }), }; - const urlParams = new URLSearchParams(params).toString(); - const response = await fetch(`${url}?${urlParams}`); - if (!response.ok) { - throw new Error(response.statusText); - } - const responseData = await response.json(); - const decoded = SnapshotType.decode(responseData); - ThrowReporter.report(decoded); - if (isRight(decoded)) { - return decoded.right; - } - throw new Error('`getSnapshotCount` response did not correspond to expected type'); + + return await apiService.get(API_URLS.SNAPSHOT_COUNT, queryParams, SnapshotType); }; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/types.ts b/x-pack/legacy/plugins/uptime/public/state/api/types.ts index a148f1c7d7ae3..4232751cbc032 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/types.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/types.ts @@ -5,7 +5,6 @@ */ export interface BaseParams { - basePath: string; dateStart: string; dateEnd: string; filters?: string; @@ -14,4 +13,4 @@ export interface BaseParams { monitorId?: string; } -export type APIFn = (params: { basePath: string } & P) => Promise; +export type APIFn = (params: P) => Promise; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/utils.ts b/x-pack/legacy/plugins/uptime/public/state/api/utils.ts new file mode 100644 index 0000000000000..e67efa8570c11 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/api/utils.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PathReporter } from 'io-ts/lib/PathReporter'; +import { isRight } from 'fp-ts/lib/Either'; +import { HttpFetchQuery, HttpSetup } from '../../../../../../../target/types/core/public'; + +class ApiService { + private static instance: ApiService; + private _http!: HttpSetup; + + public get http() { + return this._http; + } + + public set http(httpSetup: HttpSetup) { + this._http = httpSetup; + } + + private constructor() {} + + static getInstance(): ApiService { + if (!ApiService.instance) { + ApiService.instance = new ApiService(); + } + + return ApiService.instance; + } + + public async get(apiUrl: string, params?: HttpFetchQuery, decodeType?: any) { + const response = await this._http!.get(apiUrl, { query: params }); + + if (decodeType) { + const decoded = decodeType.decode(response); + if (isRight(decoded)) { + return decoded.right; + } else { + // eslint-disable-next-line no-console + console.error( + `API ${apiUrl} is not returning expected response, ${PathReporter.report(decoded)}` + ); + } + } + + return response; + } + + public async post(apiUrl: string, data?: any, decodeType?: any) { + const response = await this._http!.post(apiUrl, { + method: 'POST', + body: JSON.stringify(data), + }); + + if (decodeType) { + const decoded = decodeType.decode(response); + if (isRight(decoded)) { + return decoded.right; + } else { + // eslint-disable-next-line no-console + console.warn( + `API ${apiUrl} is not returning expected response, ${PathReporter.report(decoded)}` + ); + } + } + return response; + } + + public async delete(apiUrl: string) { + const response = await this._http!.delete(apiUrl); + if (response instanceof Error) { + throw response; + } + return response; + } +} + +export const apiService = ApiService.getInstance(); diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts index ea389ff0a6745..d1d7626b2eab3 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { call, put, select } from 'redux-saga/effects'; +import { call, put } from 'redux-saga/effects'; import { Action } from 'redux-actions'; -import { getBasePath } from '../selectors'; /** * Factory function for a fetch effect. It expects three action creators, @@ -25,15 +24,17 @@ export function fetchEffectFactory( fail: (error: Error) => Action ) { return function*(action: Action) { - try { - const { - payload: { ...params }, - } = action; - const basePath = yield select(getBasePath); - const response = yield call(fetch, { ...params, basePath }); + const { + payload: { ...params }, + } = action; + const response = yield call(fetch, params); + if (response instanceof Error) { + // eslint-disable-next-line no-console + console.error(response); + + yield put(fail(response)); + } else { yield put(success(response)); - } catch (error) { - yield put(fail(error)); } }; } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts index 1cac7424b4e5b..ed21f315476d4 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts @@ -4,48 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { call, put, takeLatest, select } from 'redux-saga/effects'; -import { Action } from 'redux-actions'; +import { takeLatest } from 'redux-saga/effects'; import { - FETCH_MONITOR_DETAILS, - FETCH_MONITOR_DETAILS_SUCCESS, - FETCH_MONITOR_DETAILS_FAIL, - FETCH_MONITOR_LOCATIONS, - FETCH_MONITOR_LOCATIONS_SUCCESS, - FETCH_MONITOR_LOCATIONS_FAIL, + getMonitorDetailsAction, + getMonitorDetailsActionSuccess, + getMonitorDetailsActionFail, + getMonitorLocationsAction, + getMonitorLocationsActionSuccess, + getMonitorLocationsActionFail, } from '../actions/monitor'; import { fetchMonitorDetails, fetchMonitorLocations } from '../api'; -import { getBasePath } from '../selectors'; -import { MonitorDetailsActionPayload } from '../actions/types'; - -function* monitorDetailsEffect(action: Action) { - const { monitorId, dateStart, dateEnd }: MonitorDetailsActionPayload = action.payload; - try { - const basePath = yield select(getBasePath); - const response = yield call(fetchMonitorDetails, { - monitorId, - basePath, - dateStart, - dateEnd, - }); - yield put({ type: FETCH_MONITOR_DETAILS_SUCCESS, payload: response }); - } catch (error) { - yield put({ type: FETCH_MONITOR_DETAILS_FAIL, payload: error.message }); - } -} - -function* monitorLocationsEffect(action: Action) { - const payload = action.payload; - try { - const basePath = yield select(getBasePath); - const response = yield call(fetchMonitorLocations, { basePath, ...payload }); - yield put({ type: FETCH_MONITOR_LOCATIONS_SUCCESS, payload: response }); - } catch (error) { - yield put({ type: FETCH_MONITOR_LOCATIONS_FAIL, payload: error.message }); - } -} +import { fetchEffectFactory } from './fetch_effect'; export function* fetchMonitorDetailsEffect() { - yield takeLatest(FETCH_MONITOR_DETAILS, monitorDetailsEffect); - yield takeLatest(FETCH_MONITOR_LOCATIONS, monitorLocationsEffect); + yield takeLatest( + getMonitorDetailsAction, + fetchEffectFactory( + fetchMonitorDetails, + getMonitorDetailsActionSuccess, + getMonitorDetailsActionFail + ) + ); + + yield takeLatest( + getMonitorLocationsAction, + fetchEffectFactory( + fetchMonitorLocations, + getMonitorLocationsActionSuccess, + getMonitorLocationsActionFail + ) + ); } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts index cab32092a14cd..1207ab20bc711 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts @@ -4,50 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { call, put, takeLatest, select } from 'redux-saga/effects'; -import { Action } from 'redux-actions'; +import { takeLatest } from 'redux-saga/effects'; import { - getSelectedMonitor, - getSelectedMonitorSuccess, - getSelectedMonitorFail, - getMonitorStatus, - getMonitorStatusSuccess, - getMonitorStatusFail, -} from '../actions/monitor_status'; + getSelectedMonitorAction, + getSelectedMonitorActionSuccess, + getSelectedMonitorActionFail, + getMonitorStatusAction, + getMonitorStatusActionSuccess, + getMonitorStatusActionFail, +} from '../actions'; import { fetchSelectedMonitor, fetchMonitorStatus } from '../api'; -import { getBasePath } from '../selectors'; - -function* selectedMonitorEffect(action: Action) { - const { monitorId } = action.payload; - try { - const basePath = yield select(getBasePath); - const response = yield call(fetchSelectedMonitor, { - monitorId, - basePath, - }); - yield put({ type: getSelectedMonitorSuccess, payload: response }); - } catch (error) { - yield put({ type: getSelectedMonitorFail, payload: error.message }); - } -} - -function* monitorStatusEffect(action: Action) { - const { monitorId, dateStart, dateEnd } = action.payload; - try { - const basePath = yield select(getBasePath); - const response = yield call(fetchMonitorStatus, { - monitorId, - basePath, - dateStart, - dateEnd, - }); - yield put({ type: getMonitorStatusSuccess, payload: response }); - } catch (error) { - yield put({ type: getMonitorStatusFail, payload: error.message }); - } -} +import { fetchEffectFactory } from './fetch_effect'; export function* fetchMonitorStatusEffect() { - yield takeLatest(getMonitorStatus, monitorStatusEffect); - yield takeLatest(getSelectedMonitor, selectedMonitorEffect); + yield takeLatest( + getMonitorStatusAction, + fetchEffectFactory( + fetchMonitorStatus, + getMonitorStatusActionSuccess, + getMonitorStatusActionFail + ) + ); + + yield takeLatest( + getSelectedMonitorAction, + fetchEffectFactory( + fetchSelectedMonitor, + getSelectedMonitorActionSuccess, + getSelectedMonitorActionFail + ) + ); } diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts index 91df43dd9e826..10010004d47a0 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts @@ -6,16 +6,20 @@ import { takeLatest } from 'redux-saga/effects'; import { - FETCH_SNAPSHOT_COUNT, - fetchSnapshotCountFail, - fetchSnapshotCountSuccess, + getSnapshotCountAction, + getSnapshotCountActionFail, + getSnapshotCountActionSuccess, } from '../actions'; import { fetchSnapshotCount } from '../api'; import { fetchEffectFactory } from './fetch_effect'; export function* fetchSnapshotCountEffect() { yield takeLatest( - FETCH_SNAPSHOT_COUNT, - fetchEffectFactory(fetchSnapshotCount, fetchSnapshotCountSuccess, fetchSnapshotCountFail) + getSnapshotCountAction, + fetchEffectFactory( + fetchSnapshotCount, + getSnapshotCountActionSuccess, + getSnapshotCountActionFail + ) ); } diff --git a/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts b/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts new file mode 100644 index 0000000000000..4fd2d446daa17 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/state/kibana_service.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 { CoreStart } from 'kibana/public'; +import { apiService } from './api/utils'; + +class KibanaService { + private static instance: KibanaService; + private _core!: CoreStart; + + public get core() { + return this._core; + } + + public set core(coreStart: CoreStart) { + this._core = coreStart; + apiService.http = this._core.http; + } + + private constructor() {} + + static getInstance(): KibanaService { + if (!KibanaService.instance) { + KibanaService.instance = new KibanaService(); + } + + return KibanaService.instance; + } +} + +export const kibanaService = KibanaService.getInstance(); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts index 95c576e0fd72e..3650422571ce8 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts @@ -5,19 +5,20 @@ */ import { snapshotReducer } from '../snapshot'; -import { SnapshotActionTypes } from '../../actions'; +import { + getSnapshotCountAction, + getSnapshotCountActionSuccess, + getSnapshotCountActionFail, +} from '../../actions'; describe('snapshot reducer', () => { it('updates existing state', () => { - const action: SnapshotActionTypes = { - type: 'FETCH_SNAPSHOT_COUNT', - payload: { - dateRangeStart: 'now-15m', - dateRangeEnd: 'now', - filters: 'foo: bar', - statusFilter: 'up', - }, - }; + const action = getSnapshotCountAction({ + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + filters: 'foo: bar', + statusFilter: 'up', + }); expect( snapshotReducer( { @@ -31,33 +32,28 @@ describe('snapshot reducer', () => { }); it(`sets the state's status to loading during a fetch`, () => { - const action: SnapshotActionTypes = { - type: 'FETCH_SNAPSHOT_COUNT', - payload: { - dateRangeStart: 'now-15m', - dateRangeEnd: 'now', - }, - }; + const action = getSnapshotCountAction({ + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + }); expect(snapshotReducer(undefined, action)).toMatchSnapshot(); }); it('changes the count when a snapshot fetch succeeds', () => { - const action: SnapshotActionTypes = { - type: 'FETCH_SNAPSHOT_COUNT_SUCCESS', - payload: { - up: 10, - down: 15, - total: 25, - }, - }; + const action = getSnapshotCountActionSuccess({ + up: 10, + down: 15, + total: 25, + }); + expect(snapshotReducer(undefined, action)).toMatchSnapshot(); }); it('appends a current error to existing errors list', () => { - const action: SnapshotActionTypes = { - type: 'FETCH_SNAPSHOT_COUNT_FAIL', - payload: new Error(`I couldn't get your data because the server denied the request`), - }; + const action = getSnapshotCountActionFail( + new Error(`I couldn't get your data because the server denied the request`) + ); + expect(snapshotReducer(undefined, action)).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts index aac8a90598d0c..632f3a270e1a1 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux-actions'; import { - MonitorActionTypes, MonitorDetailsState, - FETCH_MONITOR_DETAILS, - FETCH_MONITOR_DETAILS_SUCCESS, - FETCH_MONITOR_DETAILS_FAIL, - FETCH_MONITOR_LOCATIONS, - FETCH_MONITOR_LOCATIONS_SUCCESS, - FETCH_MONITOR_LOCATIONS_FAIL, + getMonitorDetailsAction, + getMonitorLocationsAction, + getMonitorDetailsActionSuccess, + getMonitorDetailsActionFail, + getMonitorLocationsActionSuccess, + getMonitorLocationsActionFail, } from '../actions/monitor'; import { MonitorLocations } from '../../../common/runtime_types'; @@ -32,14 +32,14 @@ const initialState: MonitorState = { errors: [], }; -export function monitorReducer(state = initialState, action: MonitorActionTypes): MonitorState { +export function monitorReducer(state = initialState, action: Action): MonitorState { switch (action.type) { - case FETCH_MONITOR_DETAILS: + case String(getMonitorDetailsAction): return { ...state, loading: true, }; - case FETCH_MONITOR_DETAILS_SUCCESS: + case String(getMonitorDetailsActionSuccess): const { monitorId } = action.payload; return { ...state, @@ -49,17 +49,17 @@ export function monitorReducer(state = initialState, action: MonitorActionTypes) }, loading: false, }; - case FETCH_MONITOR_DETAILS_FAIL: + case String(getMonitorDetailsActionFail): return { ...state, errors: [...state.errors, action.payload], }; - case FETCH_MONITOR_LOCATIONS: + case String(getMonitorLocationsAction): return { ...state, loading: true, }; - case FETCH_MONITOR_LOCATIONS_SUCCESS: + case String(getMonitorLocationsActionSuccess): const monLocations = state.monitorLocationsList; monLocations.set(action.payload.monitorId, action.payload); return { @@ -67,7 +67,7 @@ export function monitorReducer(state = initialState, action: MonitorActionTypes) monitorLocationsList: monLocations, loading: false, }; - case FETCH_MONITOR_LOCATIONS_FAIL: + case String(getMonitorLocationsActionFail): return { ...state, errors: [...state.errors, action.payload], diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts index 2688a0946dd61..c2dfbd7f90ff2 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts @@ -5,12 +5,12 @@ */ import { handleActions, Action } from 'redux-actions'; import { - getSelectedMonitor, - getSelectedMonitorSuccess, - getSelectedMonitorFail, - getMonitorStatus, - getMonitorStatusSuccess, - getMonitorStatusFail, + getSelectedMonitorAction, + getSelectedMonitorActionSuccess, + getSelectedMonitorActionFail, + getMonitorStatusAction, + getMonitorStatusActionSuccess, + getMonitorStatusActionFail, } from '../actions'; import { Ping } from '../../../common/graphql/types'; import { QueryParams } from '../actions/types'; @@ -31,34 +31,34 @@ type MonitorStatusPayload = QueryParams & Ping; export const monitorStatusReducer = handleActions( { - [String(getSelectedMonitor)]: (state, action: Action) => ({ + [String(getSelectedMonitorAction)]: (state, action: Action) => ({ ...state, loading: true, }), - [String(getSelectedMonitorSuccess)]: (state, action: Action) => ({ + [String(getSelectedMonitorActionSuccess)]: (state, action: Action) => ({ ...state, loading: false, monitor: { ...action.payload } as Ping, }), - [String(getSelectedMonitorFail)]: (state, action: Action) => ({ + [String(getSelectedMonitorActionFail)]: (state, action: Action) => ({ ...state, loading: false, }), - [String(getMonitorStatus)]: (state, action: Action) => ({ + [String(getMonitorStatusAction)]: (state, action: Action) => ({ ...state, loading: true, }), - [String(getMonitorStatusSuccess)]: (state, action: Action) => ({ + [String(getMonitorStatusActionSuccess)]: (state, action: Action) => ({ ...state, loading: false, status: { ...action.payload } as Ping, }), - [String(getMonitorStatusFail)]: (state, action: Action) => ({ + [String(getMonitorStatusActionFail)]: (state, action: Action) => ({ ...state, loading: false, }), diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts index b219421f4f4dc..0b67d8b0e7689 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts @@ -49,6 +49,7 @@ export function overviewFiltersReducer( return { ...state, errors: [...state.errors, action.payload], + loading: false, }; default: return state; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts index 2155d0e3a74e3..3ba1ef84d41a5 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Action } from 'redux-actions'; import { Snapshot } from '../../../common/runtime_types'; import { - FETCH_SNAPSHOT_COUNT, - FETCH_SNAPSHOT_COUNT_FAIL, - FETCH_SNAPSHOT_COUNT_SUCCESS, - SnapshotActionTypes, + getSnapshotCountAction, + getSnapshotCountActionSuccess, + getSnapshotCountActionFail, } from '../actions'; export interface SnapshotState { @@ -28,20 +28,20 @@ const initialState: SnapshotState = { loading: false, }; -export function snapshotReducer(state = initialState, action: SnapshotActionTypes): SnapshotState { +export function snapshotReducer(state = initialState, action: Action): SnapshotState { switch (action.type) { - case FETCH_SNAPSHOT_COUNT: + case String(getSnapshotCountAction): return { ...state, loading: true, }; - case FETCH_SNAPSHOT_COUNT_SUCCESS: + case String(getSnapshotCountActionSuccess): return { ...state, count: action.payload, loading: false, }; - case FETCH_SNAPSHOT_COUNT_FAIL: + case String(getSnapshotCountActionFail): return { ...state, errors: [...state.errors, action.payload], diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index adba288b8b145..4767c25e8f52f 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -13,11 +13,11 @@ export const isIntegrationsPopupOpen = ({ ui: { integrationsPopoverOpen } }: App integrationsPopoverOpen; // Monitor Selectors -export const getMonitorDetails = (state: AppState, summary: any) => { +export const monitorDetailsSelector = (state: AppState, summary: any) => { return state.monitor.monitorDetailsList[summary.monitor_id]; }; -export const selectMonitorLocations = (state: AppState, monitorId: string) => { +export const monitorLocationsSelector = (state: AppState, monitorId: string) => { return state.monitor.monitorLocationsList?.get(monitorId); }; diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index 427870797a206..09156db9ca7d2 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -23,6 +23,7 @@ import { CommonlyUsedRange } from './components/functional/uptime_date_picker'; import { store } from './state'; import { setBasePath } from './state/actions'; import { PageRouter } from './routes'; +import { kibanaService } from './state/kibana_service'; export interface UptimeAppColors { danger: string; @@ -83,6 +84,8 @@ const Application = (props: UptimeAppProps) => { ); }, [canSave, renderGlobalHelpControls, setBadge]); + kibanaService.core = core; + // @ts-ignore store.dispatch(setBasePath(basePath)); diff --git a/x-pack/legacy/plugins/xpack_main/public/components/index.js b/x-pack/legacy/plugins/xpack_main/public/components/index.js index e57bd6af189f8..871d86e642dec 100644 --- a/x-pack/legacy/plugins/xpack_main/public/components/index.js +++ b/x-pack/legacy/plugins/xpack_main/public/components/index.js @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -export { LicenseStatus } from '../../../license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status'; +export { LicenseStatus } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/license_status/license_status'; -export { AddLicense } from '../../../license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license'; +export { AddLicense } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/add_license/add_license'; /* * For to link to management */ -export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../license_management/common/constants'; +export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../../../plugins/license_management/common/constants'; diff --git a/x-pack/package.json b/x-pack/package.json index 268434a35d705..02ee9deb0d12f 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -23,7 +23,7 @@ } }, "resolutions": { - "**/@types/node": "10.12.27" + "**/@types/node": ">=10.17.17 <10.20.0" }, "devDependencies": { "@cypress/webpack-preprocessor": "^4.1.0", @@ -80,7 +80,7 @@ "@types/mime": "^2.0.1", "@types/mocha": "^5.2.7", "@types/nock": "^10.0.3", - "@types/node": "^10.12.27", + "@types/node": ">=10.17.17 <10.20.0", "@types/node-fetch": "^2.5.0", "@types/nodemailer": "^6.2.1", "@types/object-hash": "^1.3.0", @@ -179,7 +179,7 @@ "@babel/runtime": "^7.5.5", "@elastic/apm-rum-react": "^0.3.2", "@elastic/datemath": "5.0.2", - "@elastic/ems-client": "7.6.0", + "@elastic/ems-client": "7.7.0", "@elastic/eui": "20.0.2", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.1.0", @@ -317,7 +317,7 @@ "react-sticky": "^6.0.3", "react-syntax-highlighter": "^5.7.0", "react-tiny-virtual-list": "^2.2.0", - "react-use": "^13.13.0", + "react-use": "^13.27.0", "react-vis": "^1.8.1", "react-visibility-sensor": "^5.1.1", "recompose": "^0.26.0", diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts index 0be1983477256..7eded9bb40964 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts @@ -43,18 +43,46 @@ describe('actionTypeRegistry.get() works', () => { describe('config validation', () => { test('config validation succeeds when config is valid', () => { - const config: Record = {}; + const config: Record = { + index: 'testing-123', + refresh: false, + }; expect(validateConfig(actionType, config)).toEqual({ ...config, - index: null, + index: 'testing-123', + refresh: false, }); - config.index = 'testing-123'; + config.executionTimeField = 'field-123'; expect(validateConfig(actionType, config)).toEqual({ ...config, index: 'testing-123', + refresh: false, + executionTimeField: 'field-123', }); + + delete config.index; + + expect(() => { + validateConfig(actionType, { index: 666 }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [index]: expected value of type [string] but got [number]"` + ); + delete config.executionTimeField; + + expect(() => { + validateConfig(actionType, { index: 'testing-123', executionTimeField: true }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [executionTimeField]: expected value of type [string] but got [boolean]"` + ); + + delete config.refresh; + expect(() => { + validateConfig(actionType, { index: 'testing-123', refresh: 'foo' }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type config: [refresh]: expected value of type [boolean] but got [string]"` + ); }); test('config validation fails when config is not valid', () => { @@ -65,46 +93,16 @@ describe('config validation', () => { expect(() => { validateConfig(actionType, baseConfig); }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type config: [indeX]: definition for this key is missing"` + `"error validating action type config: [index]: expected value of type [string] but got [undefined]"` ); - - delete baseConfig.user; - baseConfig.index = 666; - - expect(() => { - validateConfig(actionType, baseConfig); - }).toThrowErrorMatchingInlineSnapshot(` -"error validating action type config: [index]: types that failed validation: -- [index.0]: expected value of type [string] but got [number] -- [index.1]: expected value to equal [null]" -`); }); }); describe('params validation', () => { test('params validation succeeds when params is valid', () => { const params: Record = { - index: 'testing-123', - executionTimeField: 'field-used-for-time', - refresh: true, documents: [{ rando: 'thing' }], }; - expect(validateParams(actionType, params)).toMatchInlineSnapshot(` - Object { - "documents": Array [ - Object { - "rando": "thing", - }, - ], - "executionTimeField": "field-used-for-time", - "index": "testing-123", - "refresh": true, - } - `); - - delete params.index; - delete params.refresh; - delete params.executionTimeField; expect(validateParams(actionType, params)).toMatchInlineSnapshot(` Object { "documents": Array [ @@ -129,24 +127,6 @@ describe('params validation', () => { `"error validating action params: [documents]: expected value of type [array] but got [undefined]"` ); - expect(() => { - validateParams(actionType, { index: 666 }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [index]: expected value of type [string] but got [number]"` - ); - - expect(() => { - validateParams(actionType, { executionTimeField: true }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [executionTimeField]: expected value of type [string] but got [boolean]"` - ); - - expect(() => { - validateParams(actionType, { refresh: 'foo' }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [refresh]: expected value of type [boolean] but got [string]"` - ); - expect(() => { validateParams(actionType, { documents: ['should be an object'] }); }).toThrowErrorMatchingInlineSnapshot( @@ -162,13 +142,10 @@ describe('execute()', () => { let params: ActionParamsType; let executorOptions: ActionTypeExecutorOptions; - // minimal params, index via param - config = { index: null }; + // minimal params + config = { index: 'index-value', refresh: false, executionTimeField: undefined }; params = { - index: 'index-via-param', documents: [{ jim: 'bob' }], - executionTimeField: undefined, - refresh: undefined, }; const actionId = 'some-id'; @@ -190,19 +167,17 @@ describe('execute()', () => { "jim": "bob", }, ], - "index": "index-via-param", + "index": "index-value", + "refresh": false, }, ], ] `); - // full params (except index), index via config - config = { index: 'index-via-config' }; + // full params + config = { index: 'index-value', executionTimeField: 'field_to_use_for_time', refresh: true }; params = { - index: undefined, documents: [{ jimbob: 'jr' }], - executionTimeField: 'field_to_use_for_time', - refresh: true, }; executorOptions = { actionId, config, secrets, params, services }; @@ -226,20 +201,17 @@ describe('execute()', () => { "jimbob": "jr", }, ], - "index": "index-via-config", + "index": "index-value", "refresh": true, }, ], ] `); - // minimal params, index via config and param - config = { index: 'index-via-config' }; + // minimal params + config = { index: 'index-value', executionTimeField: undefined, refresh: false }; params = { - index: 'index-via-param', documents: [{ jim: 'bob' }], - executionTimeField: undefined, - refresh: undefined, }; executorOptions = { actionId, config, secrets, params, services }; @@ -259,19 +231,17 @@ describe('execute()', () => { "jim": "bob", }, ], - "index": "index-via-config", + "index": "index-value", + "refresh": false, }, ], ] `); // multiple documents - config = { index: null }; + config = { index: 'index-value', executionTimeField: undefined, refresh: false }; params = { - index: 'index-via-param', documents: [{ a: 1 }, { b: 2 }], - executionTimeField: undefined, - refresh: undefined, }; executorOptions = { actionId, config, secrets, params, services }; @@ -297,7 +267,8 @@ describe('execute()', () => { "b": 2, }, ], - "index": "index-via-param", + "index": "index-value", + "refresh": false, }, ], ] diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts index f8217046b2ea5..b1fe5e3af2d11 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts @@ -8,7 +8,6 @@ import { curry } from 'lodash'; import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; -import { nullableType } from './lib/nullable'; import { Logger } from '../../../../../src/core/server'; import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../types'; @@ -17,7 +16,9 @@ import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from export type ActionTypeConfigType = TypeOf; const ConfigSchema = schema.object({ - index: nullableType(schema.string()), + index: schema.string(), + refresh: schema.boolean({ defaultValue: false }), + executionTimeField: schema.maybe(schema.string()), }); // params definition @@ -28,9 +29,6 @@ export type ActionParamsType = TypeOf; // - timeout not added here, as this seems to be a generic thing we want to do // eventually: https://github.com/elastic/kibana/projects/26#card-24087404 const ParamsSchema = schema.object({ - index: schema.maybe(schema.string()), - executionTimeField: schema.maybe(schema.string()), - refresh: schema.maybe(schema.boolean()), documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), }); @@ -60,27 +58,12 @@ async function executor( const params = execOptions.params as ActionParamsType; const services = execOptions.services; - if (config.index == null && params.index == null) { - const message = i18n.translate('xpack.actions.builtin.esIndex.indexParamRequiredErrorMessage', { - defaultMessage: 'index param needs to be set because not set in config for action', - }); - return { - status: 'error', - actionId, - message, - }; - } - - if (config.index != null && params.index != null) { - logger.debug(`index passed in params overridden by index set in config for action ${actionId}`); - } - - const index = config.index || params.index; + const index = config.index; const bulkBody = []; for (const document of params.documents) { - if (params.executionTimeField != null) { - document[params.executionTimeField] = new Date(); + if (config.executionTimeField != null) { + document[config.executionTimeField] = new Date(); } bulkBody.push({ index: {} }); @@ -92,9 +75,7 @@ async function executor( body: bulkBody, }; - if (params.refresh != null) { - bulkParams.refresh = params.refresh; - } + bulkParams.refresh = config.refresh; let result; try { @@ -103,6 +84,7 @@ async function executor( const message = i18n.translate('xpack.actions.builtin.esIndex.errorIndexingErrorMessage', { defaultMessage: 'error indexing documents', }); + logger.error(`error indexing documents: ${err.message}`); return { status: 'error', actionId, diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 885391325fcd6..b4b2de19ef24f 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -173,16 +173,6 @@ export class AlertingPlugin { muteAlertInstanceRoute(router, this.licenseState); unmuteAlertInstanceRoute(router, this.licenseState); - alertTypeRegistry.register({ - id: 'test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - name: 'Test', - executor: async options => { - return { status: 'ok' }; - }, - }); - return { registerType: alertTypeRegistry.register.bind(alertTypeRegistry), }; diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts b/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts deleted file mode 100644 index a9fbe0ef4f721..0000000000000 --- a/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts +++ /dev/null @@ -1,68 +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 Boom from 'boom'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; - -import { ActionResult } from '../../../../../../actions/common'; -import { CasesConnectorConfigurationRT, throwErrors } from '../../../../../common/api'; -import { RouteDeps } from '../../types'; -import { wrapError, escapeHatch } from '../../utils'; - -export function initCaseConfigurePatchActionConnector({ caseService, router }: RouteDeps) { - router.patch( - { - path: '/api/cases/configure/connectors/{connector_id}', - validate: { - params: schema.object({ - connector_id: schema.string(), - }), - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const query = pipe( - CasesConnectorConfigurationRT.decode(request.body), - fold(throwErrors(Boom.badRequest), identity) - ); - - const client = context.core.savedObjects.client; - const { connector_id: connectorId } = request.params; - const { cases_configuration: casesConfiguration } = query; - - const normalizedMapping = casesConfiguration.mapping.map(m => ({ - source: m.source, - target: m.target, - actionType: m.action_type, - })); - - const action = await client.get('action', connectorId); - - const { config } = action.attributes; - const res = await client.update('action', connectorId, { - config: { - ...config, - casesConfiguration: { ...casesConfiguration, mapping: normalizedMapping }, - }, - }); - - return response.ok({ - body: CasesConnectorConfigurationRT.encode({ - cases_configuration: - res.attributes.config?.casesConfiguration ?? - action.attributes.config.casesConfiguration, - }), - }); - } catch (error) { - return response.customError(wrapError(error)); - } - } - ); -} diff --git a/x-pack/plugins/case/server/routes/api/index.ts b/x-pack/plugins/case/server/routes/api/index.ts index 956f410c9c10a..60ee57a0efea7 100644 --- a/x-pack/plugins/case/server/routes/api/index.ts +++ b/x-pack/plugins/case/server/routes/api/index.ts @@ -26,7 +26,6 @@ import { initGetTagsApi } from './cases/tags/get_tags'; import { RouteDeps } from './types'; import { initCaseConfigureGetActionConnector } from './cases/configure/get_connectors'; -import { initCaseConfigurePatchActionConnector } from './cases/configure/patch_connector'; import { initGetCaseConfigure } from './cases/configure/get_configure'; import { initPatchCaseConfigure } from './cases/configure/patch_configure'; import { initPostCaseConfigure } from './cases/configure/post_configure'; @@ -48,7 +47,6 @@ export function initCaseApi(deps: RouteDeps) { initPostCommentApi(deps); // Cases Configure initCaseConfigureGetActionConnector(deps); - initCaseConfigurePatchActionConnector(deps); initGetCaseConfigure(deps); initPatchCaseConfigure(deps); initPostCaseConfigure(deps); diff --git a/x-pack/plugins/case/server/scripts/README.md b/x-pack/plugins/case/server/scripts/README.md new file mode 100644 index 0000000000000..2c35eb305282a --- /dev/null +++ b/x-pack/plugins/case/server/scripts/README.md @@ -0,0 +1,90 @@ +README.md for developers working on the Case API on how to get started +using the CURL scripts in the scripts folder. + +The scripts rely on CURL and jq: + +- [CURL](https://curl.haxx.se) +- [jq](https://stedolan.github.io/jq/) + +Install curl and jq + +```sh +brew update +brew install curl +brew install jq +``` + +Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL` +and add these environment variables: + +```sh +export ELASTICSEARCH_USERNAME=${user} +export ELASTICSEARCH_PASSWORD=${password} +export ELASTICSEARCH_URL=https://${ip}:9200 +export KIBANA_URL=http://localhost:5601 +export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id} +export KIBANA_INDEX=.kibana-${your user id} +``` + +source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set: + +```sh +source ~/.zshrc +``` + +Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will +get in the way of the CURL scripts written as is. + +Go to the scripts folder `cd kibana/x-pack/plugins/case/server/scripts` and run: + +```sh +./hard_reset.sh +``` + +which will: + +- Delete any existing cases you have +- Delete any existing comments you have +- Posts the sample case from `./mock/case/post_case.json` +- Posts the sample comment from `./mock/comment/post_comment.json` to the new case + +Now you can run + +```sh +./find_cases.sh +``` + +You should see the new case created like so: + +```sh +{ + "page": 1, + "per_page": 20, + "total": 1, + "cases": [ + { + "id": "2e0afbc0-658c-11ea-85c8-1d8f792cbc08", + "version": "Wzc5NSwxXQ==", + "comments": [], + "comment_ids": [ + "2ecec0f0-658c-11ea-85c8-1d8f792cbc08" + ], + "created_at": "2020-03-14T00:38:53.004Z", + "created_by": { + "full_name": "Steph Milovic", + "username": "smilovic" + }, + "updated_at": null, + "updated_by": null, + "description": "This looks not so good", + "title": "Bad meanie defacing data", + "status": "open", + "tags": [ + "defacement" + ] + } + ], + "count_open_cases": 1, + "count_closed_cases": 1 +} +``` diff --git a/x-pack/plugins/case/server/scripts/check_env_variables.sh b/x-pack/plugins/case/server/scripts/check_env_variables.sh new file mode 100755 index 0000000000000..2f7644051debb --- /dev/null +++ b/x-pack/plugins/case/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/case/server/scripts/delete_cases.sh b/x-pack/plugins/case/server/scripts/delete_cases.sh new file mode 100755 index 0000000000000..c04afed5fe679 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/delete_cases.sh @@ -0,0 +1,51 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./delete_cases.sh + +# Example with CASE_ID args: +# ./delete_cases.sh 1234-example-id 5678-example-id + +set -e +./check_env_variables.sh + +if [ "$1" ]; then + ALL=("$@") + i=0 + + COUNT=${#ALL[@]} + IDS="" + for ID in "${ALL[@]}" + do + let i=i+1 + if [ $i -eq $COUNT ]; then + IDS+="%22${ID}%22" + else + IDS+="%22${ID}%22," + fi + done + + curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[${IDS}\]" \ + | jq .; + exit 1 +else + CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)") + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[%22${CASE_ID}%22\]" \ + | jq .; + exit 1 +fi diff --git a/x-pack/plugins/case/server/scripts/delete_comment.sh b/x-pack/plugins/case/server/scripts/delete_comment.sh new file mode 100755 index 0000000000000..a858d9cb11a57 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/delete_comment.sh @@ -0,0 +1,39 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./delete_comment.sh + +# Example with CASE_ID and COMMENT_ID arg: +# ./delete_comment.sh 1234-example-case-id 5678-example-comment-id + +set -e +./check_env_variables.sh + + +if [ "$1" ] && [ "$2" ]; then + curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments/$2" \ + | jq .; + exit 1 +else + DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, commentId: .commentId}' -j)" + CASE_ID=$(echo $DATA | jq ".caseId" -j) + COMMENT_ID=$(echo $DATA | jq ".commentId" -j) + curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments/$COMMENT_ID" \ + | jq .; + exit 1 +fi +./delete_case.sh [b6766a90-6559-11ea-9fd5-b52942ab389a] \ No newline at end of file diff --git a/x-pack/plugins/case/server/scripts/find_cases.sh b/x-pack/plugins/case/server/scripts/find_cases.sh new file mode 100755 index 0000000000000..bb4232b0c6c27 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/find_cases.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. +# + +# Example: +# ./find_cases.sh + +set -e +./check_env_variables.sh + +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/cases/_find | jq . diff --git a/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh b/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh new file mode 100755 index 0000000000000..433311c117e98 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh @@ -0,0 +1,28 @@ +#!/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. +# + +# Example: +# ./find_cases_by_filter.sh + +# Example get all open cases: +# ./find_cases_by_filter.sh "cases.attributes.state:%20open" + +# Example get all the names that start with Bad* +# ./find_cases_by_filter.sh "cases.attributes.title:%20Bad*" + +# Exampe get everything that has phishing +# ./find_cases_by_filter.sh "cases.attributes.tags:phishing" + +set -e +./check_env_variables.sh + +FILTER=${1:-'cases.attributes.state:%20closed'} + +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/cases/_find?filter=$FILTER | jq . diff --git a/x-pack/plugins/case/server/scripts/find_cases_sort.sh b/x-pack/plugins/case/server/scripts/find_cases_sort.sh new file mode 100755 index 0000000000000..436b475220102 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/find_cases_sort.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. +# + +# Example: +# ./find_cases_sort.sh + +# Example with sort args: +# ./find_cases_sort.sh createdAt desc + +set -e +./check_env_variables.sh + +SORT=${1:-'createdAt'} +ORDER=${2:-'asc'} + +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/_find?sortField=$SORT&sortOrder=$ORDER" \ + | jq . diff --git a/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.sh b/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.sh new file mode 100755 index 0000000000000..9b6f472d798e0 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.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. +# + +# returns case/comment data as { commentId, commentVersion, caseId, caseVersion } +# Example: +# ./generate_case_and_comment_data.sh + +set -e +./check_env_variables.sh + +COMMENT=(${1:-./mock/comment/post_comment.json}) +CASE_ID=$(./post_case.sh | jq ".id" -j) + +POSTED_COMMENT="$(curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \ + -d @${COMMENT} \ + | jq '{ commentId: .id, commentVersion: .version }' +)" +POSTED_CASE=$(./get_case.sh $CASE_ID | jq '{ caseId: .id, caseVersion: .version }' -j) + +echo ${POSTED_COMMENT} ${POSTED_CASE} \ + | jq -s add; \ No newline at end of file diff --git a/x-pack/plugins/case/server/scripts/generate_case_data.sh b/x-pack/plugins/case/server/scripts/generate_case_data.sh new file mode 100755 index 0000000000000..f8f6142a5d733 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/generate_case_data.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. +# + +# returns case data as { id, version } +# Example: +# ./generate_case_data.sh + +set -e +./check_env_variables.sh +./post_case.sh | jq '{ id: .id, version: .version }' -j; + diff --git a/x-pack/plugins/case/server/scripts/get_case.sh b/x-pack/plugins/case/server/scripts/get_case.sh new file mode 100755 index 0000000000000..c0106993fd81e --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_case.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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./get_case.sh + +# Example with CASE_ID arg: +# ./get_case.sh 1234-example-id + +set -e +./check_env_variables.sh + + +if [ "$1" ]; then + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1" \ + | jq .; + exit 1 +else + CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)") + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID" \ + | jq .; + exit 1 +fi diff --git a/x-pack/plugins/case/server/scripts/get_case_comments.sh b/x-pack/plugins/case/server/scripts/get_case_comments.sh new file mode 100755 index 0000000000000..65b7c43a68824 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_case_comments.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. +# + +# Creates a new case and comments it if no CASE_ID is specified + +# Example: +# ./get_case_comments.sh + +# Example: +# ./get_case_comments.sh 1234-example-id + +set -e +./check_env_variables.sh + + +if [ "$1" ]; then + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments" \ + | jq .; + exit 1 +else + CASE_ID="$(./generate_case_and_comment_data.sh | jq '.caseId' -j)" + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \ + | jq .; + exit 1 +fi diff --git a/x-pack/plugins/case/server/scripts/get_comment.sh b/x-pack/plugins/case/server/scripts/get_comment.sh new file mode 100755 index 0000000000000..9b2f7d6636745 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_comment.sh @@ -0,0 +1,40 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./get_comment.sh + +# Example with CASE_ID and COMMENT_ID arg: +# ./get_comment.sh 1234-example-case-id 5678-example-comment-id + +set -e +./check_env_variables.sh + + +if [ "$1" ] && [ "$2" ]; then + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments/$2" \ + | jq .; + exit 1 +else + DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, commentId: .commentId}' -j)" + CASE_ID=$(echo $DATA | jq ".caseId" -j) + COMMENT_ID=$(echo $DATA | jq ".commentId" -j) + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments/$COMMENT_ID" \ + | jq .; + exit 1 +fi diff --git a/x-pack/plugins/case/server/scripts/get_reporters.sh b/x-pack/plugins/case/server/scripts/get_reporters.sh new file mode 100755 index 0000000000000..2c926269d31f8 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_reporters.sh @@ -0,0 +1,23 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./get_tags.sh + + +set -e +./check_env_variables.sh + +curl -s -k \ +-H 'Content-Type: application/json' \ +-H 'kbn-xsrf: 123' \ +-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/reporters" \ +| jq .; diff --git a/x-pack/plugins/case/server/scripts/get_status.sh b/x-pack/plugins/case/server/scripts/get_status.sh new file mode 100755 index 0000000000000..b246a2267a222 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_status.sh @@ -0,0 +1,23 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./get_tags.sh + + +set -e +./check_env_variables.sh + +curl -s -k \ +-H 'Content-Type: application/json' \ +-H 'kbn-xsrf: 123' \ +-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/status" \ +| jq .; diff --git a/x-pack/plugins/case/server/scripts/get_tags.sh b/x-pack/plugins/case/server/scripts/get_tags.sh new file mode 100755 index 0000000000000..c5fcf13405e0c --- /dev/null +++ b/x-pack/plugins/case/server/scripts/get_tags.sh @@ -0,0 +1,23 @@ +#!/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. +# + +# Creates a new case and then gets it if no CASE_ID is specified + +# Example: +# ./get_tags.sh + + +set -e +./check_env_variables.sh + +curl -s -k \ +-H 'Content-Type: application/json' \ +-H 'kbn-xsrf: 123' \ +-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ +-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/tags" \ +| jq .; diff --git a/x-pack/plugins/case/server/scripts/hard_reset.sh b/x-pack/plugins/case/server/scripts/hard_reset.sh new file mode 100755 index 0000000000000..e5309e0ab7f6c --- /dev/null +++ b/x-pack/plugins/case/server/scripts/hard_reset.sh @@ -0,0 +1,29 @@ +#!/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. +# + +# Deletes all current cases and comments and creates one new case with a comment +# Example: +# ./hard_reset.sh + +set -e +./check_env_variables.sh +# +ALL_CASES=$(curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/_find?perPage=500" | jq '.cases' -j) + +IDS="" +for row in $(echo "${ALL_CASES}" | jq -r '.[] | @base64'); do + _jq() { + echo ${row} | base64 --decode | jq -r ${1} + } + IDS+="$(_jq '.id') " +done + +./generate_case_and_comment_data.sh +./delete_cases.sh $IDS \ No newline at end of file diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case.json b/x-pack/plugins/case/server/scripts/mock/case/post_case.json new file mode 100644 index 0000000000000..25a9780596828 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/mock/case/post_case.json @@ -0,0 +1,8 @@ +{ + "description": "This looks not so good", + "title": "Bad meanie defacing data", + "status": "open", + "tags": [ + "defacement" + ] +} diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json new file mode 100644 index 0000000000000..cf066d2c8a1e8 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json @@ -0,0 +1,8 @@ +{ + "description": "I hope there are some good security engineers at this company...", + "title": "Another bad dude", + "status": "open", + "tags": [ + "phishing" + ] +} diff --git a/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json b/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json new file mode 100644 index 0000000000000..82cf3e7ce7309 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json @@ -0,0 +1,3 @@ +{ + "comment": "Solve this fast!" +} diff --git a/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json b/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json new file mode 100644 index 0000000000000..e753231e36911 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json @@ -0,0 +1,3 @@ +{ + "comment": "This looks bad" +} diff --git a/x-pack/plugins/case/server/scripts/patch_cases.sh b/x-pack/plugins/case/server/scripts/patch_cases.sh new file mode 100755 index 0000000000000..2faa524daac7b --- /dev/null +++ b/x-pack/plugins/case/server/scripts/patch_cases.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. +# + +# A new case will be generated and the title will be updated in the PATCH call +# Example: +# ./patch_cases.sh + +set -e +./check_env_variables.sh + +PATCH_CASE="$(./generate_case_data.sh | jq '{ cases: [{ id: .id, version: .version, title: "Change the title" }] }' -j)" + +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PATCH ${KIBANA_URL}${SPACE_URL}/api/cases \ + -d "$PATCH_CASE" \ + | jq .; diff --git a/x-pack/plugins/case/server/scripts/patch_comment.sh b/x-pack/plugins/case/server/scripts/patch_comment.sh new file mode 100755 index 0000000000000..2f0bbe2883b0f --- /dev/null +++ b/x-pack/plugins/case/server/scripts/patch_comment.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. +# + +# A new case and comment will be generated and the comment will be updated in the PATCH call +# Example: +# ./patch_comment.sh + +set -e +./check_env_variables.sh + +DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, id: .commentId, version: .commentVersion, comment: "Update the comment" }' -j)" +CASE_ID=$(echo "${DATA}" | jq ".caseId" -j) +PATCH_COMMENT=$(echo "${DATA}" | jq 'del(.caseId)') + +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PATCH "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \ + -d "$PATCH_COMMENT" \ + | jq .; diff --git a/x-pack/plugins/case/server/scripts/post_case.sh b/x-pack/plugins/case/server/scripts/post_case.sh new file mode 100755 index 0000000000000..fff449741fe17 --- /dev/null +++ b/x-pack/plugins/case/server/scripts/post_case.sh @@ -0,0 +1,37 @@ +#!/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. +# + +# Example: +# ./post_case.sh + +# Example: +# ./post_case.sh ./mock/case/post_case.json + +# Example glob: +# ./post_case.sh ./mock/case/* + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +CASES=(${@:-./mock/case/post_case.json}) + +for CASE in "${CASES[@]}" +do { + [ -e "$CASE" ] || 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/cases \ + -d @${CASE} \ + | jq .; +} & +done + +wait diff --git a/x-pack/plugins/case/server/scripts/post_comment.sh b/x-pack/plugins/case/server/scripts/post_comment.sh new file mode 100755 index 0000000000000..91e07f5bd110c --- /dev/null +++ b/x-pack/plugins/case/server/scripts/post_comment.sh @@ -0,0 +1,57 @@ +#!/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. +# + +# Example: +# ./post_comment.sh + +# Example: +# ./post_comment.sh 92970bf0-64a7-11ea-9979-d394b1de38af ./mock/comment/post_comment.json + +# Example glob: +# ./post_comment.sh 92970bf0-64a7-11ea-9979-d394b1de38af ./mock/comment/* + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +COMMENTS=(${2:-./mock/comment/post_comment.json}) + +if [ "$1" ]; then + for COMMENT in "${COMMENTS[@]}" + do { + [ -e "$COMMENT" ] || 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/cases/$1/comments" \ + -d @${COMMENT} \ + | jq .; + } & + done + + wait + exit 1 +else + CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)") + for COMMENT in "${COMMENTS[@]}" + do { + [ -e "$COMMENT" ] || 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/cases/$CASE_ID/comments" \ + -d @${COMMENT} \ + | jq .; + } & + done + + wait + exit 1 +fi \ No newline at end of file diff --git a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json index a6ec31465392a..2195b74640c79 100644 --- a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json +++ b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json @@ -1,7 +1,7 @@ { "ml.estimate_memory_usage": { "methods": [ - "POST" + "PUT" ], "patterns": [ "_ml/data_frame/analytics/_estimate_memory_usage" diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts index 25c6a789cca93..c493e8ce86781 100644 --- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -10,16 +10,17 @@ import { TSearchStrategyProvider, ISearchContext, ISearch, - SYNC_SEARCH_STRATEGY, getEsPreference, } from '../../../../../src/plugins/data/public'; import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common'; +import { ASYNC_SEARCH_STRATEGY } from './async_search_strategy'; +import { IAsyncSearchOptions } from './types'; export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext ) => { - const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); - const { search: syncSearch } = syncStrategyProvider(context); + const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY); + const { search: asyncSearch } = asyncStrategyProvider(context); const search: ISearch = ( request: IEnhancedEsSearchRequest, @@ -32,9 +33,12 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider; + const asyncOptions: IAsyncSearchOptions = { pollInterval: 0, ...options }; + + return asyncSearch( + { ...request, serverStrategy: ES_SEARCH_STRATEGY }, + asyncOptions + ) as Observable; }; return { search }; diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 69b357196dc32..11f0b9a0dc83c 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -14,10 +14,16 @@ import { TSearchStrategyProvider, ISearch, ISearchOptions, + ISearchCancel, getDefaultSearchParams, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; +export interface AsyncSearchResponse { + id: string; + response: SearchResponse; +} + export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext, caller: APICaller @@ -28,28 +34,62 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider { const config = await context.config$.pipe(first()).toPromise(); const defaultParams = getDefaultSearchParams(config); - const params = { ...defaultParams, ...request.params }; + const params = { ...defaultParams, trackTotalHits: true, ...request.params }; - const rawResponse = (await (request.indexType === 'rollup' + const response = await (request.indexType === 'rollup' ? rollupSearch(caller, { ...request, params }, options) - : caller('search', params, options))) as SearchResponse; + : asyncSearch(caller, { ...request, params }, options)); + + const rawResponse = + request.indexType === 'rollup' + ? (response as SearchResponse) + : (response as AsyncSearchResponse).response; + + if (typeof rawResponse.hits.total !== 'number') { + // @ts-ignore This should be fixed as part of https://github.com/elastic/kibana/issues/26356 + rawResponse.hits.total = rawResponse.hits.total.value; + } + const id = (response as AsyncSearchResponse).id; const { total, failed, successful } = rawResponse._shards; const loaded = failed + successful; - return { total, loaded, rawResponse }; + return { id, total, loaded, rawResponse }; }; - return { search }; + const cancel: ISearchCancel = async id => { + const method = 'DELETE'; + const path = `_async_search/${id}`; + await caller('transport.request', { method, path }); + }; + + return { search, cancel }; }; -function rollupSearch( +function asyncSearch( + caller: APICaller, + request: IEnhancedEsSearchRequest, + options?: ISearchOptions +) { + const { body = undefined, index = undefined, ...params } = request.id ? {} : request.params; + + // If we have an ID, then just poll for that ID, otherwise send the entire request body + const method = request.id ? 'GET' : 'POST'; + const path = request.id ? `_async_search/${request.id}` : `${index}/_async_search`; + + // Wait up to 1s for the response to return + const query = toSnakeCase({ waitForCompletion: '1s', ...params }); + + return caller('transport.request', { method, path, body, query }, options); +} + +async function rollupSearch( caller: APICaller, request: IEnhancedEsSearchRequest, options?: ISearchOptions ) { + const { body, index, ...params } = request.params; const method = 'POST'; - const path = `${request.params.index}/_rollup_search`; - const { body, ...params } = request.params; + const path = `${index}/_rollup_search`; const query = toSnakeCase(params); return caller('transport.request', { method, path, body, query }, options); } diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts index a687d7af1c590..dfb906c7af606 100644 --- a/x-pack/plugins/endpoint/common/generate_data.test.ts +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -21,8 +21,8 @@ describe('data generator', () => { const generator1 = new EndpointDocGenerator('seed'); const generator2 = new EndpointDocGenerator('seed'); const timestamp = new Date().getTime(); - const metadata1 = generator1.generateEndpointMetadata(timestamp); - const metadata2 = generator2.generateEndpointMetadata(timestamp); + const metadata1 = generator1.generateHostMetadata(timestamp); + const metadata2 = generator2.generateHostMetadata(timestamp); expect(metadata1).toEqual(metadata2); }); @@ -30,14 +30,14 @@ describe('data generator', () => { const generator1 = new EndpointDocGenerator('seed'); const generator2 = new EndpointDocGenerator('different seed'); const timestamp = new Date().getTime(); - const metadata1 = generator1.generateEndpointMetadata(timestamp); - const metadata2 = generator2.generateEndpointMetadata(timestamp); + const metadata1 = generator1.generateHostMetadata(timestamp); + const metadata2 = generator2.generateHostMetadata(timestamp); expect(metadata1).not.toEqual(metadata2); }); - it('creates endpoint metadata documents', () => { + it('creates host metadata documents', () => { const timestamp = new Date().getTime(); - const metadata = generator.generateEndpointMetadata(timestamp); + const metadata = generator.generateHostMetadata(timestamp); expect(metadata['@timestamp']).toEqual(timestamp); expect(metadata.event.created).toEqual(timestamp); expect(metadata.endpoint).not.toBeNull(); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 36896e5af6810..2e1d6074d0c2f 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -6,7 +6,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; -import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields, HostFields } from './types'; +import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields } from './types'; export type Event = AlertEvent | EndpointEvent; @@ -104,8 +104,8 @@ export class EndpointDocGenerator { this.commonInfo = this.createHostData(); } - // This function will create new values for all the host fields, so documents from a different endpoint can be created - // This provides a convenient way to make documents from multiple endpoints that are all tied to a single seed value + // This function will create new values for all the host fields, so documents from a different host can be created + // This provides a convenient way to make documents from multiple hosts that are all tied to a single seed value public randomizeHostData() { this.commonInfo = this.createHostData(); } @@ -129,7 +129,7 @@ export class EndpointDocGenerator { }; } - public generateEndpointMetadata(ts = new Date().getTime()): EndpointMetadata { + public generateHostMetadata(ts = new Date().getTime()): HostMetadata { return { '@timestamp': ts, event: { diff --git a/x-pack/plugins/endpoint/common/models/event.ts b/x-pack/plugins/endpoint/common/models/event.ts new file mode 100644 index 0000000000000..650486f3c3858 --- /dev/null +++ b/x-pack/plugins/endpoint/common/models/event.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 { EndpointEvent, LegacyEndpointEvent } from '../types'; + +export function isLegacyEvent( + event: EndpointEvent | LegacyEndpointEvent +): event is LegacyEndpointEvent { + return (event as LegacyEndpointEvent).endgame !== undefined; +} + +export function eventTimestamp( + event: EndpointEvent | LegacyEndpointEvent +): string | undefined | number { + if (isLegacyEvent(event)) { + return event.endgame.timestamp_utc; + } else { + return event['@timestamp']; + } +} + +export function eventName(event: EndpointEvent | LegacyEndpointEvent): string { + if (isLegacyEvent(event)) { + return event.endgame.process_name ? event.endgame.process_name : ''; + } else { + return event.process.name; + } +} diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index aa326c663965d..7e4cf3d700ec8 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -83,10 +83,10 @@ export interface AlertResultList { prev: string | null; } -export interface EndpointResultList { - /* the endpoints restricted by the page size */ - endpoints: EndpointMetadata[]; - /* the total number of unique endpoints in the index */ +export interface HostResultList { + /* the hosts restricted by the page size */ + hosts: HostMetadata[]; + /* the total number of unique hosts in the index */ total: number; /* the page size requested */ request_page_size: number; @@ -243,7 +243,7 @@ interface AlertMetadata { */ export type AlertData = AlertEvent & AlertMetadata; -export interface EndpointMetadata { +export type HostMetadata = Immutable<{ '@timestamp': number; event: { created: number; @@ -258,7 +258,7 @@ export interface EndpointMetadata { version: string; }; host: HostFields; -} +}>; /** * Represents `total` response from Elasticsearch after ES 7.0. @@ -311,8 +311,8 @@ export interface EndpointEvent { version: string; }; event: { - category: string; - type: string; + category: string | string[]; + type: string | string[]; id: string; kind: string; }; @@ -328,6 +328,7 @@ export interface EndpointEvent { name: string; parent?: { entity_id: string; + name?: string; }; }; } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx index f7d6551f9093b..1bafcbec93f5f 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx @@ -24,11 +24,11 @@ export const navTabs: NavTabs[] = [ href: '/', }, { - id: 'management', - name: i18n.translate('xpack.endpoint.headerNav.management', { - defaultMessage: 'Management', + id: 'hosts', + name: i18n.translate('xpack.endpoint.headerNav.hosts', { + defaultMessage: 'Hosts', }), - href: '/management', + href: '/hosts', }, { id: 'alerts', diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx index cec51f570f95d..997113754f95d 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -11,15 +11,17 @@ import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { Route, Switch, BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import { Store } from 'redux'; +import { useObservable } from 'react-use'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { RouteCapture } from './view/route_capture'; import { EndpointPluginStartDependencies } from '../../plugin'; import { appStoreFactory } from './store'; import { AlertIndex } from './view/alerts'; -import { ManagementList } from './view/managing'; +import { HostList } from './view/hosts'; import { PolicyList } from './view/policy'; import { PolicyDetails } from './view/policy'; import { HeaderNavigation } from './components/header_nav'; +import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components'; /** * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. @@ -48,43 +50,49 @@ interface RouterProps { } const AppRoot: React.FunctionComponent = React.memo( - ({ basename, store, coreStart: { http, notifications }, depsStart: { data } }) => ( - - - - - - - - ( -

- -

- )} - /> - - - - - ( - { + const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); + + return ( + + + + + + + + + ( +

+ +

+ )} + /> + + + + + ( + + )} /> - )} - /> -
-
-
-
-
-
- ) +
+
+
+ +
+
+
+ ); + } ); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts index 85215238dbefc..2dce8ead38584 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ManagementAction } from './managing'; +import { HostAction } from './hosts'; import { AlertAction } from './alerts'; import { RoutingAction } from './routing'; import { PolicyListAction } from './policy_list'; import { PolicyDetailsAction } from './policy_details'; export type AppAction = - | ManagementAction + | HostAction | AlertAction | RoutingAction | PolicyListAction diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts index 68731bb3f307f..5e9b08c09c2c7 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts @@ -165,15 +165,3 @@ export const hasSelectedAlert: (state: AlertListState) => boolean = createSelect uiQueryParams, ({ selected_alert: selectedAlert }) => selectedAlert !== undefined ); - -/** - * Determine if the alert event is most likely compatible with LegacyEndpointEvent. - */ -export const selectedAlertIsLegacyEndpointEvent: ( - state: AlertListState -) => boolean = createSelector(selectedAlertDetailsData, function(event) { - if (event === undefined) { - return false; - } - return 'endgame' in event; -}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts new file mode 100644 index 0000000000000..dee35aa3b895a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HostListPagination, ServerApiError } from '../../types'; +import { HostResultList, HostMetadata } from '../../../../../common/types'; + +interface ServerReturnedHostList { + type: 'serverReturnedHostList'; + payload: HostResultList; +} + +interface ServerReturnedHostDetails { + type: 'serverReturnedHostDetails'; + payload: HostMetadata; +} + +interface ServerFailedToReturnHostDetails { + type: 'serverFailedToReturnHostDetails'; + payload: ServerApiError; +} + +interface UserPaginatedHostList { + type: 'userPaginatedHostList'; + payload: HostListPagination; +} + +// Why is FakeActionWithNoPayload here, see: https://github.com/elastic/endpoint-app-team/issues/273 +interface FakeActionWithNoPayload { + type: 'fakeActionWithNoPayLoad'; +} + +export type HostAction = + | ServerReturnedHostList + | ServerReturnedHostDetails + | ServerFailedToReturnHostDetails + | UserPaginatedHostList + | FakeActionWithNoPayload; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts new file mode 100644 index 0000000000000..9aff66cdfb75e --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.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 { createStore, Dispatch, Store } from 'redux'; +import { HostAction, hostListReducer } from './index'; +import { HostListState } from '../../types'; +import { listData } from './selectors'; +import { mockHostResultList } from './mock_host_result_list'; + +describe('HostList store concerns', () => { + let store: Store; + let dispatch: Dispatch; + const createTestStore = () => { + store = createStore(hostListReducer); + dispatch = store.dispatch; + }; + + const loadDataToStore = () => { + dispatch({ + type: 'serverReturnedHostList', + payload: mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 }), + }); + }; + + describe('# Reducers', () => { + beforeEach(() => { + createTestStore(); + }); + + test('it creates default state', () => { + expect(store.getState()).toEqual({ + hosts: [], + pageSize: 10, + pageIndex: 0, + total: 0, + loading: false, + }); + }); + + test('it handles `serverReturnedHostList', () => { + const payload = mockHostResultList({ + request_page_size: 1, + request_page_index: 1, + total: 10, + }); + dispatch({ + type: 'serverReturnedHostList', + payload, + }); + + const currentState = store.getState(); + expect(currentState.hosts).toEqual(payload.hosts); + expect(currentState.pageSize).toEqual(payload.request_page_size); + expect(currentState.pageIndex).toEqual(payload.request_page_index); + expect(currentState.total).toEqual(payload.total); + }); + }); + + describe('# Selectors', () => { + beforeEach(() => { + createTestStore(); + loadDataToStore(); + }); + + test('it selects `hostListData`', () => { + const currentState = store.getState(); + expect(listData(currentState)).toEqual(currentState.hosts); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts similarity index 60% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts index f0bfe27c9e30f..e80d7a82dc8cb 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { managementListReducer } from './reducer'; -export { ManagementAction } from './action'; -export { managementMiddlewareFactory } from './middleware'; +export { hostListReducer } from './reducer'; +export { HostAction } from './action'; +export { hostMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts similarity index 63% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts index 459a1789a58da..a1973a38b6534 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts @@ -7,51 +7,40 @@ import { CoreStart, HttpSetup } from 'kibana/public'; import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { History, createBrowserHistory } from 'history'; -import { managementListReducer, managementMiddlewareFactory } from './index'; -import { EndpointMetadata, EndpointResultList } from '../../../../../common/types'; -import { EndpointDocGenerator } from '../../../../../common/generate_data'; -import { ManagementListState } from '../../types'; +import { hostListReducer, hostMiddlewareFactory } from './index'; +import { HostResultList } from '../../../../../common/types'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; import { listData } from './selectors'; import { DepsStartMock, depsStartMock } from '../../mocks'; +import { mockHostResultList } from './mock_host_result_list'; -describe('endpoint list saga', () => { +describe('host list middleware', () => { const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms)); let fakeCoreStart: jest.Mocked; let depsStart: DepsStartMock; let fakeHttpServices: jest.Mocked; - let store: Store; + let store: Store; let getState: typeof store['getState']; let dispatch: Dispatch; - const generator = new EndpointDocGenerator(); - // https://github.com/elastic/endpoint-app-team/issues/131 - const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(); - }; - let history: History; - const getEndpointListApiResponse = (): EndpointResultList => { - return { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }; + const getEndpointListApiResponse = (): HostResultList => { + return mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 }); }; beforeEach(() => { fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); depsStart = depsStartMock(); fakeHttpServices = fakeCoreStart.http as jest.Mocked; store = createStore( - managementListReducer, - applyMiddleware(managementMiddlewareFactory(fakeCoreStart, depsStart)) + hostListReducer, + applyMiddleware(hostMiddlewareFactory(fakeCoreStart, depsStart)) ); getState = store.getState; dispatch = store.dispatch; history = createBrowserHistory(); }); - test('it handles `userChangedUrl`', async () => { + test('handles `userChangedUrl`', async () => { const apiResponse = getEndpointListApiResponse(); fakeHttpServices.post.mockResolvedValue(apiResponse); expect(fakeHttpServices.post).not.toHaveBeenCalled(); @@ -60,7 +49,7 @@ describe('endpoint list saga', () => { type: 'userChangedUrl', payload: { ...history.location, - pathname: '/management', + pathname: '/hosts', }, }); await sleep(); @@ -69,6 +58,6 @@ describe('endpoint list saga', () => { paging_properties: [{ page_index: 0 }, { page_size: 10 }], }), }); - expect(listData(getState())).toEqual(apiResponse.endpoints); + expect(listData(getState())).toEqual(apiResponse.hosts); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts similarity index 59% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts index 1131e8d769fcf..9481b6633f12e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts @@ -5,39 +5,30 @@ */ import { MiddlewareFactory } from '../../types'; -import { - pageIndex, - pageSize, - isOnManagementPage, - hasSelectedHost, - uiQueryParams, -} from './selectors'; -import { ManagementListState } from '../../types'; +import { pageIndex, pageSize, isOnHostPage, hasSelectedHost, uiQueryParams } from './selectors'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; -export const managementMiddlewareFactory: MiddlewareFactory = coreStart => { +export const hostMiddlewareFactory: MiddlewareFactory = coreStart => { return ({ getState, dispatch }) => next => async (action: AppAction) => { next(action); const state = getState(); if ( (action.type === 'userChangedUrl' && - isOnManagementPage(state) && + isOnHostPage(state) && hasSelectedHost(state) !== true) || - action.type === 'userPaginatedManagementList' + action.type === 'userPaginatedHostList' ) { - const managementPageIndex = pageIndex(state); - const managementPageSize = pageSize(state); + const hostPageIndex = pageIndex(state); + const hostPageSize = pageSize(state); const response = await coreStart.http.post('/api/endpoint/metadata', { body: JSON.stringify({ - paging_properties: [ - { page_index: managementPageIndex }, - { page_size: managementPageSize }, - ], + paging_properties: [{ page_index: hostPageIndex }, { page_size: hostPageSize }], }), }); - response.request_page_index = managementPageIndex; + response.request_page_index = hostPageIndex; dispatch({ - type: 'serverReturnedManagementList', + type: 'serverReturnedHostList', payload: response, }); } @@ -46,12 +37,12 @@ export const managementMiddlewareFactory: MiddlewareFactory try { const response = await coreStart.http.get(`/api/endpoint/metadata/${selectedHost}`); dispatch({ - type: 'serverReturnedManagementDetails', + type: 'serverReturnedHostDetails', payload: response, }); } catch (error) { dispatch({ - type: 'serverFailedToReturnManagementDetails', + type: 'serverFailedToReturnHostDetails', payload: error, }); } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts similarity index 82% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts index 61833d1dfb957..db39ecf448312 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EndpointResultList } from '../../../../../common/types'; +import { HostResultList } from '../../../../../common/types'; import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockHostResultList: (options?: { total?: number; request_page_size?: number; request_page_index?: number; -}) => EndpointResultList = (options = {}) => { +}) => HostResultList = (options = {}) => { const { total = 1, request_page_size: requestPageSize = 10, @@ -24,13 +24,13 @@ export const mockHostResultList: (options?: { // total - numberToSkip is the count of non-skipped ones, but return no more than a pageSize, and no less than 0 const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0); - const endpoints = []; + const hosts = []; for (let index = 0; index < actualCountToReturn; index++) { const generator = new EndpointDocGenerator('seed'); - endpoints.push(generator.generateEndpointMetadata()); + hosts.push(generator.generateHostMetadata()); } - const mock: EndpointResultList = { - endpoints, + const mock: HostResultList = { + hosts, total, request_page_size: requestPageSize, request_page_index: requestPageIndex, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts similarity index 66% rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts index 582aa6b7138c9..fd70317a9f37e 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts @@ -5,12 +5,12 @@ */ import { Reducer } from 'redux'; -import { ManagementListState } from '../../types'; +import { HostListState } from '../../types'; import { AppAction } from '../action'; -const initialState = (): ManagementListState => { +const initialState = (): HostListState => { return { - endpoints: [], + hosts: [], pageSize: 10, pageIndex: 0, total: 0, @@ -21,38 +21,36 @@ const initialState = (): ManagementListState => { }; }; -export const managementListReducer: Reducer = ( +export const hostListReducer: Reducer = ( state = initialState(), action ) => { - if (action.type === 'serverReturnedManagementList') { + if (action.type === 'serverReturnedHostList') { const { - endpoints, + hosts, total, request_page_size: pageSize, request_page_index: pageIndex, } = action.payload; return { ...state, - endpoints, + hosts, total, pageSize, pageIndex, loading: false, }; - } else if (action.type === 'serverReturnedManagementDetails') { + } else if (action.type === 'serverReturnedHostDetails') { return { ...state, details: action.payload, }; - } else if (action.type === 'serverFailedToReturnManagementDetails') { + } else if (action.type === 'serverFailedToReturnHostDetails') { return { ...state, detailsError: action.payload, }; - } else if (action.type === 'userExitedManagementList') { - return initialState(); - } else if (action.type === 'userPaginatedManagementList') { + } else if (action.type === 'userPaginatedHostList') { return { ...state, ...action.payload, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts new file mode 100644 index 0000000000000..ebe310cb51190 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import querystring from 'querystring'; +import { createSelector } from 'reselect'; +import { Immutable } from '../../../../../common/types'; +import { HostListState, HostIndexUIQueryParams } from '../../types'; + +export const listData = (state: HostListState) => state.hosts; + +export const pageIndex = (state: HostListState) => state.pageIndex; + +export const pageSize = (state: HostListState) => state.pageSize; + +export const totalHits = (state: HostListState) => state.total; + +export const isLoading = (state: HostListState) => state.loading; + +export const detailsError = (state: HostListState) => state.detailsError; + +export const detailsData = (state: HostListState) => { + return state.details; +}; + +export const isOnHostPage = (state: HostListState) => + state.location ? state.location.pathname === '/hosts' : false; + +export const uiQueryParams: ( + state: HostListState +) => Immutable = createSelector( + (state: HostListState) => state.location, + (location: HostListState['location']) => { + const data: HostIndexUIQueryParams = {}; + if (location) { + // Removes the `?` from the beginning of query string if it exists + const query = querystring.parse(location.search.slice(1)); + + const keys: Array = ['selected_host']; + + for (const key of keys) { + const value = query[key]; + if (typeof value === 'string') { + data[key] = value; + } else if (Array.isArray(value)) { + data[key] = value[value.length - 1]; + } + } + } + return data; + } +); + +export const hasSelectedHost: (state: HostListState) => boolean = createSelector( + uiQueryParams, + ({ selected_host: selectedHost }) => { + return selectedHost !== undefined; + } +); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts index c051be2bb83cb..efa79b163d3b6 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts @@ -16,7 +16,7 @@ import { import { CoreStart } from 'kibana/public'; import { appReducer } from './reducer'; import { alertMiddlewareFactory } from './alerts/middleware'; -import { managementMiddlewareFactory } from './managing'; +import { hostMiddlewareFactory } from './hosts'; import { policyListMiddlewareFactory } from './policy_list'; import { policyDetailsMiddlewareFactory } from './policy_details'; import { GlobalState } from '../types'; @@ -69,8 +69,8 @@ export const appStoreFactory: (middlewareDeps?: { middleware = composeWithReduxDevTools( applyMiddleware( substateMiddlewareFactory( - globalState => globalState.managementList, - managementMiddlewareFactory(coreStart, depsStart) + globalState => globalState.hostList, + hostMiddlewareFactory(coreStart, depsStart) ), substateMiddlewareFactory( globalState => globalState.policyList, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts deleted file mode 100644 index a42e23e57d107..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts +++ /dev/null @@ -1,39 +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 { ManagementListPagination, ServerApiError } from '../../types'; -import { EndpointResultList, EndpointMetadata } from '../../../../../common/types'; - -interface ServerReturnedManagementList { - type: 'serverReturnedManagementList'; - payload: EndpointResultList; -} - -interface ServerReturnedManagementDetails { - type: 'serverReturnedManagementDetails'; - payload: EndpointMetadata; -} - -interface ServerFailedToReturnManagementDetails { - type: 'serverFailedToReturnManagementDetails'; - payload: ServerApiError; -} - -interface UserExitedManagementList { - type: 'userExitedManagementList'; -} - -interface UserPaginatedManagementList { - type: 'userPaginatedManagementList'; - payload: ManagementListPagination; -} - -export type ManagementAction = - | ServerReturnedManagementList - | ServerReturnedManagementDetails - | ServerFailedToReturnManagementDetails - | UserExitedManagementList - | UserPaginatedManagementList; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts deleted file mode 100644 index e435fded13f4c..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts +++ /dev/null @@ -1,93 +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 { createStore, Dispatch, Store } from 'redux'; -import { ManagementAction, managementListReducer } from './index'; -import { EndpointMetadata } from '../../../../../common/types'; -import { EndpointDocGenerator } from '../../../../../common/generate_data'; -import { ManagementListState } from '../../types'; -import { listData } from './selectors'; - -describe('endpoint_list store concerns', () => { - let store: Store; - let dispatch: Dispatch; - const generator = new EndpointDocGenerator(); - const createTestStore = () => { - store = createStore(managementListReducer); - dispatch = store.dispatch; - }; - const generateEndpoint = (): EndpointMetadata => { - return generator.generateEndpointMetadata(); - }; - const loadDataToStore = () => { - dispatch({ - type: 'serverReturnedManagementList', - payload: { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }, - }); - }; - - describe('# Reducers', () => { - beforeEach(() => { - createTestStore(); - }); - - test('it creates default state', () => { - expect(store.getState()).toEqual({ - endpoints: [], - pageSize: 10, - pageIndex: 0, - total: 0, - loading: false, - }); - }); - - test('it handles `serverReturnedManagementList', () => { - const payload = { - endpoints: [generateEndpoint()], - request_page_size: 1, - request_page_index: 1, - total: 10, - }; - dispatch({ - type: 'serverReturnedManagementList', - payload, - }); - - const currentState = store.getState(); - expect(currentState.endpoints).toEqual(payload.endpoints); - expect(currentState.pageSize).toEqual(payload.request_page_size); - expect(currentState.pageIndex).toEqual(payload.request_page_index); - expect(currentState.total).toEqual(payload.total); - }); - - test('it handles `userExitedManagementListPage`', () => { - loadDataToStore(); - - expect(store.getState().total).toEqual(10); - - dispatch({ type: 'userExitedManagementList' }); - expect(store.getState().endpoints.length).toEqual(0); - expect(store.getState().pageIndex).toEqual(0); - }); - }); - - describe('# Selectors', () => { - beforeEach(() => { - createTestStore(); - loadDataToStore(); - }); - - test('it selects `managementListData`', () => { - const currentState = store.getState(); - expect(listData(currentState)).toEqual(currentState.endpoints); - }); - }); -}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts deleted file mode 100644 index a7776f09fe2b8..0000000000000 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import querystring from 'querystring'; -import { createSelector } from 'reselect'; -import { Immutable } from '../../../../../common/types'; -import { ManagementListState, ManagingIndexUIQueryParams } from '../../types'; - -export const listData = (state: ManagementListState) => state.endpoints; - -export const pageIndex = (state: ManagementListState) => state.pageIndex; - -export const pageSize = (state: ManagementListState) => state.pageSize; - -export const totalHits = (state: ManagementListState) => state.total; - -export const isLoading = (state: ManagementListState) => state.loading; - -export const detailsError = (state: ManagementListState) => state.detailsError; - -export const detailsData = (state: ManagementListState) => { - return state.details; -}; - -export const isOnManagementPage = (state: ManagementListState) => - state.location ? state.location.pathname === '/management' : false; - -export const uiQueryParams: ( - state: ManagementListState -) => Immutable = createSelector( - (state: ManagementListState) => state.location, - (location: ManagementListState['location']) => { - const data: ManagingIndexUIQueryParams = {}; - if (location) { - // Removes the `?` from the beginning of query string if it exists - const query = querystring.parse(location.search.slice(1)); - - const keys: Array = ['selected_host']; - - for (const key of keys) { - const value = query[key]; - if (typeof value === 'string') { - data[key] = value; - } else if (Array.isArray(value)) { - data[key] = value[value.length - 1]; - } - } - } - return data; - } -); - -export const hasSelectedHost: (state: ManagementListState) => boolean = createSelector( - uiQueryParams, - ({ selected_host: selectedHost }) => { - return selectedHost !== undefined; - } -); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts index e655a8d5e46db..c8b2d08676724 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { combineReducers, Reducer } from 'redux'; -import { managementListReducer } from './managing'; +import { hostListReducer } from './hosts'; import { AppAction } from './action'; import { alertListReducer } from './alerts'; import { GlobalState } from '../types'; @@ -12,7 +12,7 @@ import { policyListReducer } from './policy_list'; import { policyDetailsReducer } from './policy_details'; export const appReducer: Reducer = combineReducers({ - managementList: managementListReducer, + hostList: hostListReducer, alertList: alertListReducer, policyList: policyListReducer, policyDetails: policyDetailsReducer, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 91be6e4936dbe..3045f42a93fe2 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -7,7 +7,7 @@ import { Dispatch, MiddlewareAPI } from 'redux'; import { IIndexPattern } from 'src/plugins/data/public'; import { - EndpointMetadata, + HostMetadata, AlertData, AlertResultList, Immutable, @@ -25,22 +25,22 @@ export type MiddlewareFactory = ( api: MiddlewareAPI, S> ) => (next: Dispatch) => (action: AppAction) => unknown; -export interface ManagementListState { - endpoints: EndpointMetadata[]; - total: number; +export interface HostListState { + hosts: HostMetadata[]; pageSize: number; pageIndex: number; + total: number; loading: boolean; detailsError?: ServerApiError; - details?: Immutable; + details?: Immutable; location?: Immutable; } -export interface ManagementListPagination { +export interface HostListPagination { pageIndex: number; pageSize: number; } -export interface ManagingIndexUIQueryParams { +export interface HostIndexUIQueryParams { selected_host?: string; } @@ -92,7 +92,7 @@ export interface PolicyDetailsState { } export interface GlobalState { - readonly managementList: ManagementListState; + readonly hostList: HostListState; readonly alertList: AlertListState; readonly policyList: PolicyListState; readonly policyDetails: PolicyDetailsState; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx index 82a4bc00a4396..0ec5a855c8615 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { memo, useMemo } from 'react'; +import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -19,87 +20,104 @@ import * as selectors from '../../../../store/alerts/selectors'; import { MetadataPanel } from './metadata_panel'; import { FormattedDate } from '../../formatted_date'; import { AlertDetailResolver } from '../../resolver'; +import { ResolverEvent } from '../../../../../../../common/types'; import { TakeActionDropdown } from './take_action_dropdown'; -export const AlertDetailsOverview = memo(() => { - const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); - if (alertDetailsData === undefined) { - return null; - } - const selectedAlertIsLegacyEndpointEvent = useAlertListSelector( - selectors.selectedAlertIsLegacyEndpointEvent - ); +export const AlertDetailsOverview = styled( + memo(() => { + const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData); + if (alertDetailsData === undefined) { + return null; + } - const tabs: EuiTabbedContentTab[] = useMemo(() => { - return [ - { - id: 'overviewMetadata', - 'data-test-subj': 'overviewMetadata', - name: i18n.translate( - 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', - { - defaultMessage: 'Overview', - } - ), - content: ( - <> - - - - ), - }, - { - id: 'overviewResolver', - name: i18n.translate( - 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver', - { - defaultMessage: 'Resolver', - } - ), - content: ( - <> - - {selectedAlertIsLegacyEndpointEvent && } - - ), - }, - ]; - }, [selectedAlertIsLegacyEndpointEvent]); + const tabs: EuiTabbedContentTab[] = useMemo(() => { + return [ + { + id: 'overviewMetadata', + 'data-test-subj': 'overviewMetadata', + name: i18n.translate( + 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview', + { + defaultMessage: 'Overview', + } + ), + content: ( + <> + + + + ), + }, + { + id: 'overviewResolver', + 'data-test-subj': 'overviewResolverTab', + name: i18n.translate( + 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver', + { + defaultMessage: 'Resolver', + } + ), + content: ( + <> + + + + ), + }, + ]; + }, [alertDetailsData]); - return ( - <> -
- -

+ return ( + <> +
+ +

+ +

+
+ + +

+ , + }} + /> +

+
+ + + Endpoint Status:{' '} + + {' '} + + + + + {' '} -

-
- - -

- , - }} - /> -

-
- - - Endpoint Status: Online - - Alert Status: Open - - - -
- - - ); -}); + + + + + + + + ); + }) +)` + height: 100%; + width: 100%; +`; 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 52ef480bbb930..d18bc59a35f52 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 @@ -10,12 +10,12 @@ import { Provider } from 'react-redux'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { Resolver } from '../../../../embeddables/resolver/view'; import { EndpointPluginServices } from '../../../../plugin'; -import { LegacyEndpointEvent } from '../../../../../common/types'; +import { ResolverEvent } from '../../../../../common/types'; import { storeFactory } from '../../../../embeddables/resolver/store'; export const AlertDetailResolver = styled( React.memo( - ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => { + ({ className, selectedEvent }: { className?: string; selectedEvent?: ResolverEvent }) => { const context = useKibana(); const { store } = storeFactory(context); @@ -33,4 +33,5 @@ export const AlertDetailResolver = styled( width: 100%; display: flex; flex-grow: 1; + min-height: 500px; `; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx new file mode 100644 index 0000000000000..dcf97b4b2b226 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx @@ -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 React from 'react'; +import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n/react'; + +export const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { + // If date is greater than or equal to 1h (ago), then show it as a date + // else, show it as relative to "now" + return Date.now() - date.getTime() >= 3.6e6 ? ( + <> + + {' @'} + + + ) : ( + <> + + + ); +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx similarity index 62% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx index 9f2a732042719..37080e8568350 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx @@ -13,38 +13,46 @@ import { EuiDescriptionList, EuiLoadingContent, EuiHorizontalRule, + EuiHealth, EuiSpacer, + EuiListGroup, + EuiListGroupItem, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { useManagementListSelector } from './hooks'; +import { HostMetadata } from '../../../../../common/types'; +import { useHostListSelector } from './hooks'; import { urlFromQueryParams } from './url_from_query_params'; -import { uiQueryParams, detailsData, detailsError } from './../../store/managing/selectors'; +import { FormattedDateAndTime } from '../formatted_date_time'; +import { uiQueryParams, detailsData, detailsError } from './../../store/hosts/selectors'; -const HostDetails = memo(() => { - const details = useManagementListSelector(detailsData); - if (details === undefined) { - return null; +const HostIds = styled(EuiListGroupItem)` + margin-top: 0; + .euiListGroupItem__text { + padding: 0; } +`; +const HostDetails = memo(({ details }: { details: HostMetadata }) => { const detailsResultsUpper = useMemo(() => { return [ { - title: i18n.translate('xpack.endpoint.management.details.os', { + title: i18n.translate('xpack.endpoint.host.details.os', { defaultMessage: 'OS', }), description: details.host.os.full, }, { - title: i18n.translate('xpack.endpoint.management.details.lastSeen', { + title: i18n.translate('xpack.endpoint.host.details.lastSeen', { defaultMessage: 'Last Seen', }), - description: details['@timestamp'], + description: , }, { - title: i18n.translate('xpack.endpoint.management.details.alerts', { + title: i18n.translate('xpack.endpoint.host.details.alerts', { defaultMessage: 'Alerts', }), description: '0', @@ -55,62 +63,67 @@ const HostDetails = memo(() => { const detailsResultsLower = useMemo(() => { return [ { - title: i18n.translate('xpack.endpoint.management.details.policy', { + title: i18n.translate('xpack.endpoint.host.details.policy', { defaultMessage: 'Policy', }), description: details.endpoint.policy.id, }, { - title: i18n.translate('xpack.endpoint.management.details.policyStatus', { + title: i18n.translate('xpack.endpoint.host.details.policyStatus', { defaultMessage: 'Policy Status', }), - description: 'active', + description: active, }, { - title: i18n.translate('xpack.endpoint.management.details.ipAddress', { + title: i18n.translate('xpack.endpoint.host.details.ipAddress', { defaultMessage: 'IP Address', }), - description: details.host.ip, + description: ( + + {details.host.ip.map((ip: string, index: number) => ( + + ))} + + ), }, { - title: i18n.translate('xpack.endpoint.management.details.hostname', { + title: i18n.translate('xpack.endpoint.host.details.hostname', { defaultMessage: 'Hostname', }), description: details.host.hostname, }, { - title: i18n.translate('xpack.endpoint.management.details.sensorVersion', { + title: i18n.translate('xpack.endpoint.host.details.sensorVersion', { defaultMessage: 'Sensor Version', }), description: details.agent.version, }, ]; }, [details.agent.version, details.endpoint.policy.id, details.host.hostname, details.host.ip]); - return ( <> ); }); -export const ManagementDetails = () => { +export const HostDetailsFlyout = () => { const history = useHistory(); const { notifications } = useKibana(); - const queryParams = useManagementListSelector(uiQueryParams); + const queryParams = useHostListSelector(uiQueryParams); const { selected_host: selectedHost, ...queryParamsWithoutSelectedHost } = queryParams; - const details = useManagementListSelector(detailsData); - const error = useManagementListSelector(detailsError); + const details = useHostListSelector(detailsData); + const error = useHostListSelector(detailsError); const handleFlyoutClose = useCallback(() => { history.push(urlFromQueryParams(queryParamsWithoutSelectedHost)); @@ -121,13 +134,13 @@ export const ManagementDetails = () => { notifications.toasts.danger({ title: ( ), body: ( ), @@ -137,10 +150,10 @@ export const ManagementDetails = () => { }, [error, notifications.toasts]); return ( - + -

+

{details === undefined ? : details.host.hostname}

@@ -151,7 +164,7 @@ export const ManagementDetails = () => { ) : ( - + )}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts similarity index 61% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts index a0720fbd8aeeb..99a0073f46c74 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts @@ -5,12 +5,10 @@ */ import { useSelector } from 'react-redux'; -import { GlobalState, ManagementListState } from '../../types'; +import { GlobalState, HostListState } from '../../types'; -export function useManagementListSelector( - selector: (state: ManagementListState) => TSelected -) { +export function useHostListSelector(selector: (state: HostListState) => TSelected) { return useSelector(function(state: GlobalState) { - return selector(state.managementList); + return selector(state.hostList); }); } diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx similarity index 75% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx index ced27ae8945b5..f6dfae99c1b11 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx @@ -8,15 +8,16 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; import { Provider } from 'react-redux'; import { I18nProvider } from '@kbn/i18n/react'; +import { EuiThemeProvider } from '../../../../../../../legacy/common/eui_styled_components'; import { appStoreFactory } from '../../store'; import { RouteCapture } from '../route_capture'; import { createMemoryHistory, MemoryHistory } from 'history'; import { Router } from 'react-router-dom'; import { AppAction } from '../../types'; -import { ManagementList } from './index'; -import { mockHostResultList } from '../../store/managing/mock_host_result_list'; +import { HostList } from './index'; +import { mockHostResultList } from '../../store/hosts/mock_host_result_list'; -describe('when on the managing page', () => { +describe('when on the hosts page', () => { let render: () => reactTestingLibrary.RenderResult; let history: MemoryHistory; let store: ReturnType; @@ -28,11 +29,13 @@ describe('when on the managing page', () => { return reactTestingLibrary.render( - - - - - + + + + + + + ); @@ -41,7 +44,7 @@ describe('when on the managing page', () => { it('should show a table', async () => { const renderResult = render(); - const table = await renderResult.findByTestId('managementListTable'); + const table = await renderResult.findByTestId('hostListTable'); expect(table).not.toBeNull(); }); @@ -49,7 +52,7 @@ describe('when on the managing page', () => { it('should not show the flyout', () => { const renderResult = render(); expect.assertions(1); - return renderResult.findByTestId('managementDetailsFlyout').catch(e => { + return renderResult.findByTestId('hostDetailsFlyout').catch(e => { expect(e).not.toBeNull(); }); }); @@ -57,14 +60,14 @@ describe('when on the managing page', () => { beforeEach(() => { reactTestingLibrary.act(() => { const action: AppAction = { - type: 'serverReturnedManagementList', + type: 'serverReturnedHostList', payload: mockHostResultList(), }; store.dispatch(action); }); }); - it('should render the management summary row in the table', async () => { + it('should render the host summary row in the table', async () => { const renderResult = render(); const rows = await renderResult.findAllByRole('row'); expect(rows).toHaveLength(2); @@ -81,7 +84,7 @@ describe('when on the managing page', () => { }); it('should show the flyout', () => { - return renderResult.findByTestId('managementDetailsFlyout').then(flyout => { + return renderResult.findByTestId('hostDetailsFlyout').then(flyout => { expect(flyout).not.toBeNull(); }); }); @@ -100,7 +103,7 @@ describe('when on the managing page', () => { }); it('should show the flyout', () => { const renderResult = render(); - return renderResult.findByTestId('managementDetailsFlyout').then(flyout => { + return renderResult.findByTestId('hostDetailsFlyout').then(flyout => { expect(flyout).not.toBeNull(); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx similarity index 55% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx index ba9a931a233b2..94625b8c66191 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx @@ -10,28 +10,29 @@ import { useHistory } from 'react-router-dom'; import { EuiPage, EuiPageBody, + EuiPageHeader, EuiPageContent, - EuiPageContentBody, - EuiPageContentHeader, - EuiPageContentHeaderSection, + EuiHorizontalRule, EuiTitle, EuiBasicTable, - EuiTextColor, + EuiText, EuiLink, + EuiHealth, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { createStructuredSelector } from 'reselect'; -import { ManagementDetails } from './details'; -import * as selectors from '../../store/managing/selectors'; -import { ManagementAction } from '../../store/managing/action'; -import { useManagementListSelector } from './hooks'; +import { HostDetailsFlyout } from './details'; +import * as selectors from '../../store/hosts/selectors'; +import { HostAction } from '../../store/hosts/action'; +import { useHostListSelector } from './hooks'; import { CreateStructuredSelector } from '../../types'; import { urlFromQueryParams } from './url_from_query_params'; const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); -export const ManagementList = () => { - const dispatch = useDispatch<(a: ManagementAction) => void>(); +export const HostList = () => { + const dispatch = useDispatch<(a: HostAction) => void>(); const history = useHistory(); const { listData, @@ -41,7 +42,7 @@ export const ManagementList = () => { isLoading, uiQueryParams: queryParams, hasSelectedHost, - } = useManagementListSelector(selector); + } = useHostListSelector(selector); const paginationSetup = useMemo(() => { return { @@ -57,7 +58,7 @@ export const ManagementList = () => { ({ page }: { page: { index: number; size: number } }) => { const { index, size } = page; dispatch({ - type: 'userPaginatedManagementList', + type: 'userPaginatedHostList', payload: { pageIndex: index, pageSize: size }, }); }, @@ -68,7 +69,7 @@ export const ManagementList = () => { return [ { field: '', - name: i18n.translate('xpack.endpoint.management.list.host', { + name: i18n.translate('xpack.endpoint.host.list.hostname', { defaultMessage: 'Hostname', }), render: ({ host: { hostname, id } }: { host: { hostname: string; id: string } }) => { @@ -89,7 +90,7 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.policy', { + name: i18n.translate('xpack.endpoint.host.list.policy', { defaultMessage: 'Policy', }), render: () => { @@ -98,37 +99,38 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.policyStatus', { + name: i18n.translate('xpack.endpoint.host.list.policyStatus', { defaultMessage: 'Policy Status', }), render: () => { - return 'Policy Status'; + return Policy Status; }, }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.alerts', { + name: i18n.translate('xpack.endpoint.host.list.alerts', { defaultMessage: 'Alerts', }), + dataType: 'number', render: () => { return '0'; }, }, { field: 'host.os.name', - name: i18n.translate('xpack.endpoint.management.list.os', { + name: i18n.translate('xpack.endpoint.host.list.os', { defaultMessage: 'Operating System', }), }, { field: 'host.ip', - name: i18n.translate('xpack.endpoint.management.list.ip', { + name: i18n.translate('xpack.endpoint.host.list.ip', { defaultMessage: 'IP Address', }), }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.sensorVersion', { + name: i18n.translate('xpack.endpoint.host.list.sensorVersion', { defaultMessage: 'Sensor Version', }), render: () => { @@ -137,9 +139,10 @@ export const ManagementList = () => { }, { field: '', - name: i18n.translate('xpack.endpoint.management.list.lastActive', { + name: i18n.translate('xpack.endpoint.host.list.lastActive', { defaultMessage: 'Last Active', }), + dataType: 'date', render: () => { return 'xxxx'; }, @@ -148,45 +151,59 @@ export const ManagementList = () => { }, [queryParams, history]); return ( - <> - {hasSelectedHost && } - + + {hasSelectedHost && } + - - - - -

- -

-
-

- - - -

-
-
- - + +

+ +

+
+ + + + + -
+ + +
- +
); }; + +const HostPage = styled.div` + .hostPage { + padding: 0; + } + .hostHeader { + background-color: ${props => props.theme.eui.euiColorLightestShade}; + border-bottom: ${props => props.theme.eui.euiBorderThin}; + padding: ${props => + props.theme.eui.euiSizeXL + + ' ' + + 0 + + props.theme.eui.euiSizeXL + + ' ' + + props.theme.eui.euiSizeL}; + margin-bottom: 0; + } + .hostPageContent { + border: none; + } +`; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts similarity index 78% rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts index ea6a4c6f684ad..225aad8cab020 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts @@ -5,10 +5,10 @@ */ import querystring from 'querystring'; -import { EndpointAppLocation, ManagingIndexUIQueryParams } from '../../types'; +import { EndpointAppLocation, HostIndexUIQueryParams } from '../../types'; export function urlFromQueryParams( - queryParams: ManagingIndexUIQueryParams + queryParams: HostIndexUIQueryParams ): Partial { const search = querystring.stringify(queryParams); return { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx index cf573da3703cc..e7ce53679bbe7 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx @@ -20,17 +20,12 @@ import { EuiLink, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { - FormattedMessage, - FormattedDate, - FormattedTime, - FormattedNumber, - FormattedRelative, -} from '@kbn/i18n/react'; +import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { useHistory } from 'react-router-dom'; import { usePageId } from '../use_page_id'; +import { FormattedDateAndTime } from '../formatted_date_time'; import { selectIsLoading, selectPageIndex, @@ -56,22 +51,6 @@ const TruncateTooltipText = styled(TruncateText)` } `; -const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { - // If date is greater than or equal to 24h (ago), then show it as a date - // else, show it as relative to "now" - return Date.now() - date.getTime() >= 8.64e7 ? ( - <> - - {' @'} - - - ) : ( - <> - - - ); -}; - const PolicyLink: React.FC<{ name: string; route: string }> = ({ name, route }) => { const history = useHistory(); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts index 6892bf11ecff2..c9a03f0a47968 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts @@ -6,15 +6,15 @@ import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event'; import { IndexedProcessTree } from '../types'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers'; /** * Create a new IndexedProcessTree from an array of ProcessEvents */ -export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree { - const idToChildren = new Map(); - const idToValue = new Map(); +export function factory(processes: ResolverEvent[]): IndexedProcessTree { + const idToChildren = new Map(); + const idToValue = new Map(); for (const process of processes) { idToValue.set(uniquePidForProcess(process), process); @@ -36,10 +36,7 @@ export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree { /** * Returns an array with any children `ProcessEvent`s of the passed in `process` */ -export function children( - tree: IndexedProcessTree, - process: LegacyEndpointEvent -): LegacyEndpointEvent[] { +export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] { const id = uniquePidForProcess(process); const processChildren = tree.idToChildren.get(id); return processChildren === undefined ? [] : processChildren; @@ -50,8 +47,8 @@ export function children( */ export function parent( tree: IndexedProcessTree, - childProcess: LegacyEndpointEvent -): LegacyEndpointEvent | undefined { + childProcess: ResolverEvent +): ResolverEvent | undefined { const uniqueParentPid = uniqueParentPidForProcess(childProcess); if (uniqueParentPid === undefined) { return undefined; @@ -74,7 +71,7 @@ export function root(tree: IndexedProcessTree) { if (size(tree) === 0) { return null; } - let current: LegacyEndpointEvent = tree.idToProcess.values().next().value; + let current: ResolverEvent = tree.idToProcess.values().next().value; while (parent(tree, current) !== undefined) { current = parent(tree, current)!; } diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts index 876168d2ed96a..a709d6caf46cb 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts @@ -4,36 +4,65 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; +import * as event from '../../../../common/models/event'; +import { ResolverProcessType } from '../types'; /** * Returns true if the process's eventType is either 'processCreated' or 'processRan'. * Resolver will only render 'graphable' process events. */ -export function isGraphableProcess(passedEvent: LegacyEndpointEvent) { +export function isGraphableProcess(passedEvent: ResolverEvent) { return eventType(passedEvent) === 'processCreated' || eventType(passedEvent) === 'processRan'; } +function isValue(field: string | string[], value: string) { + if (field instanceof Array) { + return field.length === 1 && field[0] === value; + } else { + return field === value; + } +} + /** * Returns a custom event type for a process event based on the event's metadata. */ -export function eventType(passedEvent: LegacyEndpointEvent) { - const { - endgame: { event_type_full: type, event_subtype_full: subType }, - } = passedEvent; +export function eventType(passedEvent: ResolverEvent): ResolverProcessType { + if (event.isLegacyEvent(passedEvent)) { + const { + endgame: { event_type_full: type, event_subtype_full: subType }, + } = passedEvent; - if (type === 'process_event') { - if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') { - return 'processCreated'; - } else if (subType === 'already_running') { - return 'processRan'; - } else if (subType === 'termination_event') { - return 'processTerminated'; - } else { - return 'unknownProcessEvent'; + if (type === 'process_event') { + if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') { + return 'processCreated'; + } else if (subType === 'already_running') { + return 'processRan'; + } else if (subType === 'termination_event') { + return 'processTerminated'; + } else { + return 'unknownProcessEvent'; + } + } else if (type === 'alert_event') { + return 'processCausedAlert'; + } + } else { + const { + event: { type, category, kind }, + } = passedEvent; + if (isValue(category, 'process')) { + if (isValue(type, 'start') || isValue(type, 'change') || isValue(type, 'creation')) { + return 'processCreated'; + } else if (isValue(type, 'info')) { + return 'processRan'; + } else if (isValue(type, 'end')) { + return 'processTerminated'; + } else { + return 'unknownProcessEvent'; + } + } else if (kind === 'alert') { + return 'processCausedAlert'; } - } else if (type === 'alert_event') { - return 'processCausedAlert'; } return 'unknownEvent'; } @@ -41,13 +70,21 @@ export function eventType(passedEvent: LegacyEndpointEvent) { /** * Returns the process event's pid */ -export function uniquePidForProcess(event: LegacyEndpointEvent) { - return event.endgame.unique_pid; +export function uniquePidForProcess(passedEvent: ResolverEvent): string { + if (event.isLegacyEvent(passedEvent)) { + return String(passedEvent.endgame.unique_pid); + } else { + return passedEvent.process.entity_id; + } } /** * Returns the process event's parent pid */ -export function uniqueParentPidForProcess(event: LegacyEndpointEvent) { - return event.endgame.unique_ppid; +export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined { + if (event.isLegacyEvent(passedEvent)) { + return String(passedEvent.endgame.unique_ppid); + } else { + return passedEvent.process.parent?.entity_id; + } } diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts index ecba0ec404d44..fec2078cc60c9 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts @@ -5,7 +5,7 @@ */ import { CameraAction } from './camera'; import { DataAction } from './data'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; /** * When the user wants to bring a process node front-and-center on the map. @@ -16,7 +16,7 @@ interface UserBroughtProcessIntoView { /** * Used to identify the process node that should be brought into view. */ - readonly process: LegacyEndpointEvent; + readonly process: ResolverEvent; /** * The time (since epoch in milliseconds) when the action was dispatched. */ @@ -33,7 +33,7 @@ interface UserChangedSelectedEvent { /** * Optional because they could have unselected the event. */ - selectedEvent?: LegacyEndpointEvent; + readonly selectedEvent?: ResolverEvent; }; } 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 f34d7c08ce08c..373afa89921dc 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 @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyEndpointEvent } from '../../../../../common/types'; +import { ResolverEvent } from '../../../../../common/types'; interface ServerReturnedResolverData { readonly type: 'serverReturnedResolverData'; readonly payload: { readonly data: { readonly result: { - readonly search_results: readonly LegacyEndpointEvent[]; + readonly search_results: readonly ResolverEvent[]; }; }; }; 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 304abbb06880b..e8007f82e30c2 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 @@ -14,7 +14,7 @@ import { ProcessWithWidthMetadata, Matrix3, } from '../../types'; -import { LegacyEndpointEvent } from '../../../../../common/types'; +import { ResolverEvent } from '../../../../../common/types'; import { Vector2 } from '../../types'; import { add as vector2Add, applyMatrix3 } from '../../lib/vector2'; import { isGraphableProcess } from '../../models/process_event'; @@ -112,7 +112,7 @@ export const graphableProcesses = createSelector( * */ function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): ProcessWidths { - const widths = new Map(); + const widths = new Map(); if (size(indexedProcessTree) === 0) { return widths; @@ -313,13 +313,13 @@ function processPositions( indexedProcessTree: IndexedProcessTree, widths: ProcessWidths ): ProcessPositions { - const positions = new Map(); + const positions = new Map(); /** * This algorithm iterates the tree in level order. It keeps counters that are reset for each parent. * By keeping track of the last parent node, we can know when we are dealing with a new set of siblings and * reset the counters. */ - let lastProcessedParentNode: LegacyEndpointEvent | undefined; + let lastProcessedParentNode: ResolverEvent | undefined; /** * Nodes are positioned relative to their siblings. We walk this in level order, so we handle * children left -> right. @@ -424,7 +424,7 @@ export const processNodePositionsAndEdgeLineSegments = createSelector( * Transform the positions of nodes and edges so they seem like they are on an isometric grid. */ const transformedEdgeLineSegments: EdgeLineSegment[] = []; - const transformedPositions = new Map(); + const transformedPositions = new Map(); for (const [processEvent, position] of positions) { transformedPositions.set(processEvent, applyMatrix3(position, isometricTransformMatrix)); diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts index 9f06643626f50..f15307a662388 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/methods.ts @@ -7,7 +7,7 @@ import { animatePanning } from './camera/methods'; import { processNodePositionsAndEdgeLineSegments } from './selectors'; import { ResolverState } from '../types'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; const animationDuration = 1000; @@ -17,7 +17,7 @@ const animationDuration = 1000; export function animateProcessIntoView( state: ResolverState, startTime: number, - process: LegacyEndpointEvent + process: ResolverEvent ): ResolverState { const { processNodePositions } = processNodePositionsAndEdgeLineSegments(state); const position = processNodePositions.get(process); 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 900aece60618d..23e4a4fe7d7ed 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/middleware.ts @@ -8,6 +8,8 @@ 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 * as event from '../../../../common/models/event'; type MiddlewareFactory = ( context?: KibanaReactContextValue @@ -19,22 +21,54 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => { return api => next => async (action: ResolverAction) => { next(action); if (action.type === 'userChangedSelectedEvent') { - if (context?.services.http) { + /** + * concurrently fetches a process's details, its ancestors, and its related events. + */ + if (context?.services.http && action.payload.selectedEvent) { api.dispatch({ type: 'appRequestedResolverData' }); - const uniquePid = action.payload.selectedEvent?.endgame?.unique_pid; - const legacyEndpointID = action.payload.selectedEvent?.agent?.id; - const [{ 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 }, - }), - ]); - const response = [...lifecycle, ...children, ...relatedEvents]; + 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 ? children.map((child: any) => child.lifecycle) : []; + } 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); + } + } + } + [{ 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), + ]); + } + childEvents = children.length > 0 ? children.map((child: any) => child.lifecycle) : []; + 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/types.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts index 4c2a1ea5ac21f..4380d3ab98999 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/types.ts @@ -8,7 +8,7 @@ import { Store } from 'redux'; import { ResolverAction } from './store/actions'; export { ResolverAction } from './store/actions'; -import { LegacyEndpointEvent } from '../../../common/types'; +import { ResolverEvent } from '../../../common/types'; /** * Redux state for the Resolver feature. Properties on this interface are populated via multiple reducers using redux's `combineReducers`. @@ -115,7 +115,7 @@ export type CameraState = { * State for `data` reducer which handles receiving Resolver data from the backend. */ export interface DataState { - readonly results: readonly LegacyEndpointEvent[]; + readonly results: readonly ResolverEvent[]; isLoading: boolean; } @@ -184,21 +184,21 @@ export interface IndexedProcessTree { /** * Map of ID to a process's children */ - idToChildren: Map; + idToChildren: Map; /** * Map of ID to process */ - idToProcess: Map; + idToProcess: Map; } /** * A map of ProcessEvents (representing process nodes) to the 'width' of their subtrees as calculated by `widthsOfProcessSubtrees` */ -export type ProcessWidths = Map; +export type ProcessWidths = Map; /** * Map of ProcessEvents (representing process nodes) to their positions. Calculated by `processPositions` */ -export type ProcessPositions = Map; +export type ProcessPositions = Map; /** * An array of vectors2 forming an polyline. Used to connect process nodes in the graph. */ @@ -208,11 +208,11 @@ export type EdgeLineSegment = Vector2[]; * Used to provide precalculated info from `widthsOfProcessSubtrees`. These 'width' values are used in the layout of the graph. */ export type ProcessWithWidthMetadata = { - process: LegacyEndpointEvent; + process: ResolverEvent; width: number; } & ( | { - parent: LegacyEndpointEvent; + parent: ResolverEvent; parentWidth: number; isOnlyChild: boolean; firstChildWidth: number; @@ -275,4 +275,15 @@ export interface SideEffectSimulator { mock: jest.Mocked> & Pick; } +/** + * The internal types of process events used by resolver, mapped from v0 and v1 events. + */ +export type ResolverProcessType = + | 'processCreated' + | 'processRan' + | 'processTerminated' + | 'unknownProcessEvent' + | 'processCausedAlert' + | 'unknownEvent'; + export type ResolverStore = Store; 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 52a0872f269f5..eab22f993d0a8 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx @@ -15,7 +15,7 @@ import { GraphControls } from './graph_controls'; import { ProcessEventDot } from './process_event_dot'; import { useCamera } from './use_camera'; import { ResolverAction } from '../types'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; const StyledPanel = styled(Panel)` position: absolute; @@ -39,7 +39,7 @@ export const Resolver = styled( selectedEvent, }: { className?: string; - selectedEvent?: LegacyEndpointEvent; + selectedEvent?: ResolverEvent; }) { const { processNodePositions, edgeLineSegments } = useSelector( selectors.processNodePositionsAndEdgeLineSegments diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx index 84c299698bb32..1250c1106b355 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/panel.tsx @@ -11,7 +11,8 @@ import euiVars from '@elastic/eui/dist/eui_theme_light.json'; import { useSelector } from 'react-redux'; import { i18n } from '@kbn/i18n'; import { SideEffectContext } from './side_effect_context'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; +import * as event from '../../../../common/models/event'; import { useResolverDispatch } from './use_resolver_dispatch'; import * as selectors from '../store/selectors'; @@ -38,7 +39,7 @@ export const Panel = memo(function Event({ className }: { className?: string }) interface ProcessTableView { name: string; timestamp?: Date; - event: LegacyEndpointEvent; + event: ResolverEvent; } const { processNodePositions } = useSelector(selectors.processNodePositionsAndEdgeLineSegments); @@ -48,14 +49,16 @@ export const Panel = memo(function Event({ className }: { className?: string }) () => [...processNodePositions.keys()].map(processEvent => { let dateTime; - if (processEvent.endgame.timestamp_utc) { - const date = new Date(processEvent.endgame.timestamp_utc); + const eventTime = event.eventTimestamp(processEvent); + const name = event.eventName(processEvent); + if (eventTime) { + const date = new Date(eventTime); if (isFinite(date.getTime())) { dateTime = date; } } return { - name: processEvent.endgame.process_name ? processEvent.endgame.process_name : '', + name, timestamp: dateTime, event: processEvent, }; @@ -115,9 +118,9 @@ export const Panel = memo(function Event({ className }: { className?: string }) }), dataType: 'date', sortable: true, - render(eventTimestamp?: Date) { - return eventTimestamp ? ( - formatter.format(eventTimestamp) + render(eventDate?: Date) { + return eventDate ? ( + formatter.format(eventDate) ) : ( {i18n.translate('xpack.endpoint.resolver.panel.tabel.row.timestampInvalidLabel', { diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx index 034780c7ba14c..2241df97291ae 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx @@ -8,7 +8,8 @@ import React from 'react'; import styled from 'styled-components'; import { applyMatrix3 } from '../lib/vector2'; import { Vector2, Matrix3 } from '../types'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; +import * as eventModel from '../../../../common/models/event'; /** * A placeholder view for a process node. @@ -32,7 +33,7 @@ export const ProcessEventDot = styled( /** * An event which contains details about the process node. */ - event: LegacyEndpointEvent; + event: ResolverEvent; /** * projectionMatrix which can be used to convert `position` to screen coordinates. */ @@ -42,14 +43,13 @@ export const ProcessEventDot = styled( * Convert the position, which is in 'world' coordinates, to screen coordinates. */ const [left, top] = applyMatrix3(position, projectionMatrix); - const style = { left: (left - 20).toString() + 'px', top: (top - 20).toString() + 'px', }; return ( - - name: {event.endgame.process_name} + + name: {eventModel.eventName(event)}
x: {position[0]}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx index 711e4f9a5c537..6e83fc19a922e 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/use_camera.test.tsx @@ -11,7 +11,7 @@ import { Provider } from 'react-redux'; import * as selectors from '../store/selectors'; import { storeFactory } from '../store'; import { Matrix3, ResolverAction, ResolverStore, SideEffectSimulator } from '../types'; -import { LegacyEndpointEvent } from '../../../../common/types'; +import { ResolverEvent } from '../../../../common/types'; import { SideEffectContext } from './side_effect_context'; import { applyMatrix3 } from '../lib/vector2'; import { sideEffectSimulator } from './side_effect_simulator'; @@ -133,9 +133,9 @@ describe('useCamera on an unpainted element', () => { expect(simulator.mock.requestAnimationFrame).not.toHaveBeenCalled(); }); describe('when the camera begins animation', () => { - let process: LegacyEndpointEvent; + let process: ResolverEvent; beforeEach(() => { - const events: LegacyEndpointEvent[] = []; + const events: ResolverEvent[] = []; const numberOfEvents: number = Math.floor(Math.random() * 10 + 1); for (let index = 0; index < numberOfEvents; index++) { @@ -164,7 +164,7 @@ describe('useCamera on an unpainted element', () => { act(() => { store.dispatch(serverResponseAction); }); - const processes: LegacyEndpointEvent[] = [ + const processes: ResolverEvent[] = [ ...selectors .processNodePositionsAndEdgeLineSegments(store.getState()) .processNodePositions.keys(), diff --git a/x-pack/plugins/endpoint/scripts/resolver_generator.ts b/x-pack/plugins/endpoint/scripts/resolver_generator.ts index a3e56497f0790..503999daec587 100644 --- a/x-pack/plugins/endpoint/scripts/resolver_generator.ts +++ b/x-pack/plugins/endpoint/scripts/resolver_generator.ts @@ -32,7 +32,7 @@ async function main() { }, metadataIndex: { alias: 'mi', - describe: 'index to store endpoint metadata in', + describe: 'index to store host metadata in', default: 'endpoint-agent-1', type: 'string', }, @@ -76,15 +76,15 @@ async function main() { type: 'number', default: 30, }, - numEndpoints: { + numHosts: { alias: 'ne', - describe: 'number of different endpoints to generate alerts for', + describe: 'number of different hosts to generate alerts for', type: 'number', default: 1, }, - alertsPerEndpoint: { + alertsPerHost: { alias: 'ape', - describe: 'number of resolver trees to make for each endpoint', + describe: 'number of resolver trees to make for each host', type: 'number', default: 1, }, @@ -133,12 +133,12 @@ async function main() { } const generator = new EndpointDocGenerator(argv.seed); - for (let i = 0; i < argv.numEndpoints; i++) { + for (let i = 0; i < argv.numHosts; i++) { await client.index({ index: argv.metadataIndex, - body: generator.generateEndpointMetadata(), + body: generator.generateHostMetadata(), }); - for (let j = 0; j < argv.alertsPerEndpoint; j++) { + for (let j = 0; j < argv.alertsPerHost; j++) { const resolverDocs = generator.generateFullResolverTree( argv.ancestors, argv.generations, diff --git a/x-pack/plugins/endpoint/server/routes/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata.test.ts index ee374bc1b57d6..65e07edbcde24 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata.test.ts @@ -18,7 +18,7 @@ import { httpServiceMock, loggingServiceMock, } from '../../../../../src/core/server/mocks'; -import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { HostMetadata, HostResultList } from '../../common/types'; import { SearchResponse } from 'elasticsearch'; import { registerEndpointRoutes } from './metadata'; import { EndpointConfigSchema } from '../config'; @@ -49,8 +49,8 @@ describe('test endpoint route', () => { it('test find the latest of all endpoints', async () => { const mockRequest = httpServerMock.createKibanaRequest({}); - const response: SearchResponse = (data as unknown) as SearchResponse< - EndpointMetadata + const response: SearchResponse = (data as unknown) as SearchResponse< + HostMetadata >; mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => @@ -72,8 +72,8 @@ describe('test endpoint route', () => { expect(mockScopedClient.callAsCurrentUser).toBeCalled(); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(0); expect(endpointResultList.request_page_size).toEqual(10); @@ -93,7 +93,7 @@ describe('test endpoint route', () => { }, }); mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => - Promise.resolve((data as unknown) as SearchResponse) + Promise.resolve((data as unknown) as SearchResponse) ); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith('/api/endpoint/metadata') @@ -117,8 +117,8 @@ describe('test endpoint route', () => { }); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(10); expect(endpointResultList.request_page_size).toEqual(10); @@ -140,7 +140,7 @@ describe('test endpoint route', () => { }, }); mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => - Promise.resolve((data as unknown) as SearchResponse) + Promise.resolve((data as unknown) as SearchResponse) ); [routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) => path.startsWith('/api/endpoint/metadata') @@ -177,8 +177,8 @@ describe('test endpoint route', () => { }); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as EndpointResultList; - expect(endpointResultList.endpoints.length).toEqual(2); + const endpointResultList = mockResponse.ok.mock.calls[0][0]?.body as HostResultList; + expect(endpointResultList.hosts.length).toEqual(2); expect(endpointResultList.total).toEqual(2); expect(endpointResultList.request_page_index).toEqual(10); expect(endpointResultList.request_page_size).toEqual(10); @@ -234,8 +234,8 @@ describe('test endpoint route', () => { const mockRequest = httpServerMock.createKibanaRequest({ params: { id: (data as any).hits.hits[0]._id }, }); - const response: SearchResponse = (data as unknown) as SearchResponse< - EndpointMetadata + const response: SearchResponse = (data as unknown) as SearchResponse< + HostMetadata >; mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); [routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) => @@ -257,7 +257,7 @@ describe('test endpoint route', () => { expect(mockScopedClient.callAsCurrentUser).toBeCalled(); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const result = mockResponse.ok.mock.calls[0][0]?.body as EndpointMetadata; + const result = mockResponse.ok.mock.calls[0][0]?.body as HostMetadata; expect(result).toHaveProperty('endpoint'); }); }); diff --git a/x-pack/plugins/endpoint/server/routes/metadata.ts b/x-pack/plugins/endpoint/server/routes/metadata.ts index 278cfac020a3b..463a071ab0c77 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata.ts @@ -12,11 +12,11 @@ import { kibanaRequestToMetadataListESQuery, kibanaRequestToMetadataGetESQuery, } from '../services/endpoint/metadata_query_builders'; -import { EndpointMetadata, EndpointResultList } from '../../common/types'; +import { HostMetadata, HostResultList } from '../../common/types'; import { EndpointAppContext } from '../types'; interface HitSource { - _source: EndpointMetadata; + _source: HostMetadata; } export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { @@ -57,8 +57,8 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( 'search', queryParams - )) as SearchResponse; - return res.ok({ body: mapToEndpointResultList(queryParams, response) }); + )) as SearchResponse; + return res.ok({ body: mapToHostResultList(queryParams, response) }); } catch (err) { return res.internalError({ body: err }); } @@ -79,7 +79,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( 'search', query - )) as SearchResponse; + )) as SearchResponse; if (response.hits.hits.length === 0) { return res.notFound({ body: 'Endpoint Not Found' }); @@ -93,27 +93,27 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp ); } -function mapToEndpointResultList( +function mapToHostResultList( queryParams: Record, - searchResponse: SearchResponse -): EndpointResultList { - const totalNumberOfEndpoints = searchResponse?.aggregations?.total?.value || 0; + searchResponse: SearchResponse +): HostResultList { + const totalNumberOfHosts = searchResponse?.aggregations?.total?.value || 0; if (searchResponse.hits.hits.length > 0) { return { request_page_size: queryParams.size, request_page_index: queryParams.from, - endpoints: searchResponse.hits.hits + hosts: searchResponse.hits.hits .map(response => response.inner_hits.most_recent.hits.hits) .flatMap(data => data as HitSource) .map(entry => entry._source), - total: totalNumberOfEndpoints, + total: totalNumberOfHosts, }; } else { return { request_page_size: queryParams.size, request_page_index: queryParams.from, - total: totalNumberOfEndpoints, - endpoints: [], + total: totalNumberOfHosts, + hosts: [], }; } } diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js index 50c0e331e3dbc..7b7ca08427087 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { EuiCodeEditor } from '@elastic/eui'; +import { EuiCodeBlock } from '@elastic/eui'; import 'brace/theme/textmate'; @@ -25,17 +25,6 @@ export class ShowJson extends React.PureComponent { return null; } const json = JSON.stringify(data, null, 2); - return ( - - ); + return {json}; } } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx index 484ea3f1d94a0..b45beef4a8b5e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx @@ -183,7 +183,10 @@ export const StepConfigureDatasource: React.FunctionComponent<{ // Step B, configure inputs (and their streams) // Assume packages only export one datasource for now const ConfigureInputs = - packageInfo.datasources && packageInfo.datasources[0] ? ( + packageInfo.datasources && + packageInfo.datasources[0] && + packageInfo.datasources[0].inputs && + packageInfo.datasources[0].inputs.length ? ( {packageInfo.datasources[0].inputs.map(packageInput => { const datasourceInput = datasource.inputs.find(input => input.type === packageInput.type); 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 ad4d636164d71..0e239c24dd9cf 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 @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`tests loading fields.yml: base.yml 1`] = ` +exports[`tests loading base.yml: base.yml 1`] = ` { "order": 1, "index_patterns": [ @@ -47,10 +47,12 @@ exports[`tests loading fields.yml: base.yml 1`] = ` "user": { "properties": { "auid": { - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "euid": { - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -59,7 +61,10 @@ exports[`tests loading fields.yml: base.yml 1`] = ` "nested": { "properties": { "foo": { - "type": "keyword" + "type": "text" + }, + "bar": { + "type": "long" } } } @@ -68,7 +73,1593 @@ exports[`tests loading fields.yml: base.yml 1`] = ` "nested": { "properties": { "bar": { + "type": "keyword", + "ignore_above": 1024 + }, + "baz": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "myalias": { + "type": "alias", + "path": "user.euid" + }, + "validarray": { + "type": "integer" + } + } + }, + "aliases": {} +} +`; + +exports[`tests loading coredns.logs.yml: coredns.logs.yml 1`] = ` +{ + "order": 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": { + "_meta": { + "package": "foo" + }, + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "date_detection": false, + "properties": { + "coredns": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "query": { + "properties": { + "size": { + "type": "long" + }, + "class": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "response": { + "properties": { + "code": { + "type": "keyword", + "ignore_above": 1024 + }, + "flags": { + "type": "keyword", + "ignore_above": 1024 + }, + "size": { + "type": "long" + } + } + }, + "dnssec_ok": { + "type": "boolean" + } + } + } + } + }, + "aliases": {} +} +`; + +exports[`tests loading system.yml: system.yml 1`] = ` +{ + "order": 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": { + "_meta": { + "package": "foo" + }, + "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" + } + } + }, + "nice": { + "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" + } + } + }, + "irq": { + "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" + } + } + } + } + }, + "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" + } + } + }, + "system": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "nice": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "idle": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "iowait": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "irq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "softirq": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "steal": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "total": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + }, + "norm": { + "properties": { + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + } + } + } + } + }, + "diskio": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "read": { + "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" + } + } + }, + "iostat": { + "properties": { + "read": { + "properties": { + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } + } + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } + } + }, + "await": { + "type": "float" + } + } + }, + "write": { + "properties": { + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } + } + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } + } + }, + "await": { + "type": "float" + } + } + }, + "request": { + "properties": { + "avg_size": { + "type": "float" + } + } + }, + "queue": { + "properties": { + "avg_size": { + "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 + } + } + }, + "filesystem": { + "properties": { + "available": { + "type": "long" + }, + "device_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "mount_point": { + "type": "keyword", + "ignore_above": 1024 + }, + "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" + } + } + } + } + }, + "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" + } + } + }, + "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" + } + } + }, + "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" + } + } + }, + "in": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "readahead": { + "properties": { + "pages": { + "type": "long" + }, + "cached": { + "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" + } + } + } + } + } + } + } + } + }, + "network": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "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" + } + } + } + } + }, + "network_summary": { + "properties": { + "ip": { + "properties": { + "*": { + "type": "object" + } + } + }, + "tcp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp_lite": { + "properties": { + "*": { + "type": "object" + } + } + }, + "icmp": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "process": { + "properties": { + "state": { + "type": "keyword", + "ignore_above": 1024 + }, + "cmdline": { + "type": "keyword", + "ignore_above": 2048 + }, + "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 + } + } + }, + "ticks": { + "type": "long" + } + } + }, + "system": { + "properties": { + "ticks": { + "type": "long" + } + } + }, + "start_time": { + "type": "date" + } + } + }, + "memory": { + "properties": { + "size": { + "type": "long" + }, + "rss": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "scaled_float", + "scaling_factor": 1000 + } + } + }, + "share": { + "type": "long" + } + } + }, + "fd": { + "properties": { + "open": { + "type": "long" + }, + "limit": { + "properties": { + "soft": { + "type": "long" + }, + "hard": { + "type": "long" + } + } + } + } + }, + "cgroup": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "cpu": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "cfs": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } + } + }, + "quota": { + "properties": { + "us": { + "type": "long" + } + } + }, + "shares": { + "type": "long" + } + } + }, + "rt": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } + } + }, + "runtime": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "stats": { + "properties": { + "periods": { + "type": "long" + }, + "throttled": { + "properties": { + "periods": { + "type": "long" + }, + "ns": { + "type": "long" + } + } + } + } + } + } + }, + "cpuacct": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "total": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "stats": { + "properties": { + "user": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "system": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "percpu": { + "type": "object" + } + } + }, + "memory": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "mem": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + } + } + }, + "memsw": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + } + } + }, + "kmem": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + } + } + }, + "kmem_tcp": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + } + } + }, + "stats": { + "properties": { + "active_anon": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "active_file": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "cache": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "hierarchical_memory_limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "hierarchical_memsw_limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "inactive_anon": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "inactive_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" + } + } + }, + "rss_huge": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "swap": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "unevictable": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "blkio": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "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" + } + } + } + } + }, + "raid": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "level": { + "type": "keyword", + "ignore_above": 1024 + }, + "sync_action": { + "type": "keyword", + "ignore_above": 1024 + }, + "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" + } + } + } + } + }, + "socket": { + "properties": { + "local": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "remote": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + }, + "host": { + "type": "keyword", + "ignore_above": 1024 + }, + "etld_plus_one": { + "type": "keyword", + "ignore_above": 1024 + }, + "host_error": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "process": { + "properties": { + "cmdline": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "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" + } + } + } + } + }, + "udp": { + "properties": { + "memory": { + "type": "long" + }, + "all": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "uptime": { + "properties": { + "duration": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "users": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "seat": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "service": { + "type": "keyword", + "ignore_above": 1024 + }, + "remote": { + "type": "boolean" + }, + "state": { + "type": "keyword", + "ignore_above": 1024 + }, + "scope": { + "type": "keyword", + "ignore_above": 1024 + }, + "leader": { + "type": "long" + }, + "remote_host": { + "type": "keyword", + "ignore_above": 1024 + } + } } } } 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 005bb78e458e3..de4ba25590c98 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 @@ -12,7 +12,7 @@ import { ElasticsearchAssetType, } from '../../../../types'; import { CallESAsCurrentUser } from '../../../../types'; -import { Field, loadFieldsFromYaml } from '../../fields/field'; +import { Field, loadFieldsFromYaml, processFields } from '../../fields/field'; import { getPipelineNameForInstallation } from '../ingest_pipeline/install'; import { generateMappings, generateTemplateName, getTemplate } from './template'; import * as Registry from '../../registry'; @@ -98,7 +98,7 @@ export async function installTemplate({ dataset: Dataset; packageVersion: string; }): Promise { - const mappings = generateMappings(fields); + const mappings = generateMappings(processFields(fields)); const templateName = generateTemplateName(dataset); let pipelineName; if (dataset.ingest_pipeline) { 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 aa5be59b6a5cd..f4e13748641ed 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 @@ -28,15 +28,38 @@ test('get template', () => { expect(template.index_patterns).toStrictEqual([`${templateName}-*`]); }); -test('tests loading fields.yml', () => { - // Load fields.yml file +test('tests loading base.yml', () => { const ymlPath = path.join(__dirname, '../../fields/tests/base.yml'); const fieldsYML = readFileSync(ymlPath, 'utf-8'); const fields: Field[] = safeLoad(fieldsYML); - processFields(fields); - const mappings = generateMappings(fields); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); const template = getTemplate('logs', 'foo', mappings); expect(template).toMatchSnapshot(path.basename(ymlPath)); }); + +test('tests loading coredns.logs.yml', () => { + const ymlPath = path.join(__dirname, '../../fields/tests/coredns.logs.yml'); + const fieldsYML = readFileSync(ymlPath, 'utf-8'); + const fields: Field[] = safeLoad(fieldsYML); + + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + const template = getTemplate('logs', 'foo', mappings); + + expect(template).toMatchSnapshot(path.basename(ymlPath)); +}); + +test('tests loading system.yml', () => { + const ymlPath = path.join(__dirname, '../../fields/tests/system.yml'); + const fieldsYML = readFileSync(ymlPath, 'utf-8'); + const fields: Field[] = safeLoad(fieldsYML); + + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + const template = getTemplate('metrics', 'whatsthis', mappings); + + expect(template).toMatchSnapshot(path.basename(ymlPath)); +}); 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 f075771e9808a..71c9acc6c10da 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 @@ -14,6 +14,10 @@ interface Properties { interface Mappings { properties: any; } + +const DEFAULT_SCALING_FACTOR = 1000; +const DEFAULT_IGNORE_ABOVE = 1024; + /** * getTemplate retrieves the default template but overwrites the index pattern with the given value. * @@ -33,31 +37,98 @@ export function getTemplate( } /** - * Generate mapping takes the given fields array and creates the Elasticsearch + * Generate mapping takes the given nested fields array and creates the Elasticsearch * mapping properties out of it. * + * This assumes that all fields with dotted.names have been expanded in a previous step. + * * @param fields */ export function generateMappings(fields: Field[]): Mappings { const props: Properties = {}; - fields.forEach(field => { - // Are there more fields inside this field? Build them recursively - if (field.fields && field.fields.length > 0) { - props[field.name] = generateMappings(field.fields); - return; - } + // TODO: this can happen when the fields property in fields.yml is present but empty + // Maybe validation should be moved to fields/field.ts + if (fields) { + fields.forEach(field => { + // If type is not defined, assume keyword + const type = field.type || 'keyword'; + + let fieldProps = getDefaultProperties(field); + + switch (type) { + case 'group': + fieldProps = generateMappings(field.fields!); + break; + case 'integer': + fieldProps.type = 'long'; + break; + case 'scaled_float': + fieldProps.type = 'scaled_float'; + fieldProps.scaling_factor = field.scaling_factor || DEFAULT_SCALING_FACTOR; + break; + case 'text': + fieldProps.type = 'text'; + if (field.analyzer) { + fieldProps.analyzer = field.analyzer; + } + if (field.search_analyzer) { + fieldProps.search_analyzer = field.search_analyzer; + } + break; + case 'keyword': + fieldProps.type = 'keyword'; + if (field.ignore_above) { + fieldProps.ignore_above = field.ignore_above; + } else { + fieldProps.ignore_above = DEFAULT_IGNORE_ABOVE; + } + break; + // TODO move handling of multi_fields here? + case 'object': + // TODO improve + fieldProps.type = 'object'; + break; + case 'array': + // this assumes array fields were validated in an earlier step + // adding an array field with no object_type would result in an error + // when the template is added to ES + if (field.object_type) { + fieldProps.type = field.object_type; + } + break; + case 'alias': + // this assumes alias fields were validated in an earlier step + // adding a path to a field that doesn't exist would result in an error + // when the template is added to ES. + fieldProps.type = 'alias'; + fieldProps.path = field.path; + break; + default: + fieldProps.type = type; + } + props[field.name] = fieldProps; + }); + } - // If not type is defined, take keyword - const type = field.type || 'keyword'; - // Only add keyword fields for now - // TODO: add support for other field types - if (type === 'keyword') { - props[field.name] = { type }; - } - }); return { properties: props }; } +function getDefaultProperties(field: Field): Properties { + const properties: Properties = {}; + + if (field.index) { + properties.index = field.index; + } + if (field.doc_values) { + properties.doc_values = field.doc_values; + } + if (field.copy_to) { + properties.copy_to = field.copy_to; + } + + return properties; +} + /** * Generates the template name out of the given information */ diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/__snapshots__/field.test.ts.snap b/x-pack/plugins/ingest_manager/server/services/epm/fields/__snapshots__/field.test.ts.snap index 76991bde77008..5c402b896093a 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/__snapshots__/field.test.ts.snap +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/__snapshots__/field.test.ts.snap @@ -23,7 +23,12 @@ exports[`tests loading fields.yml: base.yml 1`] = ` "type": "group", "fields": [ { - "name": "foo" + "name": "foo", + "type": "text" + }, + { + "name": "bar", + "type": "integer" } ] } @@ -35,8 +40,21 @@ exports[`tests loading fields.yml: base.yml 1`] = ` "fields": [ { "name": "bar" + }, + { + "name": "baz" } ] + }, + { + "name": "myalias", + "type": "alias", + "path": "user.euid" + }, + { + "name": "validarray", + "type": "array", + "object_type": "integer" } ] `; @@ -54,46 +72,2395 @@ exports[`tests loading fields.yml: coredns.logs.yml 1`] = ` "description": "id of the DNS transaction\\n" }, { - "name": "query.size", - "type": "integer", - "format": "bytes", - "description": "size of the DNS query\\n" + "name": "query", + "type": "group", + "fields": [ + { + "name": "size", + "type": "integer", + "format": "bytes", + "description": "size of the DNS query\\n" + }, + { + "name": "class", + "type": "keyword", + "description": "DNS query class\\n" + }, + { + "name": "name", + "type": "keyword", + "description": "DNS query name\\n" + }, + { + "name": "type", + "type": "keyword", + "description": "DNS query type\\n" + } + ] }, { - "name": "query.class", - "type": "keyword", - "description": "DNS query class\\n" + "name": "response", + "type": "group", + "fields": [ + { + "name": "code", + "type": "keyword", + "description": "DNS response code\\n" + }, + { + "name": "flags", + "type": "keyword", + "description": "DNS response flags\\n" + }, + { + "name": "size", + "type": "integer", + "format": "bytes", + "description": "size of the DNS response\\n" + } + ] }, { - "name": "query.name", - "type": "keyword", - "description": "DNS query name\\n" + "name": "dnssec_ok", + "type": "boolean", + "description": "dnssec flag\\n" + } + ] + } +] +`; + +exports[`tests loading fields.yml: system.yml 1`] = ` +[ + { + "name": "system", + "type": "group", + "fields": [ + { + "name": "core", + "type": "group", + "description": "\`system-core\` contains CPU metrics for a single core of a multi-core system.\\n", + "fields": [ + { + "name": "id", + "type": "long", + "description": "CPU Core number.\\n" + }, + { + "name": "user", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in user space.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in user space.\\n" + } + ] + }, + { + "name": "system", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in kernel space.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in kernel space.\\n" + } + ] + }, + { + "name": "nice", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent on low-priority processes.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent on low-priority processes.\\n" + } + ] + }, + { + "name": "idle", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent idle.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent idle.\\n" + } + ] + }, + { + "name": "iowait", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in wait (on disk).\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in wait (on disk).\\n" + } + ] + }, + { + "name": "irq", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling hardware interrupts.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent servicing and handling hardware interrupts.\\n" + } + ] + }, + { + "name": "softirq", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling software interrupts.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent servicing and handling software interrupts.\\n" + } + ] + }, + { + "name": "steal", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor was servicing another processor. Available only on Unix.\\n" + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in involuntary wait by the virtual CPU while the hypervisor was servicing another processor. Available only on Unix.\\n" + } + ] + } + ] }, { - "name": "query.type", - "type": "keyword", - "description": "DNS query type\\n" + "name": "cpu", + "type": "group", + "description": "\`cpu\` contains local CPU stats.\\n", + "release": "ga", + "fields": [ + { + "name": "cores", + "type": "long", + "description": "The number of CPU cores present on the host. The non-normalized percentages will have a maximum value of \`100% * cores\`. The normalized percentages already take this value into account and have a maximum value of 100%.\\n" + }, + { + "name": "user", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in user space. On multi-core systems, you can have percentages that are greater than 100%. For example, if 3 cores are at 60% use, then the \`system.cpu.user.pct\` will be 180%.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in user space.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in user space.\\n" + } + ] + }, + { + "name": "system", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in kernel space.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in kernel space.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in kernel space.\\n" + } + ] + }, + { + "name": "nice", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent on low-priority processes.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent on low-priority processes.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent on low-priority processes.\\n" + } + ] + }, + { + "name": "idle", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent idle.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent idle.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent idle.\\n" + } + ] + }, + { + "name": "iowait", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in wait (on disk).\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in wait (on disk).\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in wait (on disk).\\n" + } + ] + }, + { + "name": "irq", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling hardware interrupts.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling hardware interrupts.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent servicing and handling hardware interrupts.\\n" + } + ] + }, + { + "name": "softirq", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling software interrupts.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent servicing and handling software interrupts.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent servicing and handling software interrupts.\\n" + } + ] + }, + { + "name": "steal", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor was servicing another processor. Available only on Unix.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor was servicing another processor. Available only on Unix.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time spent in involuntary wait by the virtual CPU while the hypervisor was servicing another processor. Available only on Unix.\\n" + } + ] + }, + { + "name": "total", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent in states other than Idle and IOWait.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time in states other than Idle and IOWait, normalised by the number of cores.\\n" + } + ] + } + ] + } + ] }, { - "name": "response.code", - "type": "keyword", - "description": "DNS response code\\n" + "name": "diskio", + "type": "group", + "description": "\`disk\` contains disk IO metrics collected from the operating system.\\n", + "release": "ga", + "fields": [ + { + "name": "name", + "type": "keyword", + "example": "sda1", + "description": "The disk name.\\n" + }, + { + "name": "serial_number", + "type": "keyword", + "description": "The disk's serial number. This may not be provided by all operating systems.\\n" + }, + { + "name": "read", + "type": "group", + "fields": [ + { + "name": "count", + "type": "long", + "description": "The total number of reads completed successfully.\\n" + }, + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The total number of bytes read successfully. On Linux this is the number of sectors read multiplied by an assumed sector size of 512.\\n" + }, + { + "name": "time", + "type": "long", + "description": "The total number of milliseconds spent by all reads.\\n" + } + ] + }, + { + "name": "write", + "type": "group", + "fields": [ + { + "name": "count", + "type": "long", + "description": "The total number of writes completed successfully.\\n" + }, + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The total number of bytes written successfully. On Linux this is the number of sectors written multiplied by an assumed sector size of 512.\\n" + }, + { + "name": "time", + "type": "long", + "description": "The total number of milliseconds spent by all writes.\\n" + } + ] + }, + { + "name": "io", + "type": "group", + "fields": [ + { + "name": "time", + "type": "long", + "description": "The total number of of milliseconds spent doing I/Os.\\n" + } + ] + }, + { + "name": "iostat", + "type": "group", + "fields": [ + { + "name": "read", + "type": "group", + "fields": [ + { + "name": "request", + "type": "group", + "fields": [ + { + "name": "merges_per_sec", + "type": "float", + "description": "The number of read requests merged per second that were queued to the device.\\n" + }, + { + "name": "per_sec", + "type": "float", + "description": "The number of read requests that were issued to the device per second\\n" + } + ] + }, + { + "name": "per_sec", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "float", + "description": "The number of Bytes read from the device per second.\\n", + "format": "bytes" + } + ] + }, + { + "name": "await", + "type": "float", + "description": "The average time spent for read requests issued to the device to be served.\\n" + } + ] + }, + { + "name": "write", + "type": "group", + "fields": [ + { + "name": "request", + "type": "group", + "fields": [ + { + "name": "merges_per_sec", + "type": "float", + "description": "The number of write requests merged per second that were queued to the device.\\n" + }, + { + "name": "per_sec", + "type": "float", + "description": "The number of write requests that were issued to the device per second\\n" + } + ] + }, + { + "name": "per_sec", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "float", + "description": "The number of Bytes write from the device per second.\\n", + "format": "bytes" + } + ] + }, + { + "name": "await", + "type": "float", + "description": "The average time spent for write requests issued to the device to be served.\\n" + } + ] + }, + { + "name": "request", + "type": "group", + "fields": [ + { + "name": "avg_size", + "type": "float", + "description": "The average size (in bytes) of the requests that were issued to the device.\\n" + } + ] + }, + { + "name": "queue", + "type": "group", + "fields": [ + { + "name": "avg_size", + "type": "float", + "description": "The average queue length of the requests that were issued to the device.\\n" + } + ] + }, + { + "name": "await", + "type": "float", + "description": "The average time spent for requests issued to the device to be served.\\n" + }, + { + "name": "service_time", + "type": "float", + "description": "The average service time (in milliseconds) for I/O requests that were issued to the device.\\n" + }, + { + "name": "busy", + "type": "float", + "description": "Percentage of CPU time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100%.\\n" + } + ] + } + ] }, { - "name": "response.flags", - "type": "keyword", - "description": "DNS response flags\\n" + "name": "entropy", + "type": "group", + "description": "Available system entropy\\n", + "release": "ga", + "fields": [ + { + "name": "available_bits", + "type": "long", + "description": "The available bits of entropy\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of available entropy, relative to the pool size of 4096\\n" + } + ] }, { - "name": "response.size", - "type": "integer", - "format": "bytes", - "description": "size of the DNS response\\n" + "name": "filesystem", + "type": "group", + "description": "\`filesystem\` contains local filesystem stats.\\n", + "release": "ga", + "fields": [ + { + "name": "available", + "type": "long", + "format": "bytes", + "description": "The disk space available to an unprivileged user in bytes.\\n" + }, + { + "name": "device_name", + "type": "keyword", + "description": "The disk name. For example: \`/dev/disk1\`\\n" + }, + { + "name": "type", + "type": "keyword", + "description": "The disk type. For example: \`ext4\`\\n" + }, + { + "name": "mount_point", + "type": "keyword", + "description": "The mounting point. For example: \`/\`\\n" + }, + { + "name": "files", + "type": "long", + "description": "The total number of file nodes in the file system.\\n" + }, + { + "name": "free", + "type": "long", + "format": "bytes", + "description": "The disk space available in bytes.\\n" + }, + { + "name": "free_files", + "type": "long", + "description": "The number of free file nodes in the file system.\\n" + }, + { + "name": "total", + "type": "long", + "format": "bytes", + "description": "The total disk space in bytes.\\n" + }, + { + "name": "used", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The used disk space in bytes.\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of used disk space.\\n" + } + ] + } + ] }, { - "name": "dnssec_ok", - "type": "boolean", - "description": "dnssec flag\\n" + "name": "fsstat", + "type": "group", + "description": "\`system.fsstat\` contains filesystem metrics aggregated from all mounted filesystems.\\n", + "release": "ga", + "fields": [ + { + "name": "count", + "type": "long", + "description": "Number of file systems found." + }, + { + "name": "total_files", + "type": "long", + "description": "Total number of files." + }, + { + "name": "total_size", + "format": "bytes", + "type": "group", + "description": "Nested file system docs.", + "fields": [ + { + "name": "free", + "type": "long", + "format": "bytes", + "description": "Total free space.\\n" + }, + { + "name": "used", + "type": "long", + "format": "bytes", + "description": "Total used space.\\n" + }, + { + "name": "total", + "type": "long", + "format": "bytes", + "description": "Total space (used plus free).\\n" + } + ] + } + ] + }, + { + "name": "load", + "type": "group", + "description": "CPU load averages.\\n", + "release": "ga", + "fields": [ + { + "name": "1", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load average for the last minute.\\n" + }, + { + "name": "5", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load average for the last 5 minutes.\\n" + }, + { + "name": "15", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load average for the last 15 minutes.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "1", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load for the last minute divided by the number of cores.\\n" + }, + { + "name": "5", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load for the last 5 minutes divided by the number of cores.\\n" + }, + { + "name": "15", + "type": "scaled_float", + "scaling_factor": 100, + "description": "Load for the last 15 minutes divided by the number of cores.\\n" + } + ] + }, + { + "name": "cores", + "type": "long", + "description": "The number of CPU cores present on the host.\\n" + } + ] + }, + { + "name": "memory", + "type": "group", + "description": "\`memory\` contains local memory stats.\\n", + "release": "ga", + "fields": [ + { + "name": "total", + "type": "long", + "format": "bytes", + "description": "Total memory.\\n" + }, + { + "name": "used", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Used memory.\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of used memory.\\n" + } + ] + }, + { + "name": "free", + "type": "long", + "format": "bytes", + "description": "The total amount of free memory in bytes. This value does not include memory consumed by system caches and buffers (see system.memory.actual.free).\\n" + }, + { + "name": "actual", + "type": "group", + "description": "Actual memory used and free.\\n", + "fields": [ + { + "name": "used", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Actual used memory in bytes. It represents the difference between the total and the available memory. The available memory depends on the OS. For more details, please check \`system.actual.free\`.\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of actual used memory.\\n" + } + ] + }, + { + "name": "free", + "type": "long", + "format": "bytes", + "description": "Actual free memory in bytes. It is calculated based on the OS. On Linux it consists of the free memory plus caches and buffers. On OSX it is a sum of free memory and the inactive memory. On Windows, it is equal to \`system.memory.free\`.\\n" + } + ] + }, + { + "name": "swap", + "type": "group", + "prefix": "[float]", + "description": "This group contains statistics related to the swap memory usage on the system.", + "fields": [ + { + "name": "total", + "type": "long", + "format": "bytes", + "description": "Total swap memory.\\n" + }, + { + "name": "used", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Used swap memory.\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of used swap memory.\\n" + } + ] + }, + { + "name": "free", + "type": "long", + "format": "bytes", + "description": "Available swap memory.\\n" + }, + { + "name": "out", + "type": "group", + "fields": [ + { + "name": "pages", + "type": "long", + "description": "count of pages swapped out" + } + ] + }, + { + "name": "in", + "type": "group", + "fields": [ + { + "name": "pages", + "type": "long", + "description": "count of pages swapped in" + } + ] + }, + { + "name": "readahead", + "type": "group", + "fields": [ + { + "name": "pages", + "type": "long", + "description": "swap readahead pages" + }, + { + "name": "cached", + "type": "long", + "description": "swap readahead cache hits" + } + ] + } + ] + }, + { + "name": "hugepages", + "type": "group", + "prefix": "[float]", + "description": "This group contains statistics related to huge pages usage on the system.", + "fields": [ + { + "name": "total", + "type": "long", + "format": "number", + "description": "Number of huge pages in the pool.\\n" + }, + { + "name": "used", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Memory used in allocated huge pages.\\n" + }, + { + "name": "pct", + "type": "long", + "format": "percent", + "description": "Percentage of huge pages used.\\n" + } + ] + }, + { + "name": "free", + "type": "long", + "format": "number", + "description": "Number of available huge pages in the pool.\\n" + }, + { + "name": "reserved", + "type": "long", + "format": "number", + "description": "Number of reserved but not allocated huge pages in the pool.\\n" + }, + { + "name": "surplus", + "type": "long", + "format": "number", + "description": "Number of overcommited huge pages.\\n" + }, + { + "name": "default_size", + "type": "long", + "format": "bytes", + "description": "Default size for huge pages.\\n" + }, + { + "name": "swap", + "type": "group", + "fields": [ + { + "name": "out", + "type": "group", + "description": "huge pages swapped out", + "fields": [ + { + "name": "pages", + "type": "long", + "description": "pages swapped out" + }, + { + "name": "fallback", + "type": "long", + "description": "Count of huge pages that must be split before swapout" + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "network", + "type": "group", + "description": "\`network\` contains network IO metrics for a single network interface.\\n", + "release": "ga", + "fields": [ + { + "name": "name", + "type": "keyword", + "example": "eth0", + "description": "The network interface name.\\n" + }, + { + "name": "out", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The number of bytes sent.\\n" + }, + { + "name": "packets", + "type": "long", + "description": "The number of packets sent.\\n" + }, + { + "name": "errors", + "type": "long", + "description": "The number of errors while sending.\\n" + }, + { + "name": "dropped", + "type": "long", + "description": "The number of outgoing packets that were dropped. This value is always 0 on Darwin and BSD because it is not reported by the operating system.\\n" + } + ] + }, + { + "name": "in", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The number of bytes received.\\n" + }, + { + "name": "packets", + "type": "long", + "description": "The number or packets received.\\n" + }, + { + "name": "errors", + "type": "long", + "description": "The number of errors while receiving.\\n" + }, + { + "name": "dropped", + "type": "long", + "description": "The number of incoming packets that were dropped.\\n" + } + ] + } + ] + }, + { + "name": "network_summary", + "type": "group", + "release": "beta", + "description": "Metrics relating to global network activity\\n", + "fields": [ + { + "name": "ip", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "description": "IP counters\\n" + } + ] + }, + { + "name": "tcp", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "description": "TCP counters\\n" + } + ] + }, + { + "name": "udp", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "description": "UDP counters\\n" + } + ] + }, + { + "name": "udp_lite", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "description": "UDP Lite counters\\n" + } + ] + }, + { + "name": "icmp", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "description": "ICMP counters\\n" + } + ] + } + ] + }, + { + "name": "process", + "type": "group", + "description": "\`process\` contains process metadata, CPU metrics, and memory metrics.\\n", + "release": "ga", + "fields": [ + { + "name": "state", + "type": "keyword", + "description": "The process state. For example: \\"running\\".\\n" + }, + { + "name": "cmdline", + "type": "keyword", + "description": "The full command-line used to start the process, including the arguments separated by space.\\n", + "ignore_above": 2048 + }, + { + "name": "env", + "type": "object", + "object_type": "keyword", + "description": "The environment variables used to start the process. The data is available on FreeBSD, Linux, and OS X.\\n" + }, + { + "name": "cpu", + "type": "group", + "prefix": "[float]", + "description": "CPU-specific statistics per process.", + "fields": [ + { + "name": "user", + "type": "group", + "fields": [ + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time the process spent in user space.\\n" + } + ] + }, + { + "name": "total", + "type": "group", + "fields": [ + { + "name": "value", + "type": "long", + "description": "The value of CPU usage since starting the process.\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent by the process since the last update. Its value is similar to the %CPU value of the process displayed by the top command on Unix systems.\\n" + }, + { + "name": "norm", + "type": "group", + "fields": [ + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of CPU time spent by the process since the last event. This value is normalized by the number of CPU cores and it ranges from 0 to 100%.\\n" + } + ] + }, + { + "name": "ticks", + "type": "long", + "description": "The total CPU time spent by the process.\\n" + } + ] + }, + { + "name": "system", + "type": "group", + "fields": [ + { + "name": "ticks", + "type": "long", + "description": "The amount of CPU time the process spent in kernel space.\\n" + } + ] + }, + { + "name": "start_time", + "type": "date", + "description": "The time when the process was started.\\n" + } + ] + }, + { + "name": "memory", + "type": "group", + "description": "Memory-specific statistics per process.", + "prefix": "[float]", + "fields": [ + { + "name": "size", + "type": "long", + "format": "bytes", + "description": "The total virtual memory the process has.\\n" + }, + { + "name": "rss", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The Resident Set Size. The amount of memory the process occupied in main memory (RAM).\\n" + }, + { + "name": "pct", + "type": "scaled_float", + "format": "percent", + "description": "The percentage of memory the process occupied in main memory (RAM).\\n" + } + ] + }, + { + "name": "share", + "type": "long", + "format": "bytes", + "description": "The shared memory the process uses.\\n" + } + ] + }, + { + "name": "fd", + "type": "group", + "description": "File descriptor usage metrics. This set of metrics is available for Linux and FreeBSD.\\n", + "prefix": "[float]", + "fields": [ + { + "name": "open", + "type": "long", + "description": "The number of file descriptors open by the process." + }, + { + "name": "limit", + "type": "group", + "fields": [ + { + "name": "soft", + "type": "long", + "description": "The soft limit on the number of file descriptors opened by the process. The soft limit can be changed by the process at any time.\\n" + }, + { + "name": "hard", + "type": "long", + "description": "The hard limit on the number of file descriptors opened by the process. The hard limit can only be raised by root.\\n" + } + ] + } + ] + }, + { + "name": "cgroup", + "type": "group", + "description": "Metrics and limits from the cgroup of which the task is a member. cgroup metrics are reported when the process has membership in a non-root cgroup. These metrics are only available on Linux.\\n", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "The ID common to all cgroups associated with this task. If there isn't a common ID used by all cgroups this field will be absent.\\n" + }, + { + "name": "path", + "type": "keyword", + "description": "The path to the cgroup relative to the cgroup subsystem's mountpoint. If there isn't a common path used by all cgroups this field will be absent.\\n" + }, + { + "name": "cpu", + "type": "group", + "description": "The cpu subsystem schedules CPU access for tasks in the cgroup. Access can be controlled by two separate schedulers, CFS and RT. CFS stands for completely fair scheduler which proportionally divides the CPU time between cgroups based on weight. RT stands for real time scheduler which sets a maximum amount of CPU time that processes in the cgroup can consume during a given period.\\n", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "ID of the cgroup." + }, + { + "name": "path", + "type": "keyword", + "description": "Path to the cgroup relative to the cgroup subsystem's mountpoint.\\n" + }, + { + "name": "cfs", + "type": "group", + "fields": [ + { + "name": "period", + "type": "group", + "fields": [ + { + "name": "us", + "type": "long", + "description": "Period of time in microseconds for how regularly a cgroup's access to CPU resources should be reallocated.\\n" + } + ] + }, + { + "name": "quota", + "type": "group", + "fields": [ + { + "name": "us", + "type": "long", + "description": "Total amount of time in microseconds for which all tasks in a cgroup can run during one period (as defined by cfs.period.us).\\n" + } + ] + }, + { + "name": "shares", + "type": "long", + "description": "An integer value that specifies a relative share of CPU time available to the tasks in a cgroup. The value specified in the cpu.shares file must be 2 or higher.\\n" + } + ] + }, + { + "name": "rt", + "type": "group", + "fields": [ + { + "name": "period", + "type": "group", + "fields": [ + { + "name": "us", + "type": "long", + "description": "Period of time in microseconds for how regularly a cgroup's access to CPU resources is reallocated.\\n" + } + ] + }, + { + "name": "runtime", + "type": "group", + "fields": [ + { + "name": "us", + "type": "long", + "description": "Period of time in microseconds for the longest continuous period in which the tasks in a cgroup have access to CPU resources.\\n" + } + ] + } + ] + }, + { + "name": "stats", + "type": "group", + "fields": [ + { + "name": "periods", + "type": "long", + "description": "Number of period intervals (as specified in cpu.cfs.period.us) that have elapsed.\\n" + }, + { + "name": "throttled", + "type": "group", + "fields": [ + { + "name": "periods", + "type": "long", + "description": "Number of times tasks in a cgroup have been throttled (that is, not allowed to run because they have exhausted all of the available time as specified by their quota).\\n" + }, + { + "name": "ns", + "type": "long", + "description": "The total time duration (in nanoseconds) for which tasks in a cgroup have been throttled.\\n" + } + ] + } + ] + } + ] + }, + { + "name": "cpuacct", + "type": "group", + "description": "CPU accounting metrics.", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "ID of the cgroup." + }, + { + "name": "path", + "type": "keyword", + "description": "Path to the cgroup relative to the cgroup subsystem's mountpoint.\\n" + }, + { + "name": "total", + "type": "group", + "fields": [ + { + "name": "ns", + "type": "long", + "description": "Total CPU time in nanoseconds consumed by all tasks in the cgroup.\\n" + } + ] + }, + { + "name": "stats", + "type": "group", + "fields": [ + { + "name": "user", + "type": "group", + "fields": [ + { + "name": "ns", + "type": "long", + "description": "CPU time consumed by tasks in user mode." + } + ] + }, + { + "name": "system", + "type": "group", + "fields": [ + { + "name": "ns", + "type": "long", + "description": "CPU time consumed by tasks in user (kernel) mode." + } + ] + } + ] + }, + { + "name": "percpu", + "type": "object", + "object_type": "long", + "description": "CPU time (in nanoseconds) consumed on each CPU by all tasks in this cgroup.\\n" + } + ] + }, + { + "name": "memory", + "type": "group", + "description": "Memory limits and metrics.", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "ID of the cgroup." + }, + { + "name": "path", + "type": "keyword", + "description": "Path to the cgroup relative to the cgroup subsystem's mountpoint.\\n" + }, + { + "name": "mem", + "type": "group", + "fields": [ + { + "name": "usage", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Total memory usage by processes in the cgroup (in bytes).\\n" + }, + { + "name": "max", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum memory used by processes in the cgroup (in bytes).\\n" + } + ] + } + ] + }, + { + "name": "limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum amount of user memory in bytes (including file cache) that tasks in the cgroup are allowed to use.\\n" + } + ] + }, + { + "name": "failures", + "type": "long", + "description": "The number of times that the memory limit (mem.limit.bytes) was reached.\\n" + } + ] + }, + { + "name": "memsw", + "type": "group", + "fields": [ + { + "name": "usage", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The sum of current memory usage plus swap space used by processes in the cgroup (in bytes).\\n" + }, + { + "name": "max", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum amount of memory and swap space used by processes in the cgroup (in bytes).\\n" + } + ] + } + ] + }, + { + "name": "limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum amount for the sum of memory and swap usage that tasks in the cgroup are allowed to use.\\n" + } + ] + }, + { + "name": "failures", + "type": "long", + "description": "The number of times that the memory plus swap space limit (memsw.limit.bytes) was reached.\\n" + } + ] + }, + { + "name": "kmem", + "type": "group", + "fields": [ + { + "name": "usage", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Total kernel memory usage by processes in the cgroup (in bytes).\\n" + }, + { + "name": "max", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum kernel memory used by processes in the cgroup (in bytes).\\n" + } + ] + } + ] + }, + { + "name": "limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum amount of kernel memory that tasks in the cgroup are allowed to use.\\n" + } + ] + }, + { + "name": "failures", + "type": "long", + "description": "The number of times that the memory limit (kmem.limit.bytes) was reached.\\n" + } + ] + }, + { + "name": "kmem_tcp", + "type": "group", + "fields": [ + { + "name": "usage", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Total memory usage for TCP buffers in bytes.\\n" + }, + { + "name": "max", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum memory used for TCP buffers by processes in the cgroup (in bytes).\\n" + } + ] + } + ] + }, + { + "name": "limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "The maximum amount of memory for TCP buffers that tasks in the cgroup are allowed to use.\\n" + } + ] + }, + { + "name": "failures", + "type": "long", + "description": "The number of times that the memory limit (kmem_tcp.limit.bytes) was reached.\\n" + } + ] + }, + { + "name": "stats", + "type": "group", + "fields": [ + { + "name": "active_anon", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Anonymous and swap cache on active least-recently-used (LRU) list, including tmpfs (shmem), in bytes.\\n" + } + ] + }, + { + "name": "active_file", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "File-backed memory on active LRU list, in bytes." + } + ] + }, + { + "name": "cache", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Page cache, including tmpfs (shmem), in bytes." + } + ] + }, + { + "name": "hierarchical_memory_limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Memory limit for the hierarchy that contains the memory cgroup, in bytes.\\n" + } + ] + }, + { + "name": "hierarchical_memsw_limit", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Memory plus swap limit for the hierarchy that contains the memory cgroup, in bytes.\\n" + } + ] + }, + { + "name": "inactive_anon", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Anonymous and swap cache on inactive LRU list, including tmpfs (shmem), in bytes\\n" + } + ] + }, + { + "name": "inactive_file", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "File-backed memory on inactive LRU list, in bytes.\\n" + } + ] + }, + { + "name": "mapped_file", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Size of memory-mapped mapped files, including tmpfs (shmem), in bytes.\\n" + } + ] + }, + { + "name": "page_faults", + "type": "long", + "description": "Number of times that a process in the cgroup triggered a page fault.\\n" + }, + { + "name": "major_page_faults", + "type": "long", + "description": "Number of times that a process in the cgroup triggered a major fault. \\"Major\\" faults happen when the kernel actually has to read the data from disk.\\n" + }, + { + "name": "pages_in", + "type": "long", + "description": "Number of pages paged into memory. This is a counter.\\n" + }, + { + "name": "pages_out", + "type": "long", + "description": "Number of pages paged out of memory. This is a counter.\\n" + }, + { + "name": "rss", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Anonymous and swap cache (includes transparent hugepages), not including tmpfs (shmem), in bytes.\\n" + } + ] + }, + { + "name": "rss_huge", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Number of bytes of anonymous transparent hugepages.\\n" + } + ] + }, + { + "name": "swap", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Swap usage, in bytes.\\n" + } + ] + }, + { + "name": "unevictable", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Memory that cannot be reclaimed, in bytes.\\n" + } + ] + } + ] + } + ] + }, + { + "name": "blkio", + "type": "group", + "description": "Block IO metrics.", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "ID of the cgroup." + }, + { + "name": "path", + "type": "keyword", + "description": "Path to the cgroup relative to the cgroup subsystems mountpoint.\\n" + }, + { + "name": "total", + "type": "group", + "fields": [ + { + "name": "bytes", + "type": "long", + "format": "bytes", + "description": "Total number of bytes transferred to and from all block devices by processes in the cgroup.\\n" + }, + { + "name": "ios", + "type": "long", + "description": "Total number of I/O operations performed on all devices by processes in the cgroup as seen by the throttling policy.\\n" + } + ] + } + ] + } + ] + }, + { + "name": "summary", + "title": "Process Summary", + "type": "group", + "description": "Summary metrics for the processes running on the host.\\n", + "release": "ga", + "fields": [ + { + "name": "total", + "type": "long", + "description": "Total number of processes on this host.\\n" + }, + { + "name": "running", + "type": "long", + "description": "Number of running processes on this host.\\n" + }, + { + "name": "idle", + "type": "long", + "description": "Number of idle processes on this host.\\n" + }, + { + "name": "sleeping", + "type": "long", + "description": "Number of sleeping processes on this host.\\n" + }, + { + "name": "stopped", + "type": "long", + "description": "Number of stopped processes on this host.\\n" + }, + { + "name": "zombie", + "type": "long", + "description": "Number of zombie processes on this host.\\n" + }, + { + "name": "dead", + "type": "long", + "description": "Number of dead processes on this host. It's very unlikely that it will appear but in some special situations it may happen.\\n" + }, + { + "name": "unknown", + "type": "long", + "description": "Number of processes for which the state couldn't be retrieved or is unknown.\\n" + } + ] + } + ] + }, + { + "name": "raid", + "type": "group", + "description": "raid\\n", + "release": "ga", + "fields": [ + { + "name": "name", + "type": "keyword", + "description": "Name of the device.\\n" + }, + { + "name": "status", + "type": "keyword", + "description": "activity-state of the device.\\n" + }, + { + "name": "level", + "type": "keyword", + "description": "The raid level of the device\\n" + }, + { + "name": "sync_action", + "type": "keyword", + "description": "Current sync action, if the RAID array is redundant\\n" + }, + { + "name": "disks", + "type": "group", + "fields": [ + { + "name": "active", + "type": "long", + "description": "Number of active disks.\\n" + }, + { + "name": "total", + "type": "long", + "description": "Total number of disks the device consists of.\\n" + }, + { + "name": "spare", + "type": "long", + "description": "Number of spared disks.\\n" + }, + { + "name": "failed", + "type": "long", + "description": "Number of failed disks.\\n" + }, + { + "name": "states", + "type": "group", + "fields": [ + { + "name": "*", + "type": "object", + "object_type": "keyword", + "description": "map of raw disk states\\n" + } + ] + } + ] + }, + { + "name": "blocks", + "type": "group", + "fields": [ + { + "name": "total", + "type": "long", + "description": "Number of blocks the device holds, in 1024-byte blocks.\\n" + }, + { + "name": "synced", + "type": "long", + "description": "Number of blocks on the device that are in sync, in 1024-byte blocks.\\n" + } + ] + } + ] + }, + { + "name": "socket", + "type": "group", + "description": "TCP sockets that are active.\\n", + "release": "ga", + "fields": [ + { + "name": "local", + "type": "group", + "fields": [ + { + "name": "ip", + "type": "ip", + "example": "192.0.2.1 or 2001:0DB8:ABED:8536::1", + "description": "Local IP address. This can be an IPv4 or IPv6 address.\\n" + }, + { + "name": "port", + "type": "long", + "example": 22, + "description": "Local port.\\n" + } + ] + }, + { + "name": "remote", + "type": "group", + "fields": [ + { + "name": "ip", + "type": "ip", + "example": "192.0.2.1 or 2001:0DB8:ABED:8536::1", + "description": "Remote IP address. This can be an IPv4 or IPv6 address.\\n" + }, + { + "name": "port", + "type": "long", + "example": 22, + "description": "Remote port.\\n" + }, + { + "name": "host", + "type": "keyword", + "example": "76-211-117-36.nw.example.com.", + "description": "PTR record associated with the remote IP. It is obtained via reverse IP lookup.\\n" + }, + { + "name": "etld_plus_one", + "type": "keyword", + "example": "example.com.", + "description": "The effective top-level domain (eTLD) of the remote host plus one more label. For example, the eTLD+1 for \\"foo.bar.golang.org.\\" is \\"golang.org.\\". The data for determining the eTLD comes from an embedded copy of the data from http://publicsuffix.org.\\n" + }, + { + "name": "host_error", + "type": "keyword", + "description": "Error describing the cause of the reverse lookup failure.\\n" + } + ] + }, + { + "name": "process", + "type": "group", + "fields": [ + { + "name": "cmdline", + "type": "keyword", + "description": "Full command line\\n" + } + ] + }, + { + "name": "user", + "type": "group", + "fields": [] + }, + { + "name": "summary", + "title": "Socket summary", + "type": "group", + "description": "Summary metrics of open sockets in the host system\\n", + "release": "ga", + "fields": [ + { + "name": "all", + "type": "group", + "description": "All connections\\n", + "fields": [ + { + "name": "count", + "type": "integer", + "description": "All open connections\\n" + }, + { + "name": "listening", + "type": "integer", + "description": "All listening ports\\n" + } + ] + }, + { + "name": "tcp", + "type": "group", + "description": "All TCP connections\\n", + "fields": [ + { + "name": "memory", + "type": "integer", + "format": "bytes", + "description": "Memory used by TCP sockets in bytes, based on number of allocated pages and system page size. Corresponds to limits set in /proc/sys/net/ipv4/tcp_mem. Only available on Linux.\\n" + }, + { + "name": "all", + "type": "group", + "description": "All TCP connections\\n", + "fields": [ + { + "name": "orphan", + "type": "integer", + "description": "A count of all orphaned tcp sockets. Only available on Linux.\\n" + }, + { + "name": "count", + "type": "integer", + "description": "All open TCP connections\\n" + }, + { + "name": "listening", + "type": "integer", + "description": "All TCP listening ports\\n" + }, + { + "name": "established", + "type": "integer", + "description": "Number of established TCP connections\\n" + }, + { + "name": "close_wait", + "type": "integer", + "description": "Number of TCP connections in _close_wait_ state\\n" + }, + { + "name": "time_wait", + "type": "integer", + "description": "Number of TCP connections in _time_wait_ state\\n" + }, + { + "name": "syn_sent", + "type": "integer", + "description": "Number of TCP connections in _syn_sent_ state\\n" + }, + { + "name": "syn_recv", + "type": "integer", + "description": "Number of TCP connections in _syn_recv_ state\\n" + }, + { + "name": "fin_wait1", + "type": "integer", + "description": "Number of TCP connections in _fin_wait1_ state\\n" + }, + { + "name": "fin_wait2", + "type": "integer", + "description": "Number of TCP connections in _fin_wait2_ state\\n" + }, + { + "name": "last_ack", + "type": "integer", + "description": "Number of TCP connections in _last_ack_ state\\n" + }, + { + "name": "closing", + "type": "integer", + "description": "Number of TCP connections in _closing_ state\\n" + } + ] + } + ] + }, + { + "name": "udp", + "type": "group", + "description": "All UDP connections\\n", + "fields": [ + { + "name": "memory", + "type": "integer", + "format": "bytes", + "description": "Memory used by UDP sockets in bytes, based on number of allocated pages and system page size. Corresponds to limits set in /proc/sys/net/ipv4/udp_mem. Only available on Linux.\\n" + }, + { + "name": "all", + "type": "group", + "description": "All UDP connections\\n", + "fields": [ + { + "name": "count", + "type": "integer", + "description": "All open UDP connections\\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "name": "uptime", + "type": "group", + "description": "\`uptime\` contains the operating system uptime metric.\\n", + "release": "ga", + "fields": [ + { + "name": "duration", + "type": "group", + "fields": [ + { + "name": "ms", + "type": "long", + "format": "duration", + "input_format": "milliseconds", + "description": "The OS uptime in milliseconds.\\n" + } + ] + } + ] + }, + { + "name": "users", + "type": "group", + "release": "beta", + "description": "Logged-in user session data\\n", + "fields": [ + { + "name": "id", + "type": "keyword", + "description": "The ID of the session\\n" + }, + { + "name": "seat", + "type": "keyword", + "description": "An associated logind seat\\n" + }, + { + "name": "path", + "type": "keyword", + "description": "The DBus object path of the session\\n" + }, + { + "name": "type", + "type": "keyword", + "description": "The type of the user session\\n" + }, + { + "name": "service", + "type": "keyword", + "description": "A session associated with the service\\n" + }, + { + "name": "remote", + "type": "boolean", + "description": "A bool indicating a remote session\\n" + }, + { + "name": "state", + "type": "keyword", + "description": "The current state of the session\\n" + }, + { + "name": "scope", + "type": "keyword", + "description": "The associated systemd scope\\n" + }, + { + "name": "leader", + "type": "long", + "description": "The root PID of the session\\n" + }, + { + "name": "remote_host", + "type": "keyword", + "description": "A remote host address for the session\\n" + } + ] } ] } 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 3cdf011d9d0e3..929f2518ee748 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 @@ -8,7 +8,7 @@ import { readFileSync } from 'fs'; import glob from 'glob'; import { safeLoad } from 'js-yaml'; import path from 'path'; -import { Field, processFields } from './field'; +import { Field, Fields, getField, processFields } from './field'; // Add our own serialiser to just do JSON.stringify expect.addSnapshotSerializer({ @@ -27,9 +27,56 @@ test('tests loading fields.yml', () => { for (const file of files) { const fieldsYML = readFileSync(file, 'utf-8'); const fields: Field[] = safeLoad(fieldsYML); - processFields(fields); + const processedFields = processFields(fields); // Check that content file and generated file are equal - expect(fields).toMatchSnapshot(path.basename(file)); + expect(processedFields).toMatchSnapshot(path.basename(file)); } }); + +describe('getField searches recursively for nested field in fields given an array of path parts', () => { + const searchFields: Fields = [ + { + name: '1', + fields: [ + { + name: '1-1', + }, + { + name: '1-2', + }, + ], + }, + { + name: '2', + fields: [ + { + name: '2-1', + }, + { + name: '2-2', + fields: [ + { + name: '2-2-1', + }, + { + name: '2-2-2', + }, + ], + }, + ], + }, + ]; + test('returns undefined when the field does not exist', () => { + expect(getField(searchFields, ['0'])).toBe(undefined); + }); + test('returns undefined if the field is not a leaf node', () => { + expect(getField(searchFields, ['1'])?.name).toBe(undefined); + }); + test('returns undefined searching for a nested field that does not exist', () => { + expect(getField(searchFields, ['1', '1-3'])?.name).toBe(undefined); + }); + test('returns nested field that is a leaf node', () => { + expect(getField(searchFields, ['2', '2-2', '2-2-1'])?.name).toBe('2-2-1'); + }); +}); 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 eb515f5652f36..4a1a84baf6599 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 @@ -21,6 +21,12 @@ export interface Field { required?: boolean; multi_fields?: Fields; doc_values?: boolean; + copy_to?: string; + analyzer?: string; + search_analyzer?: string; + ignore_above?: number; + object_type?: string; + scaling_factor?: number; // Kibana specific analyzed?: boolean; @@ -43,44 +49,140 @@ export interface Field { export type Fields = Field[]; /** - * ProcessFields takes the given fields read from yaml and expands it. + * expandFields takes the given fields read from yaml and expands them. * There are dotted fields in the field.yml like `foo.bar`. These should - * be stored as an object inside an object and is the main purpose of this - * preprocessing. + * be stored as an field within a 'group' field. * - * Note: This function modifies the passed field param. + * Note: This function modifies the passed fields array. */ -export function processFields(fields: Fields) { +export function expandFields(fields: Fields) { fields.forEach((field, key) => { const fieldName = field.name; - // If the field name contains a dot, it means we need to create sub objects + // If the field name contains a dot, it means we need to + // - take the first part of the name + // - create a field of type 'group' with this first part + // - put the original field, named with the rest of the original name in the fields property of the new group field if (fieldName.includes('.')) { // Split up the name by dots to extract first and other parts const nameParts = fieldName.split('.'); // Getting first part of the name for the new field - const newNameTop = nameParts[0]; - delete nameParts[0]; + const groupFieldName = nameParts[0]; // Put back together the parts again for the new field name - const newName = nameParts.length === 1 ? nameParts[0] : nameParts.slice(1).join('.'); + const restFieldName = nameParts.slice(1).join('.'); - field.name = newName; + // keep all properties of the original field, but give it the shortened name + field.name = restFieldName; - // Create the new field with the old field inside - const newField: Field = { - name: newNameTop, + // create a new field of type group with the original field in the fields array + const groupField: Field = { + name: groupFieldName, type: 'group', fields: [field], }; - // Replace the old field in the array - fields[key] = newField; - if (newField.fields) { - processFields(newField.fields); + // check child fields further down the tree + if (groupField.fields) { + expandFields(groupField.fields); } + // Replace the original field in the array with the new one + fields[key] = groupField; + } else { + // even if this field doesn't have dots to expand, its child fields further down the tree might + if (field.fields) { + expandFields(field.fields); + } + } + }); +} +/** + * dedupFields takes the given fields and merges sibling fields with the + * same name together. + * These can result from expandFields when the input contains dotted field + * names that share parts of their hierarchy. + */ +function dedupFields(fields: Fields): Fields { + const dedupedFields: Fields = []; + fields.forEach(field => { + const found = dedupedFields.find(f => { + return f.name === field.name; + }); + if (found) { + if (found.type === 'group' && field.type === 'group' && found.fields && field.fields) { + found.fields = dedupFields(found.fields.concat(field.fields)); + } else { + // only 'group' fields can be merged in this way + // XXX: don't abort on error for now + // see discussion in https://github.com/elastic/kibana/pull/59894 + // throw new Error( + // "Can't merge fields " + JSON.stringify(found) + ' and ' + JSON.stringify(field) + // ); + } + } else { + if (field.fields) { + field.fields = dedupFields(field.fields); + } + dedupedFields.push(field); } }); + return dedupedFields; +} + +/** validateFields takes the given fields and verifies: + * + * - all fields of type alias point to existing fields. + * - all fields of type array have a property object_type + * + * Invalid fields are silently removed. + */ + +function validateFields(fields: Fields, allFields: Fields): Fields { + const validatedFields: Fields = []; + + fields.forEach(field => { + if (field.type === 'alias') { + if (field.path && getField(allFields, field.path.split('.'))) { + validatedFields.push(field); + } + } else if (field.type === 'array') { + if (field.object_type) { + validatedFields.push(field); + } + } else { + validatedFields.push(field); + } + if (field.fields) { + field.fields = validateFields(field.fields, allFields); + } + }); + return validatedFields; +} + +export const getField = (fields: Fields, pathNames: string[]): Field | undefined => { + if (!pathNames.length) return undefined; + // get the first rest of path names + const [name, ...restPathNames] = pathNames; + for (const field of fields) { + if (field.name === name) { + // check field's fields, passing in the remaining path names + if (field.fields && field.fields.length > 0) { + return getField(field.fields, restPathNames); + } + // no nested fields to search, but still more names - not found + if (restPathNames.length) { + return undefined; + } + return field; + } + } + return undefined; +}; + +export function processFields(fields: Fields): Fields { + expandFields(fields); + const dedupedFields = dedupFields(fields); + return validateFields(dedupedFields, dedupedFields); } const isFields = (path: string) => { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/base.yml b/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/base.yml index 86b61245aa3b8..5a71c7dee54dc 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/base.yml +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/base.yml @@ -4,4 +4,20 @@ - name: auid - name: euid - name: long.nested.foo + type: text +- name: long.nested.bar + type: integer - name: nested.bar +- name: nested.baz +- name: myalias + type: alias + path: user.euid +- name: invalidalias + type: alias + path: euid +- name: validarray + type: array + object_type: integer +- name: invalidarray + type: array + diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/system.yml b/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/system.yml new file mode 100644 index 0000000000000..609914616a683 --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/tests/system.yml @@ -0,0 +1,1625 @@ +- name: system.core + type: group + description: > + `system-core` contains CPU metrics for a single core of a multi-core system. + fields: + - name: id + type: long + description: > + CPU Core number. + + # Percentages + - name: user.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in user space. + + - name: user.ticks + type: long + description: > + The amount of CPU time spent in user space. + + - name: system.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in kernel space. + + - name: system.ticks + type: long + description: > + The amount of CPU time spent in kernel space. + + - name: nice.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent on low-priority processes. + + - name: nice.ticks + type: long + description: > + The amount of CPU time spent on low-priority processes. + + - name: idle.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent idle. + + - name: idle.ticks + type: long + description: > + The amount of CPU time spent idle. + + - name: iowait.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in wait (on disk). + + - name: iowait.ticks + type: long + description: > + The amount of CPU time spent in wait (on disk). + + - name: irq.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling hardware interrupts. + + - name: irq.ticks + type: long + description: > + The amount of CPU time spent servicing and handling hardware interrupts. + + - name: softirq.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling software interrupts. + + - name: softirq.ticks + type: long + description: > + The amount of CPU time spent servicing and handling software interrupts. + + - name: steal.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor + was servicing another processor. + Available only on Unix. + + - name: steal.ticks + type: long + description: > + The amount of CPU time spent in involuntary wait by the virtual CPU while the hypervisor + was servicing another processor. + Available only on Unix. +- name: system.cpu + type: group + description: > + `cpu` contains local CPU stats. + release: ga + fields: + - name: cores + type: long + description: > + The number of CPU cores present on the host. The non-normalized + percentages will have a maximum value of `100% * cores`. The + normalized percentages already take this value into account and have + a maximum value of 100%. + + # Percentages + - name: user.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in user space. On multi-core systems, + you can have percentages that are greater than 100%. For example, if 3 + cores are at 60% use, then the `system.cpu.user.pct` will be 180%. + + - name: system.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in kernel space. + + - name: nice.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent on low-priority processes. + + - name: idle.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent idle. + + - name: iowait.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in wait (on disk). + + - name: irq.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling hardware interrupts. + + - name: softirq.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling software interrupts. + + - name: steal.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor + was servicing another processor. + Available only on Unix. + + - name: total.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in states other than Idle and IOWait. + + # Normalized Percentages + - name: user.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in user space. + + - name: system.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in kernel space. + + - name: nice.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent on low-priority processes. + + - name: idle.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent idle. + + - name: iowait.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in wait (on disk). + + - name: irq.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling hardware interrupts. + + - name: softirq.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent servicing and handling software interrupts. + + - name: steal.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent in involuntary wait by the virtual CPU while the hypervisor + was servicing another processor. + Available only on Unix. + + - name: total.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time in states other than Idle and IOWait, normalised by the number of cores. + + + # Ticks + - name: user.ticks + type: long + description: > + The amount of CPU time spent in user space. + + - name: system.ticks + type: long + description: > + The amount of CPU time spent in kernel space. + + - name: nice.ticks + type: long + description: > + The amount of CPU time spent on low-priority processes. + + - name: idle.ticks + type: long + description: > + The amount of CPU time spent idle. + + - name: iowait.ticks + type: long + description: > + The amount of CPU time spent in wait (on disk). + + - name: irq.ticks + type: long + description: > + The amount of CPU time spent servicing and handling hardware interrupts. + + - name: softirq.ticks + type: long + description: > + The amount of CPU time spent servicing and handling software interrupts. + + - name: steal.ticks + type: long + description: > + The amount of CPU time spent in involuntary wait by the virtual CPU while the hypervisor + was servicing another processor. + Available only on Unix. +- name: system.diskio + type: group + description: > + `disk` contains disk IO metrics collected from the operating system. + release: ga + fields: + - name: name + type: keyword + example: sda1 + description: > + The disk name. + + - name: serial_number + type: keyword + description: > + The disk's serial number. This may not be provided by all operating + systems. + + - name: read.count + type: long + description: > + The total number of reads completed successfully. + + - name: write.count + type: long + description: > + The total number of writes completed successfully. + + - name: read.bytes + type: long + format: bytes + description: > + The total number of bytes read successfully. On Linux this is + the number of sectors read multiplied by an assumed sector size of 512. + + - name: write.bytes + type: long + format: bytes + description: > + The total number of bytes written successfully. On Linux this is + the number of sectors written multiplied by an assumed sector size of + 512. + + - name: read.time + type: long + description: > + The total number of milliseconds spent by all reads. + + - name: write.time + type: long + description: > + The total number of milliseconds spent by all writes. + + - name: io.time + type: long + description: > + The total number of of milliseconds spent doing I/Os. + + - name: iostat.read.request.merges_per_sec + type: float + description: > + The number of read requests merged per second that were queued to the device. + + - name: iostat.write.request.merges_per_sec + type: float + description: > + The number of write requests merged per second that were queued to the device. + + - name: iostat.read.request.per_sec + type: float + description: > + The number of read requests that were issued to the device per second + + - name: iostat.write.request.per_sec + type: float + description: > + The number of write requests that were issued to the device per second + + - name: iostat.read.per_sec.bytes + type: float + description: > + The number of Bytes read from the device per second. + format: bytes + + - name: iostat.read.await + type: float + description: > + The average time spent for read requests issued to the device to be served. + + - name: iostat.write.per_sec.bytes + type: float + description: > + The number of Bytes write from the device per second. + format: bytes + + - name: iostat.write.await + type: float + description: > + The average time spent for write requests issued to the device to be served. + + - name: iostat.request.avg_size + type: float + description: > + The average size (in bytes) of the requests that were issued to the device. + + - name: iostat.queue.avg_size + type: float + description: > + The average queue length of the requests that were issued to the device. + + - name: iostat.await + type: float + description: > + The average time spent for requests issued to the device to be served. + + - name: iostat.service_time + type: float + description: > + The average service time (in milliseconds) for I/O requests that were issued to the device. + + - name: iostat.busy + type: float + description: > + Percentage of CPU time during which I/O requests were issued to the device (bandwidth utilization for the device). Device saturation occurs when this value is close to 100%. +- name: system.entropy + type: group + description: > + Available system entropy + release: ga + fields: + - name: available_bits + type: long + description: > + The available bits of entropy + - name: pct + type: scaled_float + format: percent + description: > + The percentage of available entropy, relative to the pool size of 4096 +- name: system.filesystem + type: group + description: > + `filesystem` contains local filesystem stats. + release: ga + fields: + - name: available + type: long + format: bytes + description: > + The disk space available to an unprivileged user in bytes. + - name: device_name + type: keyword + description: > + The disk name. For example: `/dev/disk1` + - name: type + type: keyword + description: > + The disk type. For example: `ext4` + - name: mount_point + type: keyword + description: > + The mounting point. For example: `/` + - name: files + type: long + description: > + The total number of file nodes in the file system. + - name: free + type: long + format: bytes + description: > + The disk space available in bytes. + - name: free_files + type: long + description: > + The number of free file nodes in the file system. + - name: total + type: long + format: bytes + description: > + The total disk space in bytes. + - name: used.bytes + type: long + format: bytes + description: > + The used disk space in bytes. + - name: used.pct + type: scaled_float + format: percent + description: > + The percentage of used disk space. +- name: system.fsstat + type: group + description: > + `system.fsstat` contains filesystem metrics aggregated from all mounted + filesystems. + release: ga + fields: + - name: count + type: long + description: Number of file systems found. + - name: total_files + type: long + description: Total number of files. + - name: total_size + format: bytes + type: group + description: Nested file system docs. + fields: + - name: free + type: long + format: bytes + description: > + Total free space. + - name: used + type: long + format: bytes + description: > + Total used space. + - name: total + type: long + format: bytes + description: > + Total space (used plus free). +- name: system.load + type: group + description: > + CPU load averages. + release: ga + fields: + - name: "1" + type: scaled_float + scaling_factor: 100 + description: > + Load average for the last minute. + - name: "5" + type: scaled_float + scaling_factor: 100 + description: > + Load average for the last 5 minutes. + - name: "15" + type: scaled_float + scaling_factor: 100 + description: > + Load average for the last 15 minutes. + + - name: "norm.1" + type: scaled_float + scaling_factor: 100 + description: > + Load for the last minute divided by the number of cores. + + - name: "norm.5" + type: scaled_float + scaling_factor: 100 + description: > + Load for the last 5 minutes divided by the number of cores. + + - name: "norm.15" + type: scaled_float + scaling_factor: 100 + description: > + Load for the last 15 minutes divided by the number of cores. + + - name: "cores" + type: long + description: > + The number of CPU cores present on the host. +- name: system.memory + type: group + description: > + `memory` contains local memory stats. + release: ga + fields: + - name: total + type: long + format: bytes + description: > + Total memory. + + - name: used.bytes + type: long + format: bytes + description: > + Used memory. + + - name: free + type: long + format: bytes + description: > + The total amount of free memory in bytes. This value does not include memory consumed by system caches and + buffers (see system.memory.actual.free). + + - name: used.pct + type: scaled_float + format: percent + description: > + The percentage of used memory. + + - name: actual + type: group + description: > + Actual memory used and free. + fields: + + - name: used.bytes + type: long + format: bytes + description: > + Actual used memory in bytes. It represents the difference between the total and the available memory. The + available memory depends on the OS. For more details, please check `system.actual.free`. + + - name: free + type: long + format: bytes + description: > + Actual free memory in bytes. It is calculated based on the OS. On Linux it consists of the free memory + plus caches and buffers. On OSX it is a sum of free memory and the inactive memory. On Windows, it is equal + to `system.memory.free`. + + - name: used.pct + type: scaled_float + format: percent + description: > + The percentage of actual used memory. + + - name: swap + type: group + prefix: "[float]" + description: This group contains statistics related to the swap memory usage on the system. + fields: + - name: total + type: long + format: bytes + description: > + Total swap memory. + + - name: used.bytes + type: long + format: bytes + description: > + Used swap memory. + + - name: free + type: long + format: bytes + description: > + Available swap memory. + + - name: out.pages + type: long + description: count of pages swapped out + + - name: in.pages + type: long + description: count of pages swapped in + + - name: readahead.pages + type: long + description: swap readahead pages + + - name: readahead.cached + type: long + description: swap readahead cache hits + + - name: used.pct + type: scaled_float + format: percent + description: > + The percentage of used swap memory. + + - name: hugepages + type: group + prefix: "[float]" + description: This group contains statistics related to huge pages usage on the system. + fields: + - name: total + type: long + format: number + description: > + Number of huge pages in the pool. + + - name: used.bytes + type: long + format: bytes + description: > + Memory used in allocated huge pages. + + - name: used.pct + type: long + format: percent + description: > + Percentage of huge pages used. + + - name: free + type: long + format: number + description: > + Number of available huge pages in the pool. + + - name: reserved + type: long + format: number + description: > + Number of reserved but not allocated huge pages in the pool. + + - name: surplus + type: long + format: number + description: > + Number of overcommited huge pages. + + - name: default_size + type: long + format: bytes + description: > + Default size for huge pages. + + - name: swap.out + type: group + description: huge pages swapped out + fields: + - name: pages + type: long + description: pages swapped out + - name: fallback + type: long + description: Count of huge pages that must be split before swapout +- name: system.network + type: group + description: > + `network` contains network IO metrics for a single network interface. + release: ga + fields: + - name: name + type: keyword + example: eth0 + description: > + The network interface name. + + - name: out.bytes + type: long + format: bytes + description: > + The number of bytes sent. + + - name: in.bytes + type: long + format: bytes + description: > + The number of bytes received. + + - name: out.packets + type: long + description: > + The number of packets sent. + + - name: in.packets + type: long + description: > + The number or packets received. + + - name: in.errors + type: long + description: > + The number of errors while receiving. + + - name: out.errors + type: long + description: > + The number of errors while sending. + + - name: in.dropped + type: long + description: > + The number of incoming packets that were dropped. + + - name: out.dropped + type: long + description: > + The number of outgoing packets that were dropped. This value is always + 0 on Darwin and BSD because it is not reported by the operating system. +- name: system.network_summary + type: group + release: beta + description: > + Metrics relating to global network activity + fields: + - name: ip.* + type: object + description: > + IP counters + - name: tcp.* + type: object + description: > + TCP counters + - name: udp.* + type: object + description: > + UDP counters + - name: udp_lite.* + type: object + description: > + UDP Lite counters + - name: icmp.* + type: object + description: > + ICMP counters +- name: system.process + type: group + description: > + `process` contains process metadata, CPU metrics, and memory metrics. + release: ga + fields: + - name: name + type: alias + path: process.name + migration: true + - name: state + type: keyword + description: > + The process state. For example: "running". + - name: pid + type: alias + path: process.pid + migration: true + - name: ppid + type: alias + path: process.ppid + migration: true + - name: pgid + type: alias + path: process.pgid + migration: true + - name: cmdline + type: keyword + description: > + The full command-line used to start the process, including the + arguments separated by space. + ignore_above: 2048 + - name: username + type: alias + path: user.name + migration: true + - name: cwd + type: alias + path: process.working_directory + migration: true + - name: env + type: object + object_type: keyword + description: > + The environment variables used to start the process. The data is + available on FreeBSD, Linux, and OS X. + - name: cpu + type: group + prefix: "[float]" + description: CPU-specific statistics per process. + fields: + - name: user.ticks + type: long + description: > + The amount of CPU time the process spent in user space. + - name: total.value + type: long + description: > + The value of CPU usage since starting the process. + - name: total.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent by the process since the last update. Its value is similar to the + %CPU value of the process displayed by the top command on Unix systems. + - name: total.norm.pct + type: scaled_float + format: percent + description: > + The percentage of CPU time spent by the process since the last event. + This value is normalized by the number of CPU cores and it ranges + from 0 to 100%. + - name: system.ticks + type: long + description: > + The amount of CPU time the process spent in kernel space. + - name: total.ticks + type: long + description: > + The total CPU time spent by the process. + - name: start_time + type: date + description: > + The time when the process was started. + - name: memory + type: group + description: Memory-specific statistics per process. + prefix: "[float]" + fields: + - name: size + type: long + format: bytes + description: > + The total virtual memory the process has. + - name: rss.bytes + type: long + format: bytes + description: > + The Resident Set Size. The amount of memory the process occupied in main memory (RAM). + - name: rss.pct + type: scaled_float + format: percent + description: > + The percentage of memory the process occupied in main memory (RAM). + - name: share + type: long + format: bytes + description: > + The shared memory the process uses. + - name: fd + type: group + description: > + File descriptor usage metrics. This set of metrics is available for + Linux and FreeBSD. + prefix: "[float]" + fields: + - name: open + type: long + description: The number of file descriptors open by the process. + - name: limit.soft + type: long + description: > + The soft limit on the number of file descriptors opened by the + process. The soft limit can be changed by the process at any time. + - name: limit.hard + type: long + description: > + The hard limit on the number of file descriptors opened by the + process. The hard limit can only be raised by root. + - name: cgroup + type: group + description: > + Metrics and limits from the cgroup of which the task is a member. + cgroup metrics are reported when the process has membership in a + non-root cgroup. These metrics are only available on Linux. + fields: + - name: id + type: keyword + description: > + The ID common to all cgroups associated with this task. + If there isn't a common ID used by all cgroups this field will be + absent. + + - name: path + type: keyword + description: > + The path to the cgroup relative to the cgroup subsystem's mountpoint. + If there isn't a common path used by all cgroups this field will be + absent. + + - name: cpu + type: group + description: > + The cpu subsystem schedules CPU access for tasks in the cgroup. + Access can be controlled by two separate schedulers, CFS and RT. + CFS stands for completely fair scheduler which proportionally + divides the CPU time between cgroups based on weight. RT stands for + real time scheduler which sets a maximum amount of CPU time that + processes in the cgroup can consume during a given period. + + fields: + - name: id + type: keyword + description: ID of the cgroup. + + - name: path + type: keyword + description: > + Path to the cgroup relative to the cgroup subsystem's + mountpoint. + + - name: cfs.period.us + type: long + description: > + Period of time in microseconds for how regularly a + cgroup's access to CPU resources should be reallocated. + + - name: cfs.quota.us + type: long + description: > + Total amount of time in microseconds for which all + tasks in a cgroup can run during one period (as defined by + cfs.period.us). + + - name: cfs.shares + type: long + description: > + An integer value that specifies a relative share of CPU time + available to the tasks in a cgroup. The value specified in the + cpu.shares file must be 2 or higher. + + - name: rt.period.us + type: long + description: > + Period of time in microseconds for how regularly a cgroup's + access to CPU resources is reallocated. + + - name: rt.runtime.us + type: long + description: > + Period of time in microseconds for the longest continuous period + in which the tasks in a cgroup have access to CPU resources. + + - name: stats.periods + type: long + description: > + Number of period intervals (as specified in cpu.cfs.period.us) + that have elapsed. + + - name: stats.throttled.periods + type: long + description: > + Number of times tasks in a cgroup have been throttled (that is, + not allowed to run because they have exhausted all of the + available time as specified by their quota). + + - name: stats.throttled.ns + type: long + description: > + The total time duration (in nanoseconds) for which tasks in a + cgroup have been throttled. + + - name: cpuacct + type: group + description: CPU accounting metrics. + fields: + - name: id + type: keyword + description: ID of the cgroup. + + - name: path + type: keyword + description: > + Path to the cgroup relative to the cgroup subsystem's + mountpoint. + + - name: total.ns + type: long + description: > + Total CPU time in nanoseconds consumed by all tasks in the + cgroup. + + - name: stats.user.ns + type: long + description: CPU time consumed by tasks in user mode. + + - name: stats.system.ns + type: long + description: CPU time consumed by tasks in user (kernel) mode. + + - name: percpu + type: object + object_type: long + description: > + CPU time (in nanoseconds) consumed on each CPU by all tasks in + this cgroup. + + - name: memory + type: group + description: Memory limits and metrics. + fields: + - name: id + type: keyword + description: ID of the cgroup. + + - name: path + type: keyword + description: > + Path to the cgroup relative to the cgroup subsystem's mountpoint. + + - name: mem.usage.bytes + type: long + format: bytes + description: > + Total memory usage by processes in the cgroup (in bytes). + + - name: mem.usage.max.bytes + type: long + format: bytes + description: > + The maximum memory used by processes in the cgroup (in bytes). + + - name: mem.limit.bytes + type: long + format: bytes + description: > + The maximum amount of user memory in bytes (including file + cache) that tasks in the cgroup are allowed to use. + + - name: mem.failures + type: long + description: > + The number of times that the memory limit (mem.limit.bytes) was + reached. + + - name: memsw.usage.bytes + type: long + format: bytes + description: > + The sum of current memory usage plus swap space used by + processes in the cgroup (in bytes). + + - name: memsw.usage.max.bytes + type: long + format: bytes + description: > + The maximum amount of memory and swap space used by processes in + the cgroup (in bytes). + + - name: memsw.limit.bytes + type: long + format: bytes + description: > + The maximum amount for the sum of memory and swap usage + that tasks in the cgroup are allowed to use. + + - name: memsw.failures + type: long + description: > + The number of times that the memory plus swap space limit + (memsw.limit.bytes) was reached. + + - name: kmem.usage.bytes + type: long + format: bytes + description: > + Total kernel memory usage by processes in the cgroup (in bytes). + + - name: kmem.usage.max.bytes + type: long + format: bytes + description: > + The maximum kernel memory used by processes in the cgroup (in + bytes). + + - name: kmem.limit.bytes + type: long + format: bytes + description: > + The maximum amount of kernel memory that tasks in the cgroup are + allowed to use. + + - name: kmem.failures + type: long + description: > + The number of times that the memory limit (kmem.limit.bytes) was + reached. + + - name: kmem_tcp.usage.bytes + type: long + format: bytes + description: > + Total memory usage for TCP buffers in bytes. + + - name: kmem_tcp.usage.max.bytes + type: long + format: bytes + description: > + The maximum memory used for TCP buffers by processes in the + cgroup (in bytes). + + - name: kmem_tcp.limit.bytes + type: long + format: bytes + description: > + The maximum amount of memory for TCP buffers that tasks in the + cgroup are allowed to use. + + - name: kmem_tcp.failures + type: long + description: > + The number of times that the memory limit (kmem_tcp.limit.bytes) + was reached. + + - name: stats.active_anon.bytes + type: long + format: bytes + description: > + Anonymous and swap cache on active least-recently-used (LRU) + list, including tmpfs (shmem), in bytes. + + - name: stats.active_file.bytes + type: long + format: bytes + description: File-backed memory on active LRU list, in bytes. + + - name: stats.cache.bytes + type: long + format: bytes + description: Page cache, including tmpfs (shmem), in bytes. + + - name: stats.hierarchical_memory_limit.bytes + type: long + format: bytes + description: > + Memory limit for the hierarchy that contains the memory cgroup, + in bytes. + + - name: stats.hierarchical_memsw_limit.bytes + type: long + format: bytes + description: > + Memory plus swap limit for the hierarchy that contains the + memory cgroup, in bytes. + + - name: stats.inactive_anon.bytes + type: long + format: bytes + description: > + Anonymous and swap cache on inactive LRU list, including tmpfs + (shmem), in bytes + + - name: stats.inactive_file.bytes + type: long + format: bytes + description: > + File-backed memory on inactive LRU list, in bytes. + + - name: stats.mapped_file.bytes + type: long + format: bytes + description: > + Size of memory-mapped mapped files, including tmpfs (shmem), + in bytes. + + - name: stats.page_faults + type: long + description: > + Number of times that a process in the cgroup triggered a page + fault. + + - name: stats.major_page_faults + type: long + description: > + Number of times that a process in the cgroup triggered a major + fault. "Major" faults happen when the kernel actually has to + read the data from disk. + + - name: stats.pages_in + type: long + description: > + Number of pages paged into memory. This is a counter. + + - name: stats.pages_out + type: long + description: > + Number of pages paged out of memory. This is a counter. + + - name: stats.rss.bytes + type: long + format: bytes + description: > + Anonymous and swap cache (includes transparent hugepages), not + including tmpfs (shmem), in bytes. + + - name: stats.rss_huge.bytes + type: long + format: bytes + description: > + Number of bytes of anonymous transparent hugepages. + + - name: stats.swap.bytes + type: long + format: bytes + description: > + Swap usage, in bytes. + + - name: stats.unevictable.bytes + type: long + format: bytes + description: > + Memory that cannot be reclaimed, in bytes. + + - name: blkio + type: group + description: Block IO metrics. + fields: + - name: id + type: keyword + description: ID of the cgroup. + + - name: path + type: keyword + description: > + Path to the cgroup relative to the cgroup subsystems mountpoint. + + - name: total.bytes + type: long + format: bytes + description: > + Total number of bytes transferred to and from all block devices + by processes in the cgroup. + + - name: total.ios + type: long + description: > + Total number of I/O operations performed on all devices + by processes in the cgroup as seen by the throttling policy. +- name: system.process.summary + title: Process Summary + type: group + description: > + Summary metrics for the processes running on the host. + release: ga + fields: + - name: total + type: long + description: > + Total number of processes on this host. + - name: running + type: long + description: > + Number of running processes on this host. + - name: idle + type: long + description: > + Number of idle processes on this host. + - name: sleeping + type: long + description: > + Number of sleeping processes on this host. + - name: stopped + type: long + description: > + Number of stopped processes on this host. + - name: zombie + type: long + description: > + Number of zombie processes on this host. + - name: dead + type: long + description: > + Number of dead processes on this host. It's very unlikely that it will appear but in some special situations it may happen. + - name: unknown + type: long + description: > + Number of processes for which the state couldn't be retrieved or is unknown. +- name: system.raid + type: group + description: > + raid + release: ga + fields: + - name: name + type: keyword + description: > + Name of the device. + - name: status + type: keyword + description: > + activity-state of the device. + - name: level + type: keyword + description: > + The raid level of the device + - name: sync_action + type: keyword + description: > + Current sync action, if the RAID array is redundant + - name: disks.active + type: long + description: > + Number of active disks. + - name: disks.total + type: long + description: > + Total number of disks the device consists of. + - name: disks.spare + type: long + description: > + Number of spared disks. + - name: disks.failed + type: long + description: > + Number of failed disks. + - name: disks.states.* + type: object + object_type: keyword + description: > + map of raw disk states + - name: blocks.total + type: long + description: > + Number of blocks the device holds, in 1024-byte blocks. + - name: blocks.synced + type: long + description: > + Number of blocks on the device that are in sync, in 1024-byte blocks. +- name: system.socket + type: group + description: > + TCP sockets that are active. + release: ga + fields: + - name: direction + type: alias + path: network.direction + migration: true + + - name: family + type: alias + path: network.type + migration: true + + - name: local.ip + type: ip + example: 192.0.2.1 or 2001:0DB8:ABED:8536::1 + description: > + Local IP address. This can be an IPv4 or IPv6 address. + + - name: local.port + type: long + example: 22 + description: > + Local port. + + - name: remote.ip + type: ip + example: 192.0.2.1 or 2001:0DB8:ABED:8536::1 + description: > + Remote IP address. This can be an IPv4 or IPv6 address. + + - name: remote.port + type: long + example: 22 + description: > + Remote port. + + - name: remote.host + type: keyword + example: 76-211-117-36.nw.example.com. + description: > + PTR record associated with the remote IP. It is obtained via reverse + IP lookup. + + - name: remote.etld_plus_one + type: keyword + example: example.com. + description: > + The effective top-level domain (eTLD) of the remote host plus one more + label. For example, the eTLD+1 for "foo.bar.golang.org." is "golang.org.". + The data for determining the eTLD comes from an embedded copy of the data + from http://publicsuffix.org. + + - name: remote.host_error + type: keyword + description: > + Error describing the cause of the reverse lookup failure. + + - name: process.pid + type: alias + path: process.pid + migration: true + + - name: process.command + type: alias + path: process.name + migration: true + + - name: process.cmdline + type: keyword + description: > + Full command line + + - name: process.exe + type: alias + path: process.executable + migration: true + + - name: user.id + type: alias + path: user.id + migration: true + + - name: user.name + type: alias + path: user.full_name + migration: true +- name: system.socket.summary + title: Socket summary + type: group + description: > + Summary metrics of open sockets in the host system + release: ga + fields: + - name: all + type: group + description: > + All connections + fields: + - name: count + type: integer + description: > + All open connections + - name: listening + type: integer + description: > + All listening ports + - name: tcp + type: group + description: > + All TCP connections + fields: + - name: memory + type: integer + format: bytes + description: > + Memory used by TCP sockets in bytes, based on number of allocated pages and system page size. Corresponds to limits set in /proc/sys/net/ipv4/tcp_mem. Only available on Linux. + - name: all + type: group + description: > + All TCP connections + fields: + - name: orphan + type: integer + description: > + A count of all orphaned tcp sockets. Only available on Linux. + - name: count + type: integer + description: > + All open TCP connections + - name: listening + type: integer + description: > + All TCP listening ports + - name: established + type: integer + description: > + Number of established TCP connections + - name: close_wait + type: integer + description: > + Number of TCP connections in _close_wait_ state + - name: time_wait + type: integer + description: > + Number of TCP connections in _time_wait_ state + - name: syn_sent + type: integer + description: > + Number of TCP connections in _syn_sent_ state + - name: syn_recv + type: integer + description: > + Number of TCP connections in _syn_recv_ state + - name: fin_wait1 + type: integer + description: > + Number of TCP connections in _fin_wait1_ state + - name: fin_wait2 + type: integer + description: > + Number of TCP connections in _fin_wait2_ state + - name: last_ack + type: integer + description: > + Number of TCP connections in _last_ack_ state + - name: closing + type: integer + description: > + Number of TCP connections in _closing_ state + - name: udp + type: group + description: > + All UDP connections + fields: + - name: memory + type: integer + format: bytes + description: > + Memory used by UDP sockets in bytes, based on number of allocated pages and system page size. Corresponds to limits set in /proc/sys/net/ipv4/udp_mem. Only available on Linux. + - name: all + type: group + description: > + All UDP connections + fields: + - name: count + type: integer + description: > + All open UDP connections +- name: system.uptime + type: group + description: > + `uptime` contains the operating system uptime metric. + release: ga + fields: + - name: duration.ms + type: long + format: duration + input_format: milliseconds + description: > + The OS uptime in milliseconds. +- name: system.users + type: group + release: beta + description: > + Logged-in user session data + fields: + - name: id + type: keyword + description: > + The ID of the session + - name: seat + type: keyword + description: > + An associated logind seat + - name: path + type: keyword + description: > + The DBus object path of the session + - name: type + type: keyword + description: > + The type of the user session + - name: service + type: keyword + description: > + A session associated with the service + - name: remote + type: boolean + description: > + A bool indicating a remote session + - name: state + type: keyword + description: > + The current state of the session + - name: scope + type: keyword + description: > + The associated systemd scope + - name: leader + type: long + description: > + The root PID of the session + - name: remote_host + type: keyword + description: > + A remote host address for the session + + + + + 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 1f11136360465..7aecc408e05fe 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 @@ -95,10 +95,7 @@ export async function installIndexPatterns( // if this is an update because a package is being unisntalled (no pkgkey argument passed) and no other packages are installed, remove the index pattern if (!pkgkey && installedPackages.length === 0) { try { - await savedObjectsClient.delete( - INDEX_PATTERN_SAVED_OBJECT_TYPE, - `epm-ip-${indexPatternType}` - ); + await savedObjectsClient.delete(INDEX_PATTERN_SAVED_OBJECT_TYPE, `${indexPatternType}-*`); } catch (err) { // index pattern was probably deleted by the user already } @@ -111,7 +108,7 @@ export async function installIndexPatterns( const kibanaIndexPattern = createIndexPattern(indexPatternType, fields); // create or overwrite the index pattern await savedObjectsClient.create(INDEX_PATTERN_SAVED_OBJECT_TYPE, kibanaIndexPattern, { - id: `epm-ip-${indexPatternType}`, + id: `${indexPatternType}-*`, overwrite: true, }); }); diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap rename to x-pack/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/license_status.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/license_status.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/__snapshots__/license_status.test.js.snap rename to x-pack/plugins/license_management/__jest__/__snapshots__/license_status.test.js.snap diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap rename to x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap rename to x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap diff --git a/x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap rename to x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap new file mode 100644 index 0000000000000..5a7d136180808 --- /dev/null +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap @@ -0,0 +1,2891 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UploadLicense should display a modal when license requires acknowledgement 1`] = ` + + + + + + +
+ +
+ +

+ + Upload your license + +

+
+ +
+ + + +
+
+
+
+ +
+
+
+ Confirm License Upload +
+
+
+
+
+
+
+ Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. +
+
+
    +
  • + Watcher will be disabled +
  • +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ } + > + + } + confirmButtonText={ + + } + onCancel={[Function]} + onConfirm={[Function]} + title={ + + } + > + + + +
+
+
+ +
+ +
+
+
+ Confirm License Upload +
+
+
+
+
+
+
+ Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. +
+
+
    +
  • + Watcher will be disabled +
  • +
+
+
+
+
+
+
+ + +
+
+
+
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + > + +
+ +
+
+
+ Confirm License Upload +
+
+
+
+
+
+
+ Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. +
+
+
    +
  • + Watcher will be disabled +
  • +
+
+
+
+
+
+
+ + +
+
+
+
+ } + onActivation={[Function]} + onDeactivation={[Function]} + persistentFocus={false} + /> + +
+ + + + + +
+ +
+ +
+ + Confirm License Upload + +
+
+
+
+ +
+
+ +
+
+ +
+ Some functionality will be lost if you replace your TRIAL license with a BASIC license. Review the list of features below. +
+
+ +
+
    +
  • + Watcher will be disabled +
  • +
+
+
+
+
+
+
+
+
+ +
+ + + + + + +
+
+
+
+
+
+ + + + + + + +
+

+ + Your license key is a JSON file with a signature attached. + +

+

+ + + , + } + } + > + Uploading a license will replace your current + + license. + +

+
+
+ +
+ + +
+ +
+ +
+ +
+ + } + onChange={[Function]} + > + +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + + + +`; + +exports[`UploadLicense should display an error when ES says license is expired 1`] = ` + + + + + + +
+ +
+ +

+ + Upload your license + +

+
+ +
+ + +
+

+ + Your license key is a JSON file with a signature attached. + +

+

+ + + , + } + } + > + Uploading a license will replace your current + + license. + +

+
+ + +
+ + +
+ + +
+
+ + Please address the errors in your form. + +
+ +
+
    +
  • + The supplied license has expired. +
  • +
+
+
+
+
+
+ +
+ +
+ +
+ + } + onChange={[Function]} + > + +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + + + +`; + +exports[`UploadLicense should display an error when ES says license is invalid 1`] = ` + + + + + + +
+ +
+ +

+ + Upload your license + +

+
+ +
+ + +
+

+ + Your license key is a JSON file with a signature attached. + +

+

+ + + , + } + } + > + Uploading a license will replace your current + + license. + +

+
+ + +
+ + +
+ + +
+
+ + Please address the errors in your form. + +
+ +
+
    +
  • + The supplied license is not valid for this product. +
  • +
+
+
+
+
+
+ +
+ +
+ +
+ + } + onChange={[Function]} + > + +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + + + +`; + +exports[`UploadLicense should display an error when submitting invalid JSON 1`] = ` + + + + + + +
+ +
+ +

+ + Upload your license + +

+
+ +
+ + +
+

+ + Your license key is a JSON file with a signature attached. + +

+

+ + + , + } + } + > + Uploading a license will replace your current + + license. + +

+
+ + +
+ + +
+ + +
+
+ + Please address the errors in your form. + +
+ +
+
    +
  • + Error encountered uploading license: Check your license file. +
  • +
+
+
+
+
+
+ +
+ +
+ +
+ + } + onChange={[Function]} + > + +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + + + +`; + +exports[`UploadLicense should display error when ES returns error 1`] = ` + + + + + + +
+ +
+ +

+ + Upload your license + +

+
+ +
+ + +
+

+ + Your license key is a JSON file with a signature attached. + +

+

+ + + , + } + } + > + Uploading a license will replace your current + + license. + +

+
+ + +
+ + +
+ + +
+
+ + Please address the errors in your form. + +
+ +
+
    +
  • + Error encountered uploading license: Can not upgrade to a production license unless TLS is configured or security is disabled +
  • +
+
+
+
+
+
+ +
+ +
+ +
+ + } + onChange={[Function]} + > + +
+
+ + + +
+
+
+ + +
+ +
+ +
+ + +
+ + +
+ + +
+ + + + +
+ + + +
+
+
+
+
+ +
+ +
+ + + + + + +`; diff --git a/x-pack/legacy/plugins/license_management/__jest__/add_license.test.js b/x-pack/plugins/license_management/__jest__/add_license.test.js similarity index 90% rename from x-pack/legacy/plugins/license_management/__jest__/add_license.test.js rename to x-pack/plugins/license_management/__jest__/add_license.test.js index 6ffb43025ff59..070d4df98a90a 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/add_license.test.js +++ b/x-pack/plugins/license_management/__jest__/add_license.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AddLicense } from '../public/np_ready/application/sections/license_dashboard/add_license'; +import { AddLicense } from '../public/application/sections/license_dashboard/add_license'; import { createMockLicense, getComponent } from './util'; jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); diff --git a/x-pack/legacy/plugins/license_management/__jest__/api_responses/index.js b/x-pack/plugins/license_management/__jest__/api_responses/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/api_responses/index.js rename to x-pack/plugins/license_management/__jest__/api_responses/index.js diff --git a/x-pack/legacy/plugins/license_management/__jest__/api_responses/upload_license.js b/x-pack/plugins/license_management/__jest__/api_responses/upload_license.js similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/api_responses/upload_license.js rename to x-pack/plugins/license_management/__jest__/api_responses/upload_license.js diff --git a/x-pack/legacy/plugins/license_management/__jest__/license_status.test.js b/x-pack/plugins/license_management/__jest__/license_status.test.js similarity index 88% rename from x-pack/legacy/plugins/license_management/__jest__/license_status.test.js rename to x-pack/plugins/license_management/__jest__/license_status.test.js index f44d5c1f138b7..dc7dc7d00f49e 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/license_status.test.js +++ b/x-pack/plugins/license_management/__jest__/license_status.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LicenseStatus } from '../public/np_ready/application/sections/license_dashboard/license_status'; +import { LicenseStatus } from '../public/application/sections/license_dashboard/license_status'; import { createMockLicense, getComponent } from './util'; describe('LicenseStatus component', () => { diff --git a/x-pack/legacy/plugins/license_management/__jest__/request_trial_extension.test.js b/x-pack/plugins/license_management/__jest__/request_trial_extension.test.js similarity index 96% rename from x-pack/legacy/plugins/license_management/__jest__/request_trial_extension.test.js rename to x-pack/plugins/license_management/__jest__/request_trial_extension.test.js index a74a7b16185c6..6d5a9fdd3fb38 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/request_trial_extension.test.js +++ b/x-pack/plugins/license_management/__jest__/request_trial_extension.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestTrialExtension } from '../public/np_ready/application/sections/license_dashboard/request_trial_extension'; +import { RequestTrialExtension } from '../public/application/sections/license_dashboard/request_trial_extension'; import { createMockLicense, getComponent } from './util'; jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); diff --git a/x-pack/legacy/plugins/license_management/__jest__/revert_to_basic.test.js b/x-pack/plugins/license_management/__jest__/revert_to_basic.test.js similarity index 94% rename from x-pack/legacy/plugins/license_management/__jest__/revert_to_basic.test.js rename to x-pack/plugins/license_management/__jest__/revert_to_basic.test.js index 488279d87ece0..c223c39a8f12c 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/revert_to_basic.test.js +++ b/x-pack/plugins/license_management/__jest__/revert_to_basic.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RevertToBasic } from '../public/np_ready/application/sections/license_dashboard/revert_to_basic'; +import { RevertToBasic } from '../public/application/sections/license_dashboard/revert_to_basic'; import { createMockLicense, getComponent } from './util'; jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); diff --git a/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js b/x-pack/plugins/license_management/__jest__/start_trial.test.js similarity index 96% rename from x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js rename to x-pack/plugins/license_management/__jest__/start_trial.test.js index 5436a51a2632b..5bd005bc1adbd 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/start_trial.test.js +++ b/x-pack/plugins/license_management/__jest__/start_trial.test.js @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { StartTrial } from '../public/np_ready/application/sections/license_dashboard/start_trial'; +import { StartTrial } from '../public/application/sections/license_dashboard/start_trial'; import { createMockLicense, getComponent } from './util'; -jest.mock('ui/new_platform'); + jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); describe('StartTrial component when trial is allowed', () => { diff --git a/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx b/x-pack/plugins/license_management/__jest__/upload_license.test.tsx similarity index 55% rename from x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx rename to x-pack/plugins/license_management/__jest__/upload_license.test.tsx index ca9b5b0db9ca1..ad2fbd288e9f4 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/upload_license.test.tsx +++ b/x-pack/plugins/license_management/__jest__/upload_license.test.tsx @@ -4,21 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { httpServiceMock, chromeServiceMock } from '../../../../../src/core/public/mocks'; -import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; import React from 'react'; import { Provider } from 'react-redux'; - -jest.mock('ui/new_platform'); +import { httpServiceMock } from '../../../../src/core/public/mocks'; +import { mountWithIntl } from '../../../test_utils/enzyme_helpers'; // @ts-ignore -import { uploadLicense } from '../public/np_ready/application/store/actions/upload_license'; +import { uploadLicense } from '../public/application/store/actions/upload_license'; // @ts-ignore -import { licenseManagementStore } from '../public/np_ready/application/store/store'; +import { licenseManagementStore } from '../public/application/store/store'; // @ts-ignore -import { UploadLicense } from '../public/np_ready/application/sections/upload_license'; +import { UploadLicense } from '../public/application/sections/upload_license'; +import { AppContextProvider } from '../public/application/app_context'; import { UPLOAD_LICENSE_EXPIRED, @@ -33,36 +32,43 @@ window.location.reload = () => {}; let store: any = null; let component: any = null; -const services = { - legacy: { - xPackInfo: { + +const appDependencies = { + plugins: { + licensing: { refresh: jest.fn(), - get: () => { - return { license: { type: 'basic' } }; - }, }, - refreshXpack: jest.fn(), }, + docLinks: {}, +}; + +const thunkServices = { http: httpServiceMock.createSetupContract(), - chrome: chromeServiceMock.createStartContract(), history: { replace: jest.fn(), }, + breadcrumbService: { + setBreadcrumbs() {}, + }, + licensing: appDependencies.plugins.licensing, }; describe('UploadLicense', () => { beforeEach(() => { - store = licenseManagementStore({}, services); + store = licenseManagementStore({}, thunkServices); component = ( - - - + + + + + ); + appDependencies.plugins.licensing.refresh.mockResolvedValue({}); }); afterEach(() => { - services.legacy.xPackInfo.refresh.mockReset(); - services.history.replace.mockReset(); + appDependencies.plugins.licensing.refresh.mockReset(); + thunkServices.history.replace.mockReset(); jest.clearAllMocks(); }); @@ -74,46 +80,46 @@ describe('UploadLicense', () => { }); it('should display an error when ES says license is invalid', async () => { - services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_INVALID[2])); + thunkServices.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_INVALID[2])); const rendered = mountWithIntl(component); const invalidLicense = JSON.stringify({ license: { type: 'basic' } }); - await uploadLicense(invalidLicense)(store.dispatch, null, services); + await uploadLicense(invalidLicense)(store.dispatch, null, thunkServices); rendered.update(); expect(rendered).toMatchSnapshot(); }); it('should display an error when ES says license is expired', async () => { - services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_EXPIRED[2])); + thunkServices.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_EXPIRED[2])); const rendered = mountWithIntl(component); const invalidLicense = JSON.stringify({ license: { type: 'basic' } }); - await uploadLicense(invalidLicense)(store.dispatch, null, services); + await uploadLicense(invalidLicense)(store.dispatch, null, thunkServices); rendered.update(); expect(rendered).toMatchSnapshot(); }); it('should display a modal when license requires acknowledgement', async () => { - services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_REQUIRES_ACK[2])); + thunkServices.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_REQUIRES_ACK[2])); const unacknowledgedLicense = JSON.stringify({ license: { type: 'basic' }, }); - await uploadLicense(unacknowledgedLicense, 'trial')(store.dispatch, null, services); + await uploadLicense(unacknowledgedLicense, 'trial')(store.dispatch, null, thunkServices); const rendered = mountWithIntl(component); expect(rendered).toMatchSnapshot(); }); it('should refresh xpack info and navigate to BASE_PATH when ES accepts new license', async () => { - services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_SUCCESS[2])); + thunkServices.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_SUCCESS[2])); const validLicense = JSON.stringify({ license: { type: 'basic' } }); - await uploadLicense(validLicense)(store.dispatch, null, services); - expect(services.legacy.refreshXpack).toHaveBeenCalled(); - expect(services.history.replace).toHaveBeenCalled(); + await uploadLicense(validLicense)(store.dispatch, null, thunkServices); + expect(appDependencies.plugins.licensing.refresh).toHaveBeenCalled(); + expect(thunkServices.history.replace).toHaveBeenCalled(); }); it('should display error when ES returns error', async () => { - services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_TLS_NOT_ENABLED[2])); + thunkServices.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_TLS_NOT_ENABLED[2])); const rendered = mountWithIntl(component); const license = JSON.stringify({ license: { type: 'basic' } }); - await uploadLicense(license)(store.dispatch, null, services); + await uploadLicense(license)(store.dispatch, null, thunkServices); rendered.update(); expect(rendered).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/license_management/__jest__/util/index.js b/x-pack/plugins/license_management/__jest__/util/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/__jest__/util/index.js rename to x-pack/plugins/license_management/__jest__/util/index.js diff --git a/x-pack/legacy/plugins/license_management/__jest__/util/util.js b/x-pack/plugins/license_management/__jest__/util/util.js similarity index 60% rename from x-pack/legacy/plugins/license_management/__jest__/util/util.js rename to x-pack/plugins/license_management/__jest__/util/util.js index 93b97c51b24da..5a7e49c8c3315 100644 --- a/x-pack/legacy/plugins/license_management/__jest__/util/util.js +++ b/x-pack/plugins/license_management/__jest__/util/util.js @@ -3,15 +3,22 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ -import { Provider } from 'react-redux'; -import { licenseManagementStore } from '../../public/np_ready/application/store/store'; import React from 'react'; -import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers'; -import { httpServiceMock } from '../../../../../../src/core/public/mocks'; +import { Provider } from 'react-redux'; + +import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; +import { httpServiceMock } from '../../../../../src/core/public/mocks'; +import { licenseManagementStore } from '../../public/application/store/store'; +import { AppContextProvider } from '../../public/application/app_context'; const highExpirationMillis = new Date('October 13, 2099 00:00:00Z').getTime(); +const appDependencies = { + docLinks: {}, +}; + export const createMockLicense = (type, expiryDateInMillis = highExpirationMillis) => { return { type, @@ -19,14 +26,17 @@ export const createMockLicense = (type, expiryDateInMillis = highExpirationMilli isActive: new Date().getTime() < expiryDateInMillis, }; }; + export const getComponent = (initialState, Component) => { const services = { http: httpServiceMock.createSetupContract(), }; const store = licenseManagementStore(initialState, services); return mountWithIntl( - - - + + + + + ); }; diff --git a/x-pack/legacy/plugins/license_management/__mocks__/focus-trap-react.js b/x-pack/plugins/license_management/__mocks__/focus-trap-react.js similarity index 100% rename from x-pack/legacy/plugins/license_management/__mocks__/focus-trap-react.js rename to x-pack/plugins/license_management/__mocks__/focus-trap-react.js diff --git a/x-pack/legacy/plugins/license_management/common/constants/base_path.ts b/x-pack/plugins/license_management/common/constants/base_path.ts similarity index 87% rename from x-pack/legacy/plugins/license_management/common/constants/base_path.ts rename to x-pack/plugins/license_management/common/constants/base_path.ts index 9b24ab561dba8..7b981ec8727e6 100644 --- a/x-pack/legacy/plugins/license_management/common/constants/base_path.ts +++ b/x-pack/plugins/license_management/common/constants/base_path.ts @@ -5,3 +5,5 @@ */ export const BASE_PATH = '/management/elasticsearch/license_management/'; + +export const API_BASE_PATH = '/api/license'; diff --git a/x-pack/legacy/plugins/license_management/common/constants/external_links.ts b/x-pack/plugins/license_management/common/constants/external_links.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/common/constants/external_links.ts rename to x-pack/plugins/license_management/common/constants/external_links.ts diff --git a/x-pack/legacy/plugins/license_management/common/constants/index.ts b/x-pack/plugins/license_management/common/constants/index.ts similarity index 87% rename from x-pack/legacy/plugins/license_management/common/constants/index.ts rename to x-pack/plugins/license_management/common/constants/index.ts index c115fb7b69c0e..ec411fea4b7a9 100644 --- a/x-pack/legacy/plugins/license_management/common/constants/index.ts +++ b/x-pack/plugins/license_management/common/constants/index.ts @@ -5,6 +5,6 @@ */ export { PLUGIN } from './plugin'; -export { BASE_PATH } from './base_path'; +export { BASE_PATH, API_BASE_PATH } from './base_path'; export { EXTERNAL_LINKS } from './external_links'; export { APP_PERMISSION } from './permissions'; diff --git a/x-pack/legacy/plugins/license_management/common/constants/permissions.ts b/x-pack/plugins/license_management/common/constants/permissions.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/common/constants/permissions.ts rename to x-pack/plugins/license_management/common/constants/permissions.ts diff --git a/x-pack/legacy/plugins/license_management/common/constants/plugin.ts b/x-pack/plugins/license_management/common/constants/plugin.ts similarity index 79% rename from x-pack/legacy/plugins/license_management/common/constants/plugin.ts rename to x-pack/plugins/license_management/common/constants/plugin.ts index 14b591e3834ef..406ac867a77b5 100644 --- a/x-pack/legacy/plugins/license_management/common/constants/plugin.ts +++ b/x-pack/plugins/license_management/common/constants/plugin.ts @@ -6,8 +6,8 @@ import { i18n } from '@kbn/i18n'; export const PLUGIN = { - TITLE: i18n.translate('xpack.licenseMgmt.managementSectionDisplayName', { + title: i18n.translate('xpack.licenseMgmt.managementSectionDisplayName', { defaultMessage: 'License Management', }), - ID: 'license_management', + id: 'license_management', }; diff --git a/x-pack/plugins/license_management/kibana.json b/x-pack/plugins/license_management/kibana.json new file mode 100644 index 0000000000000..be28c8e978d8a --- /dev/null +++ b/x-pack/plugins/license_management/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "licenseManagement", + "version": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["home", "licensing", "management"], + "optionalPlugins": ["telemetry"], + "configPath": ["xpack", "license_management"] +} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/_license_management.scss b/x-pack/plugins/license_management/public/application/_license_management.scss similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/_license_management.scss rename to x-pack/plugins/license_management/public/application/_license_management.scss diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/app.container.js b/x-pack/plugins/license_management/public/application/app.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/app.container.js rename to x-pack/plugins/license_management/public/application/app.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js b/x-pack/plugins/license_management/public/application/app.js similarity index 97% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/app.js rename to x-pack/plugins/license_management/public/application/app.js index 6a6c38fa6abb6..1bc8e9cd563e2 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/app.js +++ b/x-pack/plugins/license_management/public/application/app.js @@ -8,7 +8,7 @@ import React, { Component } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { LicenseDashboard, UploadLicense } from './sections'; import { Switch, Route } from 'react-router-dom'; -import { APP_PERMISSION, BASE_PATH } from '../../../common/constants'; +import { APP_PERMISSION, BASE_PATH } from '../../common/constants'; import { EuiPageBody, EuiEmptyPrompt, EuiText, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui'; export class App extends Component { diff --git a/x-pack/plugins/license_management/public/application/app_context.tsx b/x-pack/plugins/license_management/public/application/app_context.tsx new file mode 100644 index 0000000000000..1e90f4c907b8c --- /dev/null +++ b/x-pack/plugins/license_management/public/application/app_context.tsx @@ -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 React, { createContext, useContext } from 'react'; + +import { CoreStart } from '../../../../../src/core/public'; +import { LicensingPluginSetup, ILicense } from '../../../licensing/public'; +import { TelemetryPluginSetup } from '../../../../../src/plugins/telemetry/public'; +import { ClientConfigType } from '../types'; +import { BreadcrumbService } from './breadcrumbs'; + +const AppContext = createContext(undefined); + +export interface AppDependencies { + core: CoreStart; + services: { + breadcrumbService: BreadcrumbService; + }; + plugins: { + licensing: LicensingPluginSetup; + telemetry?: TelemetryPluginSetup; + }; + docLinks: { + security: string; + }; + store: { + initialLicense: ILicense; + }; + config: ClientConfigType; +} + +export const AppContextProvider = ({ + children, + value, +}: { + value: AppDependencies; + children: React.ReactNode; +}) => { + return {children}; +}; + +export const AppContextConsumer = AppContext.Consumer; + +export const useAppContext = () => { + const ctx = useContext(AppContext); + if (!ctx) { + throw new Error('"useAppContext" can only be called inside of AppContext.Provider!'); + } + return ctx; +}; diff --git a/x-pack/plugins/license_management/public/application/app_providers.tsx b/x-pack/plugins/license_management/public/application/app_providers.tsx new file mode 100644 index 0000000000000..9f9fd2a8275df --- /dev/null +++ b/x-pack/plugins/license_management/public/application/app_providers.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import * as history from 'history'; +import { Provider } from 'react-redux'; + +import { BASE_PATH } from '../../common/constants'; +import { AppContextProvider, AppDependencies } from './app_context'; +// @ts-ignore +import { licenseManagementStore } from './store'; + +interface Props { + appDependencies: AppDependencies; + children: React.ReactNode; +} + +export const AppProviders = ({ appDependencies, children }: Props) => { + const { + core, + plugins, + services, + store: { initialLicense }, + } = appDependencies; + + const { + http, + notifications: { toasts }, + i18n: { Context: I18nContext }, + } = core; + + // Setup Redux store + const thunkServices = { + // So we can imperatively control the hash route + history: history.createHashHistory({ basename: BASE_PATH }), + toasts, + http, + telemetry: plugins.telemetry, + licensing: plugins.licensing, + breadcrumbService: services.breadcrumbService, + }; + const initialState = { license: initialLicense }; + + const store = licenseManagementStore(initialState, thunkServices); + + return ( + + + {children} + + + ); +}; diff --git a/x-pack/plugins/license_management/public/application/breadcrumbs.ts b/x-pack/plugins/license_management/public/application/breadcrumbs.ts new file mode 100644 index 0000000000000..b1773a10f01ba --- /dev/null +++ b/x-pack/plugins/license_management/public/application/breadcrumbs.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 { i18n } from '@kbn/i18n'; + +import { ManagementAppMountParams } from '../../../../../src/plugins/management/public'; +import { BASE_PATH } from '../../common/constants'; + +type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs']; + +export class BreadcrumbService { + private breadcrumbs: { + [key: string]: Array<{ + text: string; + href?: string; + }>; + } = { + dashboard: [], + upload: [], + }; + private setBreadcrumbsHandler?: SetBreadcrumbs; + + public setup(setBreadcrumbsHandler: SetBreadcrumbs): void { + this.setBreadcrumbsHandler = setBreadcrumbsHandler; + + // Home and sections + this.breadcrumbs.dashboard = [ + { + text: i18n.translate('xpack.licenseMgmt.dashboard.breadcrumb', { + defaultMessage: 'License management', + }), + href: `#${BASE_PATH}home`, + }, + ]; + + this.breadcrumbs.upload = [ + ...this.breadcrumbs.dashboard, + { + text: i18n.translate('xpack.licenseMgmt.upload.breadcrumb', { + defaultMessage: 'Upload', + }), + }, + ]; + } + + public setBreadcrumbs(type: 'dashboard' | 'upload'): void { + if (!this.setBreadcrumbsHandler) { + throw new Error(`BreadcrumbService#setup() must be called first!`); + } + + const newBreadcrumbs = this.breadcrumbs[type] + ? [...this.breadcrumbs[type]] + : [...this.breadcrumbs.home]; + + // Pop off last breadcrumb + const lastBreadcrumb = newBreadcrumbs.pop() as { + text: string; + href?: string; + }; + + // Put last breadcrumb back without href + newBreadcrumbs.push({ + ...lastBreadcrumb, + href: undefined, + }); + + this.setBreadcrumbsHandler(newBreadcrumbs); + } +} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.ts b/x-pack/plugins/license_management/public/application/components/telemetry_opt_in/index.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/index.ts rename to x-pack/plugins/license_management/public/application/components/telemetry_opt_in/index.ts diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx b/x-pack/plugins/license_management/public/application/components/telemetry_opt_in/telemetry_opt_in.tsx similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/components/telemetry_opt_in/telemetry_opt_in.tsx rename to x-pack/plugins/license_management/public/application/components/telemetry_opt_in/telemetry_opt_in.tsx diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/index.scss b/x-pack/plugins/license_management/public/application/index.scss similarity index 63% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/index.scss rename to x-pack/plugins/license_management/public/application/index.scss index 4fb8aafcca93c..92150eea40219 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/index.scss +++ b/x-pack/plugins/license_management/public/application/index.scss @@ -1,7 +1,4 @@ -// EUI globals -@import 'src/legacy/ui/public/styles/styling_constants'; - -// License amnagement plugin styles +// License management plugin styles // Prefix all styles with "lic" to avoid conflicts. // Examples diff --git a/x-pack/plugins/license_management/public/application/index.tsx b/x-pack/plugins/license_management/public/application/index.tsx new file mode 100644 index 0000000000000..75f2f98f51e6e --- /dev/null +++ b/x-pack/plugins/license_management/public/application/index.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { HashRouter } from 'react-router-dom'; + +import { AppDependencies } from './app_context'; +import { AppProviders } from './app_providers'; +// @ts-ignore +import { App } from './app.container'; + +const AppWithRouter = (props: { [key: string]: any }) => ( + + + +); + +export const renderApp = (element: Element, dependencies: AppDependencies) => { + render( + + + , + element + ); + + return () => { + unmountComponentAtNode(element); + }; +}; + +export { AppDependencies }; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/es.ts b/x-pack/plugins/license_management/public/application/lib/es.ts similarity index 79% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/lib/es.ts rename to x-pack/plugins/license_management/public/application/lib/es.ts index 3924de2202d51..52df5c2509226 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/es.ts +++ b/x-pack/plugins/license_management/public/application/lib/es.ts @@ -5,11 +5,10 @@ */ import { HttpSetup } from 'src/core/public'; - -const BASE_PATH = '/api/license'; +import { API_BASE_PATH } from '../../../common/constants'; export function putLicense(http: HttpSetup, license: string, acknowledge: boolean) { - return http.put(BASE_PATH, { + return http.put(API_BASE_PATH, { query: { acknowledge: acknowledge ? 'true' : '', }, @@ -22,7 +21,7 @@ export function putLicense(http: HttpSetup, license: string, acknowledge: boolea } export function startBasic(http: HttpSetup, acknowledge: boolean) { - return http.post(`${BASE_PATH}/start_basic`, { + return http.post(`${API_BASE_PATH}/start_basic`, { query: { acknowledge: acknowledge ? 'true' : '', }, @@ -35,7 +34,7 @@ export function startBasic(http: HttpSetup, acknowledge: boolean) { } export function startTrial(http: HttpSetup) { - return http.post(`${BASE_PATH}/start_trial`, { + return http.post(`${API_BASE_PATH}/start_trial`, { headers: { contentType: 'application/json', }, @@ -44,7 +43,7 @@ export function startTrial(http: HttpSetup) { } export function canStartTrial(http: HttpSetup) { - return http.get(`${BASE_PATH}/start_trial`, { + return http.get(`${API_BASE_PATH}/start_trial`, { headers: { contentType: 'application/json', }, @@ -53,7 +52,7 @@ export function canStartTrial(http: HttpSetup) { } export function getPermissions(http: HttpSetup) { - return http.post(`${BASE_PATH}/permissions`, { + return http.post(`${API_BASE_PATH}/permissions`, { headers: { contentType: 'application/json', }, diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts b/x-pack/plugins/license_management/public/application/lib/telemetry.ts similarity index 69% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts rename to x-pack/plugins/license_management/public/application/lib/telemetry.ts index 9cc4ec5978fdc..1d90fce6f6b9a 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/telemetry.ts +++ b/x-pack/plugins/license_management/public/application/lib/telemetry.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TelemetryPluginSetup } from '../../../../../../../../src/plugins/telemetry/public'; +import { TelemetryPluginSetup } from '../../../../../../src/plugins/telemetry/public'; -export { OptInExampleFlyout } from '../../../../../../../../src/plugins/telemetry/public/components'; -export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/plugins/telemetry/common/constants'; +export { OptInExampleFlyout } from '../../../../../../src/plugins/telemetry/public/components'; +export { PRIVACY_STATEMENT_URL } from '../../../../../../src/plugins/telemetry/common/constants'; export { TelemetryPluginSetup, shouldShowTelemetryOptIn }; function shouldShowTelemetryOptIn( diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/index.js b/x-pack/plugins/license_management/public/application/sections/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/index.js rename to x-pack/plugins/license_management/public/application/sections/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/add_license/add_license.js similarity index 94% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/add_license/add_license.js index d2f44bfc701f7..158702e1286ae 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license.js +++ b/x-pack/plugins/license_management/public/application/sections/license_dashboard/add_license/add_license.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { BASE_PATH } from '../../../../../../common/constants'; +import { BASE_PATH } from '../../../../../common/constants'; import { EuiCard, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/add_license/index.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/add_license/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/add_license/index.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/add_license/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/index.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/index.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.container.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/license_dashboard.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.container.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/license_dashboard.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/license_dashboard.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_dashboard.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/license_dashboard.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/index.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/index.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status.container.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/license_status.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status.container.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/license_status.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/license_status.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/license_status/license_status.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/index.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/index.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/request_trial_extension.container.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/request_trial_extension.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/request_trial_extension.container.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/request_trial_extension.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js similarity index 96% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js index fae454cbaac50..fb1ea026abaa0 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js +++ b/x-pack/plugins/license_management/public/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js @@ -8,7 +8,7 @@ import React from 'react'; import { EuiFlexItem, EuiCard, EuiLink, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EXTERNAL_LINKS } from '../../../../../../common/constants'; +import { EXTERNAL_LINKS } from '../../../../../common/constants'; export const RequestTrialExtension = ({ shouldShowRequestTrialExtension }) => { if (!shouldShowRequestTrialExtension) { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/index.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/index.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/revert_to_basic.container.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/revert_to_basic.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/revert_to_basic.container.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/revert_to_basic.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js similarity index 98% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js index 9115e82833ee7..2424e336fe6e6 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js +++ b/x-pack/plugins/license_management/public/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js @@ -16,7 +16,7 @@ import { EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EXTERNAL_LINKS } from '../../../../../../common/constants'; +import { EXTERNAL_LINKS } from '../../../../../common/constants'; export class RevertToBasic extends React.PureComponent { cancel = () => { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.ts b/x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/index.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/index.ts rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/index.ts diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js b/x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/start_trial.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.container.js rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/start_trial.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx b/x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/start_trial.tsx similarity index 92% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx rename to x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/start_trial.tsx index e0f8ade8e45da..25cbfb7242239 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/license_dashboard/start_trial/start_trial.tsx +++ b/x-pack/plugins/license_management/public/application/sections/license_dashboard/start_trial/start_trial.tsx @@ -24,8 +24,8 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { TelemetryOptIn } from '../../../components/telemetry_opt_in'; -import { EXTERNAL_LINKS } from '../../../../../../common/constants'; -import { getDocLinks } from '../../../lib/docs_links'; +import { EXTERNAL_LINKS } from '../../../../../common/constants'; +import { AppContextConsumer, AppDependencies } from '../../../app_context'; import { TelemetryPluginSetup, shouldShowTelemetryOptIn } from '../../../lib/telemetry'; interface Props { @@ -68,7 +68,7 @@ export class StartTrial extends Component { cancel = () => { this.setState({ showConfirmation: false }); }; - acknowledgeModal() { + acknowledgeModal(docLinks: AppDependencies['docLinks']) { const { showConfirmation, isOptingInToTelemetry } = this.state; const { telemetry } = this.props; @@ -148,7 +148,7 @@ export class StartTrial extends Component { values={{ authenticationTypeList: 'AD/LDAP, SAML, PKI, SAML/SSO', securityDocumentationLinkText: ( - + { ); return ( - - {this.acknowledgeModal()} - + {dependencies => ( + + {this.acknowledgeModal(dependencies!.docLinks)} + + } + description={description} + footer={footer} /> - } - description={description} - footer={footer} - /> - + + )} + ); } } diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/index.js b/x-pack/plugins/license_management/public/application/sections/upload_license/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/index.js rename to x-pack/plugins/license_management/public/application/sections/upload_license/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js b/x-pack/plugins/license_management/public/application/sections/upload_license/upload_license.container.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.container.js rename to x-pack/plugins/license_management/public/application/sections/upload_license/upload_license.container.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js b/x-pack/plugins/license_management/public/application/sections/upload_license/upload_license.js similarity index 99% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js rename to x-pack/plugins/license_management/public/application/sections/upload_license/upload_license.js index e8dd9495a8c2d..49f2474f83911 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/sections/upload_license/upload_license.js +++ b/x-pack/plugins/license_management/public/application/sections/upload_license/upload_license.js @@ -5,7 +5,7 @@ */ import React, { Fragment } from 'react'; -import { BASE_PATH } from '../../../../../common/constants'; +import { BASE_PATH } from '../../../../common/constants'; import { EuiButton, EuiButtonEmpty, diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/add_error_message.js b/x-pack/plugins/license_management/public/application/store/actions/add_error_message.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/add_error_message.js rename to x-pack/plugins/license_management/public/application/store/actions/add_error_message.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/add_license.js b/x-pack/plugins/license_management/public/application/store/actions/add_license.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/add_license.js rename to x-pack/plugins/license_management/public/application/store/actions/add_license.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/index.js b/x-pack/plugins/license_management/public/application/store/actions/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/index.js rename to x-pack/plugins/license_management/public/application/store/actions/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/permissions.js b/x-pack/plugins/license_management/public/application/store/actions/permissions.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/permissions.js rename to x-pack/plugins/license_management/public/application/store/actions/permissions.js diff --git a/x-pack/plugins/license_management/public/application/store/actions/set_breadcrumb.ts b/x-pack/plugins/license_management/public/application/store/actions/set_breadcrumb.ts new file mode 100644 index 0000000000000..2c6a726203bc1 --- /dev/null +++ b/x-pack/plugins/license_management/public/application/store/actions/set_breadcrumb.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 { ThunkAction } from 'redux-thunk'; +import { BreadcrumbService } from '../../breadcrumbs'; + +export const setBreadcrumb = ( + section: 'dashboard' | 'upload' +): ThunkAction => ( + dispatch, + getState, + { breadcrumbService } +) => { + breadcrumbService.setBreadcrumbs(section); +}; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_basic.js b/x-pack/plugins/license_management/public/application/store/actions/start_basic.js similarity index 95% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_basic.js rename to x-pack/plugins/license_management/public/application/store/actions/start_basic.js index 5bc9e8fad07be..93c722c1f8968 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_basic.js +++ b/x-pack/plugins/license_management/public/application/store/actions/start_basic.js @@ -19,7 +19,7 @@ export const cancelStartBasicLicense = createAction( export const startBasicLicense = (currentLicenseType, ack) => async ( dispatch, getState, - { legacy: { refreshXpack }, toasts, http } + { licensing, toasts, http } ) => { /*eslint camelcase: 0*/ const { acknowledged, basic_was_started, error_message, acknowledge } = await startBasic( @@ -28,7 +28,7 @@ export const startBasicLicense = (currentLicenseType, ack) => async ( ); if (acknowledged) { if (basic_was_started) { - await refreshXpack(); + await licensing.refresh(); // reload necessary to get left nav to refresh with proper links window.location.reload(); } else { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_trial.js b/x-pack/plugins/license_management/public/application/store/actions/start_trial.js similarity index 85% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_trial.js rename to x-pack/plugins/license_management/public/application/store/actions/start_trial.js index c8ec538e846ec..3bae271b213c0 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/start_trial.js +++ b/x-pack/plugins/license_management/public/application/store/actions/start_trial.js @@ -14,15 +14,11 @@ export const loadTrialStatus = () => async (dispatch, getState, { http }) => { dispatch(trialStatusLoaded(trialOK)); }; -export const startLicenseTrial = () => async ( - dispatch, - getState, - { legacy: { refreshXpack }, toasts, http } -) => { +export const startLicenseTrial = () => async (dispatch, getState, { licensing, toasts, http }) => { /*eslint camelcase: 0*/ const { trial_was_started, error_message } = await startTrial(http); if (trial_was_started) { - await refreshXpack(); + await licensing.refresh(); // reload necessary to get left nav to refresh with proper links window.location.reload(); } else { diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/upload_license.js b/x-pack/plugins/license_management/public/application/store/actions/upload_license.js similarity index 96% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/upload_license.js rename to x-pack/plugins/license_management/public/application/store/actions/upload_license.js index 51b3af2b6308f..376a22d3d1efa 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/upload_license.js +++ b/x-pack/plugins/license_management/public/application/store/actions/upload_license.js @@ -24,7 +24,7 @@ const dispatchFromResponse = async ( dispatch, currentLicenseType, newLicenseType, - { history, legacy: { xPackInfo, refreshXpack } } + { history, licensing } ) => { const { error, acknowledged, license_status: licenseStatus, acknowledge } = response; if (error) { @@ -50,8 +50,8 @@ const dispatchFromResponse = async ( ) ); } else { - await refreshXpack(); - dispatch(addLicense(xPackInfo.get('license'))); + const updatedLicense = await licensing.refresh(); + dispatch(addLicense(updatedLicense)); dispatch(uploadLicenseStatus({})); history.replace('/home'); // reload necessary to get left nav to refresh with proper links diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/index.js b/x-pack/plugins/license_management/public/application/store/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/index.js rename to x-pack/plugins/license_management/public/application/store/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/index.js b/x-pack/plugins/license_management/public/application/store/reducers/index.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/index.js rename to x-pack/plugins/license_management/public/application/store/reducers/index.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/license.js b/x-pack/plugins/license_management/public/application/store/reducers/license.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/license.js rename to x-pack/plugins/license_management/public/application/store/reducers/license.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/license_management.js b/x-pack/plugins/license_management/public/application/store/reducers/license_management.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/license_management.js rename to x-pack/plugins/license_management/public/application/store/reducers/license_management.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/permissions.js b/x-pack/plugins/license_management/public/application/store/reducers/permissions.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/permissions.js rename to x-pack/plugins/license_management/public/application/store/reducers/permissions.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/start_basic_license_status.js b/x-pack/plugins/license_management/public/application/store/reducers/start_basic_license_status.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/start_basic_license_status.js rename to x-pack/plugins/license_management/public/application/store/reducers/start_basic_license_status.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/trial_status.js b/x-pack/plugins/license_management/public/application/store/reducers/trial_status.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/trial_status.js rename to x-pack/plugins/license_management/public/application/store/reducers/trial_status.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/upload_error_message.js b/x-pack/plugins/license_management/public/application/store/reducers/upload_error_message.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/upload_error_message.js rename to x-pack/plugins/license_management/public/application/store/reducers/upload_error_message.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/upload_status.js b/x-pack/plugins/license_management/public/application/store/reducers/upload_status.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/reducers/upload_status.js rename to x-pack/plugins/license_management/public/application/store/reducers/upload_status.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/store/store.js b/x-pack/plugins/license_management/public/application/store/store.js similarity index 100% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/store/store.js rename to x-pack/plugins/license_management/public/application/store/store.js diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/index.ts b/x-pack/plugins/license_management/public/index.ts similarity index 86% rename from x-pack/legacy/plugins/license_management/public/np_ready/index.ts rename to x-pack/plugins/license_management/public/index.ts index 59e2f02d8cb52..3c76549ebdc16 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/index.ts +++ b/x-pack/plugins/license_management/public/index.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { PluginInitializerContext } from 'src/core/public'; + import { LicenseManagementUIPlugin } from './plugin'; +import './application/index.scss'; -export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementUIPlugin(); +export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementUIPlugin(ctx); diff --git a/x-pack/plugins/license_management/public/plugin.ts b/x-pack/plugins/license_management/public/plugin.ts new file mode 100644 index 0000000000000..00d353bc97e04 --- /dev/null +++ b/x-pack/plugins/license_management/public/plugin.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { first } from 'rxjs/operators'; +import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; + +import { TelemetryPluginSetup } from '../../../../src/plugins/telemetry/public'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { LicensingPluginSetup } from '../../../plugins/licensing/public'; +import { PLUGIN } from '../common/constants'; +import { ClientConfigType } from './types'; +import { AppDependencies } from './application'; +import { BreadcrumbService } from './application/breadcrumbs'; + +interface PluginsDependencies { + management: ManagementSetup; + licensing: LicensingPluginSetup; + telemetry?: TelemetryPluginSetup; +} + +export class LicenseManagementUIPlugin implements Plugin { + private breadcrumbService = new BreadcrumbService(); + + constructor(private readonly initializerContext: PluginInitializerContext) {} + + setup(coreSetup: CoreSetup, plugins: PluginsDependencies) { + const config = this.initializerContext.config.get(); + + if (!config.ui.enabled) { + // No need to go any further + return; + } + + const { getStartServices } = coreSetup; + const { management, telemetry, licensing } = plugins; + + management.sections.getSection('elasticsearch')!.registerApp({ + id: PLUGIN.id, + title: PLUGIN.title, + order: 99, + mount: async ({ element, setBreadcrumbs }) => { + const [core] = await getStartServices(); + const initialLicense = await plugins.licensing.license$.pipe(first()).toPromise(); + + // Setup documentation links + const { docLinks } = core; + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; + const appDocLinks = { + security: `${esBase}/security-settings.html`, + }; + + // Setup services + this.breadcrumbService.setup(setBreadcrumbs); + + const appDependencies: AppDependencies = { + core, + config, + plugins: { + licensing, + telemetry, + }, + services: { + breadcrumbService: this.breadcrumbService, + }, + store: { + initialLicense, + }, + docLinks: appDocLinks, + }; + + const { renderApp } = await import('./application'); + + return renderApp(element, appDependencies); + }, + }); + } + + start() {} + stop() {} +} diff --git a/x-pack/legacy/plugins/license_management/public/legacy.ts b/x-pack/plugins/license_management/public/types.ts similarity index 78% rename from x-pack/legacy/plugins/license_management/public/legacy.ts rename to x-pack/plugins/license_management/public/types.ts index 0e7c3ae60c775..4213203bf42cc 100644 --- a/x-pack/legacy/plugins/license_management/public/legacy.ts +++ b/x-pack/plugins/license_management/public/types.ts @@ -4,5 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import './management_section'; -import './register_route'; +export interface ClientConfigType { + ui: { enabled: boolean }; +} diff --git a/x-pack/plugins/license_management/server/config.ts b/x-pack/plugins/license_management/server/config.ts new file mode 100644 index 0000000000000..9bc39204a7c31 --- /dev/null +++ b/x-pack/plugins/license_management/server/config.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 { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), +}); + +export type LicenseManagementConfig = TypeOf; diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/index.ts b/x-pack/plugins/license_management/server/index.ts similarity index 57% rename from x-pack/legacy/plugins/license_management/server/np_ready/index.ts rename to x-pack/plugins/license_management/server/index.ts index 2ad4143a94730..b378fffbce7e7 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/index.ts +++ b/x-pack/plugins/license_management/server/index.ts @@ -4,7 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/server'; +import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; + import { LicenseManagementServerPlugin } from './plugin'; +import { configSchema, LicenseManagementConfig } from './config'; export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementServerPlugin(); + +export const config: PluginConfigDescriptor = { + schema: configSchema, + exposeToBrowser: { + ui: true, + }, +}; diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/docs_links.ts b/x-pack/plugins/license_management/server/lib/is_es_error.ts similarity index 55% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/lib/docs_links.ts rename to x-pack/plugins/license_management/server/lib/is_es_error.ts index 761fcd2674df6..4137293cf39c0 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/lib/docs_links.ts +++ b/x-pack/plugins/license_management/server/lib/is_es_error.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -let docLinks: Record = {}; +import * as legacyElasticsearch from 'elasticsearch'; -export const setDocLinks = (links: Record) => { - docLinks = links; -}; +const esErrorsParent = legacyElasticsearch.errors._Abstract; -export const getDocLinks = () => docLinks; +export function isEsError(err: Error) { + return err instanceof esErrorsParent; +} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/lib/license.ts b/x-pack/plugins/license_management/server/lib/license.ts similarity index 50% rename from x-pack/legacy/plugins/license_management/server/np_ready/lib/license.ts rename to x-pack/plugins/license_management/server/lib/license.ts index b52c9d50170b9..d36365eb62a7e 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/lib/license.ts +++ b/x-pack/plugins/license_management/server/lib/license.ts @@ -3,29 +3,39 @@ * 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 'src/core/server'; -import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch'; +import { LicensingPluginSetup } from '../../../licensing/server'; +import { CallAsCurrentUser } from '../types'; + const getLicensePath = (acknowledge: boolean) => `/_license${acknowledge ? '?acknowledge=true' : ''}`; -export async function putLicense( - req: KibanaRequest, - elasticsearch: ElasticsearchPlugin, - xpackInfo: any -) { - const { acknowledge } = req.query; - const { callWithRequest } = elasticsearch.getCluster('admin'); +interface PutLicenseArg { + acknowledge: boolean; + callAsCurrentUser: CallAsCurrentUser; + licensing: LicensingPluginSetup; + license: { [key: string]: any }; +} + +export async function putLicense({ + acknowledge, + callAsCurrentUser, + licensing, + license, +}: PutLicenseArg) { const options = { method: 'POST', - path: getLicensePath(Boolean(acknowledge)), - body: req.body, + path: getLicensePath(acknowledge), + body: license, }; + try { - const response = await callWithRequest(req as any, 'transport.request', options); + const response = await callAsCurrentUser('transport.request', options); const { acknowledged, license_status: licenseStatus } = response; + if (acknowledged && licenseStatus === 'valid') { - await xpackInfo.refreshNow(); + await licensing.refresh(); } + return response; } catch (error) { return error.body; diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/lib/permissions.ts b/x-pack/plugins/license_management/server/lib/permissions.ts similarity index 53% rename from x-pack/legacy/plugins/license_management/server/np_ready/lib/permissions.ts rename to x-pack/plugins/license_management/server/lib/permissions.ts index 84cd92821797f..a1ecc2e7b4034 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/lib/permissions.ts +++ b/x-pack/plugins/license_management/server/lib/permissions.ts @@ -4,23 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest } from 'src/core/server'; -import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch'; +import { CallAsCurrentUser } from '../types'; -export async function getPermissions( - req: KibanaRequest, - elasticsearch: ElasticsearchPlugin, - xpackInfo: any -) { - const securityInfo = xpackInfo && xpackInfo.isAvailable() && xpackInfo.feature('security'); - if (!securityInfo || !securityInfo.isAvailable() || !securityInfo.isEnabled()) { +interface GetPermissionsArg { + isSecurityEnabled: boolean; + callAsCurrentUser: CallAsCurrentUser; +} + +export async function getPermissions({ isSecurityEnabled, callAsCurrentUser }: GetPermissionsArg) { + if (!isSecurityEnabled) { // If security isn't enabled, let the user use license management return { hasPermission: true, }; } - const { callWithRequest } = elasticsearch.getCluster('admin'); const options = { method: 'POST', path: '/_security/user/_has_privileges', @@ -30,7 +28,7 @@ export async function getPermissions( }; try { - const response = await callWithRequest(req as any, 'transport.request', options); + const response = await callAsCurrentUser('transport.request', options); return { hasPermission: response.cluster.manage, }; diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/lib/start_basic.ts b/x-pack/plugins/license_management/server/lib/start_basic.ts similarity index 50% rename from x-pack/legacy/plugins/license_management/server/np_ready/lib/start_basic.ts rename to x-pack/plugins/license_management/server/lib/start_basic.ts index ba042be132d68..d48192c6ca32e 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/lib/start_basic.ts +++ b/x-pack/plugins/license_management/server/lib/start_basic.ts @@ -3,29 +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 { KibanaRequest } from 'kibana/server'; -import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch'; +import { LicensingPluginSetup } from '../../../licensing/server'; +import { CallAsCurrentUser } from '../types'; const getStartBasicPath = (acknowledge: boolean) => `/_license/start_basic${acknowledge ? '?acknowledge=true' : ''}`; -export async function startBasic( - req: KibanaRequest, - elasticsearch: ElasticsearchPlugin, - xpackInfo: any -) { - const { acknowledge } = req.query; - const { callWithRequest } = elasticsearch.getCluster('admin'); +interface StartBasicArg { + acknowledge: boolean; + callAsCurrentUser: CallAsCurrentUser; + licensing: LicensingPluginSetup; +} + +export async function startBasic({ acknowledge, callAsCurrentUser, licensing }: StartBasicArg) { const options = { method: 'POST', - path: getStartBasicPath(Boolean(acknowledge)), + path: getStartBasicPath(acknowledge), }; try { - const response = await callWithRequest(req as any, 'transport.request', options); + const response = await callAsCurrentUser('transport.request', options); const { basic_was_started: basicWasStarted } = response; if (basicWasStarted) { - await xpackInfo.refreshNow(); + await licensing.refresh(); } return response; } catch (error) { diff --git a/x-pack/plugins/license_management/server/lib/start_trial.ts b/x-pack/plugins/license_management/server/lib/start_trial.ts new file mode 100644 index 0000000000000..d3e2ba37ec203 --- /dev/null +++ b/x-pack/plugins/license_management/server/lib/start_trial.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { LicensingPluginSetup } from '../../../licensing/server'; +import { CallAsCurrentUser } from '../types'; + +export async function canStartTrial(callAsCurrentUser: CallAsCurrentUser) { + const options = { + method: 'GET', + path: '/_license/trial_status', + }; + try { + const response = await callAsCurrentUser('transport.request', options); + return response.eligible_to_start_trial; + } catch (error) { + return error.body; + } +} + +interface StartTrialArg { + callAsCurrentUser: CallAsCurrentUser; + licensing: LicensingPluginSetup; +} + +export async function startTrial({ callAsCurrentUser, licensing }: StartTrialArg) { + const options = { + method: 'POST', + path: '/_license/start_trial?acknowledge=true', + }; + try { + const response = await callAsCurrentUser('transport.request', options); + const { trial_was_started: trialWasStarted } = response; + + if (trialWasStarted) { + await licensing.refresh(); + } + + return response; + } catch (error) { + return error.body; + } +} diff --git a/x-pack/plugins/license_management/server/plugin.ts b/x-pack/plugins/license_management/server/plugin.ts new file mode 100644 index 0000000000000..9546f5b1ef88a --- /dev/null +++ b/x-pack/plugins/license_management/server/plugin.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup } from 'kibana/server'; + +import { ApiRoutes } from './routes'; +import { isEsError } from './lib/is_es_error'; +import { Dependencies } from './types'; + +export class LicenseManagementServerPlugin implements Plugin { + private readonly apiRoutes = new ApiRoutes(); + + setup({ http }: CoreSetup, { licensing, security }: Dependencies) { + const router = http.createRouter(); + + this.apiRoutes.setup({ + router, + plugins: { + licensing, + }, + lib: { + isEsError, + }, + config: { + isSecurityEnabled: security !== undefined, + }, + }); + } + + start() {} + stop() {} +} diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/index.ts b/x-pack/plugins/license_management/server/routes/api/license/index.ts similarity index 100% rename from x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/index.ts rename to x-pack/plugins/license_management/server/routes/api/license/index.ts diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts similarity index 50% rename from x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts rename to x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts index 03ec583a34166..0f426764f68ee 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts +++ b/x-pack/plugins/license_management/server/routes/api/license/register_license_route.ts @@ -6,12 +6,13 @@ import { schema } from '@kbn/config-schema'; import { putLicense } from '../../../lib/license'; -import { Legacy, Server } from '../../../types'; +import { RouteDependencies } from '../../../types'; +import { addBasePath } from '../../helpers'; -export function registerLicenseRoute(server: Server, legacy: Legacy, xpackInfo: any) { - server.router.put( +export function registerLicenseRoute({ router, plugins: { licensing } }: RouteDependencies) { + router.put( { - path: '/api/license', + path: addBasePath(''), validate: { query: schema.object({ acknowledge: schema.string() }), body: schema.object({ @@ -19,13 +20,19 @@ export function registerLicenseRoute(server: Server, legacy: Legacy, xpackInfo: }), }, }, - async (ctx, request, response) => { + async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.adminClient; try { - return response.ok({ - body: await putLicense(request, legacy.plugins.elasticsearch, xpackInfo), + return res.ok({ + body: await putLicense({ + acknowledge: Boolean(req.query.acknowledge), + callAsCurrentUser, + licensing, + license: req.body, + }), }); } catch (e) { - return response.internalError({ body: e }); + return res.internalError({ body: e }); } } ); diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts new file mode 100644 index 0000000000000..7aa3c4733acfd --- /dev/null +++ b/x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.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 { getPermissions } from '../../../lib/permissions'; +import { RouteDependencies } from '../../../types'; +import { addBasePath } from '../../helpers'; + +export function registerPermissionsRoute({ + router, + config: { isSecurityEnabled }, +}: RouteDependencies) { + router.post({ path: addBasePath('/permissions'), validate: false }, async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.adminClient; + + try { + return res.ok({ + body: await getPermissions({ callAsCurrentUser, isSecurityEnabled }), + }); + } catch (e) { + return res.internalError({ body: e }); + } + }); +} diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts b/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts new file mode 100644 index 0000000000000..ebfa283872e60 --- /dev/null +++ b/x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.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 { schema } from '@kbn/config-schema'; +import { startBasic } from '../../../lib/start_basic'; +import { RouteDependencies } from '../../../types'; +import { addBasePath } from '../../helpers'; + +export function registerStartBasicRoute({ router, plugins: { licensing } }: RouteDependencies) { + router.post( + { + path: addBasePath('/start_basic'), + validate: { query: schema.object({ acknowledge: schema.string() }) }, + }, + async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.adminClient; + try { + return res.ok({ + body: await startBasic({ + acknowledge: Boolean(req.query.acknowledge), + callAsCurrentUser, + licensing, + }), + }); + } catch (e) { + return res.internalError({ body: e }); + } + } + ); +} diff --git a/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts b/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts new file mode 100644 index 0000000000000..e418c390aaab6 --- /dev/null +++ b/x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.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 { canStartTrial, startTrial } from '../../../lib/start_trial'; +import { RouteDependencies } from '../../../types'; +import { addBasePath } from '../../helpers'; + +export function registerStartTrialRoutes({ router, plugins: { licensing } }: RouteDependencies) { + router.get({ path: addBasePath('/start_trial'), validate: false }, async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.adminClient; + try { + return res.ok({ body: await canStartTrial(callAsCurrentUser) }); + } catch (e) { + return res.internalError({ body: e }); + } + }); + + router.post({ path: addBasePath('/start_trial'), validate: false }, async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.adminClient; + try { + return res.ok({ + body: await startTrial({ callAsCurrentUser, licensing }), + }); + } catch (e) { + return res.internalError({ body: e }); + } + }); +} diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts b/x-pack/plugins/license_management/server/routes/helpers.ts similarity index 65% rename from x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts rename to x-pack/plugins/license_management/server/routes/helpers.ts index 398d58f8460ba..f1bbfd5fd4497 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts +++ b/x-pack/plugins/license_management/server/routes/helpers.ts @@ -4,5 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const getApiPath = (path: string, basePath?: string) => - basePath ? `${basePath}${path}` : path; +import { API_BASE_PATH } from '../../common/constants'; + +export const addBasePath = (uri: string): string => API_BASE_PATH + uri; diff --git a/x-pack/plugins/license_management/server/routes/index.ts b/x-pack/plugins/license_management/server/routes/index.ts new file mode 100644 index 0000000000000..9d196b6673e55 --- /dev/null +++ b/x-pack/plugins/license_management/server/routes/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 { RouteDependencies } from '../types'; + +import { + registerLicenseRoute, + registerStartTrialRoutes, + registerStartBasicRoute, + registerPermissionsRoute, +} from './api/license'; + +export class ApiRoutes { + setup(dependencies: RouteDependencies) { + registerLicenseRoute(dependencies); + registerStartTrialRoutes(dependencies); + registerStartBasicRoute(dependencies); + registerPermissionsRoute(dependencies); + } +} diff --git a/x-pack/plugins/license_management/server/types.ts b/x-pack/plugins/license_management/server/types.ts new file mode 100644 index 0000000000000..37f4781ba1e02 --- /dev/null +++ b/x-pack/plugins/license_management/server/types.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 { ScopedClusterClient, IRouter } from 'kibana/server'; + +import { LicensingPluginSetup } from '../../licensing/server'; +import { SecurityPluginSetup } from '../../security/server'; +import { isEsError } from './lib/is_es_error'; + +export interface Dependencies { + licensing: LicensingPluginSetup; + security?: SecurityPluginSetup; +} + +export interface RouteDependencies { + router: IRouter; + plugins: { + licensing: LicensingPluginSetup; + }; + lib: { + isEsError: typeof isEsError; + }; + config: { + isSecurityEnabled: boolean; + }; +} + +export type CallAsCurrentUser = ScopedClusterClient['callAsCurrentUser']; + +export type CallAsInternalUser = ScopedClusterClient['callAsInternalUser']; diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index b1483cefa43bc..3b1513f4bb95d 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -10,6 +10,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; +export const EMS_APP_NAME = 'kibana'; export const EMS_CATALOGUE_PATH = 'ems/catalogue'; export const EMS_FILES_CATALOGUE_PATH = 'ems/files'; @@ -47,6 +48,7 @@ export const LAYER_TYPE = { VECTOR: 'VECTOR', VECTOR_TILE: 'VECTOR_TILE', HEATMAP: 'HEATMAP', + BLENDED_VECTOR: 'BLENDED_VECTOR', }; export enum SORT_ORDER { @@ -187,3 +189,9 @@ export enum LABEL_BORDER_SIZES { } export const DEFAULT_ICON = 'airfield'; + +export enum SCALING_TYPES { + LIMIT = 'LIMIT', + CLUSTERS = 'CLUSTERS', + TOP_HITS = 'TOP_HITS', +} diff --git a/x-pack/plugins/maps/public/reducers/map.js b/x-pack/plugins/maps/public/reducers/map.js index 7e07569b44b83..1e20df89c8fad 100644 --- a/x-pack/plugins/maps/public/reducers/map.js +++ b/x-pack/plugins/maps/public/reducers/map.js @@ -74,7 +74,7 @@ const updateLayerInList = (state, layerId, attribute, newValue) => { return { ...state, layerList: updatedList }; }; -const updateLayerSourceDescriptorProp = (state, layerId, propName, value) => { +const updateLayerSourceDescriptorProp = (state, layerId, propName, value, newLayerType) => { const { layerList } = state; const layerIdx = getLayerIndex(layerList, layerId); const updatedLayer = { @@ -84,6 +84,9 @@ const updateLayerSourceDescriptorProp = (state, layerId, propName, value) => { [propName]: value, }, }; + if (newLayerType) { + updatedLayer.type = newLayerType; + } const updatedList = [ ...layerList.slice(0, layerIdx), updatedLayer, @@ -258,7 +261,13 @@ export function map(state = INITIAL_STATE, action) { case UPDATE_LAYER_PROP: return updateLayerInList(state, action.id, action.propName, action.newValue); case UPDATE_SOURCE_PROP: - return updateLayerSourceDescriptorProp(state, action.layerId, action.propName, action.value); + return updateLayerSourceDescriptorProp( + state, + action.layerId, + action.propName, + action.value, + action.newLayerType + ); case SET_JOINS: const layerDescriptor = state.layerList.find( descriptor => descriptor.id === action.layer.getId() diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 2f8863fc1d7cf..b6db289f4be6d 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -12,7 +12,8 @@ "features", "home", "licensing", - "usageCollection" + "usageCollection", + "share" ], "optionalPlugins": [ "security", diff --git a/x-pack/plugins/ml/mappings.json b/x-pack/plugins/ml/mappings.json deleted file mode 100644 index 041b85dbea4a1..0000000000000 --- a/x-pack/plugins/ml/mappings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type" : "long" - } - } - } - } - } -} diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index 206189c79696f..2597715488399 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -41,6 +41,7 @@ const App: FC = ({ coreStart, deps, appMountParams }) => { application: coreStart.application, http: coreStart.http, security: deps.security, + urlGenerators: deps.share.urlGenerators, }); const mlLicense = setLicenseCache(deps.licensing); diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap index 8aec3c8336da9..c6503a639997d 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/__snapshots__/explorer_no_jobs_found.test.js.snap @@ -9,7 +9,7 @@ exports[`ExplorerNoInfluencersFound snapshot 1`] = ` href="ml#/jobs" > diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js index 5cce2e1eece95..6f391f9746f23 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js @@ -23,10 +23,7 @@ export const ExplorerNoJobsFound = () => ( } actions={ - + } data-test-subj="mlNoJobsFound" diff --git a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js index e9b39058c23a8..a7734289314ae 100644 --- a/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/components/custom_url_editor/utils.js @@ -7,10 +7,9 @@ import { TIME_RANGE_TYPE, URL_TYPE } from './constants'; import rison from 'rison-node'; -// import url from 'url'; +import url from 'url'; -// import { npStart } from 'ui/new_platform'; -// import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard_embeddable_container/public'; +import { DASHBOARD_APP_URL_GENERATOR } from '../../../../../../../../src/plugins/dashboard/public'; import { ML_RESULTS_INDEX_PATTERN } from '../../../../../common/constants/index_patterns'; import { getPartitioningFieldNames } from '../../../../../common/util/job_utils'; @@ -19,7 +18,7 @@ import { replaceTokensInUrlValue, isValidLabel } from '../../../util/custom_url_ import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; import { escapeForElasticsearchQuery } from '../../../util/string_utils'; -// import { getSavedObjectsClient } from '../../../util/dependency_cache'; +import { getSavedObjectsClient, getGetUrlGenerator } from '../../../util/dependency_cache'; export function getNewCustomUrlDefaults(job, dashboards, indexPatterns) { // Returns the settings object in the format used by the custom URL editor @@ -119,7 +118,7 @@ export function buildCustomUrlFromSettings(settings) { // Dashboard URL returns a Promise as a query is made to obtain the full dashboard config. // So wrap the other two return types in a Promise for consistent return type. if (settings.type === URL_TYPE.KIBANA_DASHBOARD) { - // return buildDashboardUrlFromSettings(settings); + return buildDashboardUrlFromSettings(settings); } else if (settings.type === URL_TYPE.KIBANA_DISCOVER) { return Promise.resolve(buildDiscoverUrlFromSettings(settings)); } else { @@ -132,72 +131,70 @@ export function buildCustomUrlFromSettings(settings) { } } -// function buildDashboardUrlFromSettings(settings) { -// // Get the complete list of attributes for the selected dashboard (query, filters). -// return new Promise((resolve, reject) => { -// const { dashboardId, queryFieldNames } = settings.kibanaSettings; - -// const savedObjectsClient = getSavedObjectsClient(); -// savedObjectsClient -// .get('dashboard', dashboardId) -// .then(response => { -// // Use the filters from the saved dashboard if there are any. -// // let filters = []; - -// // Use the query from the dashboard only if no job entities are selected. -// let query = undefined; - -// const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON'); -// if (searchSourceJSON !== undefined) { -// const searchSourceData = JSON.parse(searchSourceJSON); -// if (searchSourceData.filter !== undefined) { -// filters = searchSourceData.filter; -// } -// query = searchSourceData.query; -// } - -// const queryFromEntityFieldNames = buildAppStateQueryParam(queryFieldNames); -// if (queryFromEntityFieldNames !== undefined) { -// query = queryFromEntityFieldNames; -// } - -// const generator = npStart.plugins.share.urlGenerators.getUrlGenerator( -// DASHBOARD_APP_URL_GENERATOR -// ); - -// return generator -// .createUrl({ -// dashboardId, -// timeRange: { -// from: '$earliest$', -// to: '$latest$', -// mode: 'absolute', -// }, -// filters, -// query, -// // Don't hash the URL since this string will be 1. shown to the user and 2. used as a -// // template to inject the time parameters. -// useHash: false, -// }) -// .then(urlValue => { -// const urlToAdd = { -// url_name: settings.label, -// url_value: decodeURIComponent(`kibana${url.parse(urlValue).hash}`), -// time_range: TIME_RANGE_TYPE.AUTO, -// }; - -// if (settings.timeRange.type === TIME_RANGE_TYPE.INTERVAL) { -// urlToAdd.time_range = settings.timeRange.interval; -// } - -// resolve(urlToAdd); -// }); -// }) -// .catch(resp => { -// reject(resp); -// }); -// }); -// } +function buildDashboardUrlFromSettings(settings) { + // Get the complete list of attributes for the selected dashboard (query, filters). + return new Promise((resolve, reject) => { + const { dashboardId, queryFieldNames } = settings.kibanaSettings; + + const savedObjectsClient = getSavedObjectsClient(); + savedObjectsClient + .get('dashboard', dashboardId) + .then(response => { + // Use the filters from the saved dashboard if there are any. + let filters = []; + + // Use the query from the dashboard only if no job entities are selected. + let query = undefined; + + const searchSourceJSON = response.get('kibanaSavedObjectMeta.searchSourceJSON'); + if (searchSourceJSON !== undefined) { + const searchSourceData = JSON.parse(searchSourceJSON); + if (searchSourceData.filter !== undefined) { + filters = searchSourceData.filter; + } + query = searchSourceData.query; + } + + const queryFromEntityFieldNames = buildAppStateQueryParam(queryFieldNames); + if (queryFromEntityFieldNames !== undefined) { + query = queryFromEntityFieldNames; + } + + const getUrlGenerator = getGetUrlGenerator(); + const generator = getUrlGenerator(DASHBOARD_APP_URL_GENERATOR); + return generator + .createUrl({ + dashboardId, + timeRange: { + from: '$earliest$', + to: '$latest$', + mode: 'absolute', + }, + filters, + query, + // Don't hash the URL since this string will be 1. shown to the user and 2. used as a + // template to inject the time parameters. + useHash: false, + }) + .then(urlValue => { + const urlToAdd = { + url_name: settings.label, + url_value: decodeURIComponent(`kibana${url.parse(urlValue).hash}`), + time_range: TIME_RANGE_TYPE.AUTO, + }; + + if (settings.timeRange.type === TIME_RANGE_TYPE.INTERVAL) { + urlToAdd.time_range = settings.timeRange.interval; + } + + resolve(urlToAdd); + }); + }) + .catch(resp => { + reject(resp); + }); + }); +} function buildDiscoverUrlFromSettings(settings) { const { discoverIndexPatternId, queryFieldNames } = settings.kibanaSettings; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js index 08155f3f4edba..3c791ff658978 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js @@ -15,7 +15,7 @@ function createJobStats(jobsSummaryList) { const jobStats = { activeNodes: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js index cacca839a4f59..1297ca5b9afd1 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/new_job_button/new_job_button.js @@ -29,7 +29,7 @@ export function NewJobButton() { > ); diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts index eab40c0f577f8..b030a1ef45ab0 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts @@ -72,7 +72,7 @@ export function getStatsBarData(jobsList: any) { const jobStats = { activeNodes: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index f092e85bef5ce..e087740ec0e99 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -45,7 +45,7 @@ class JobService { this.jobStats = { activeNodes: { label: i18n.translate('xpack.ml.jobService.activeMLNodesLabel', { - defaultMessage: 'Active ML Nodes', + defaultMessage: 'Active ML nodes', }), value: 0, show: true, diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index 5343c51b525d2..d5605d3bca65f 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -21,6 +21,7 @@ import { ChromeRecentlyAccessed, IBasePath, } from 'kibana/public'; +import { SharePluginStart } from 'src/plugins/share/public'; import { SecurityPluginSetup } from '../../../../security/public'; export interface DependencyCache { @@ -40,6 +41,7 @@ export interface DependencyCache { http: HttpStart | null; security: SecurityPluginSetup | null; i18n: I18nStart | null; + urlGenerators: SharePluginStart['urlGenerators'] | null; } const cache: DependencyCache = { @@ -59,6 +61,7 @@ const cache: DependencyCache = { http: null, security: null, i18n: null, + urlGenerators: null, }; export function setDependencyCache(deps: Partial) { @@ -78,6 +81,7 @@ export function setDependencyCache(deps: Partial) { cache.http = deps.http || null; cache.security = deps.security || null; cache.i18n = deps.i18n || null; + cache.urlGenerators = deps.urlGenerators || null; } export function getTimefilter() { @@ -191,6 +195,13 @@ export function getI18n() { return cache.i18n; } +export function getGetUrlGenerator() { + if (cache.urlGenerators === null) { + throw new Error("urlGenerators hasn't been initialized"); + } + return cache.urlGenerators.getUrlGenerator; +} + export function clearCache() { console.log('clearing dependency cache'); // eslint-disable-line no-console Object.keys(cache).forEach(k => { diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index ef85a36494df5..79aebece85af2 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -7,6 +7,8 @@ import { i18n } from '@kbn/i18n'; import { Plugin, CoreStart, CoreSetup, AppMountParameters } from 'kibana/public'; import { ManagementSetup } from 'src/plugins/management/public'; +import { SharePluginStart } from 'src/plugins/share/public'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { DataPublicPluginStart } from 'src/plugins/data/public'; import { SecurityPluginSetup } from '../../security/public'; @@ -17,11 +19,13 @@ import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; export interface MlStartDependencies { data: DataPublicPluginStart; + share: SharePluginStart; } export interface MlSetupDependencies { security: SecurityPluginSetup; licensing: LicensingPluginSetup; management: ManagementSetup; + usageCollection: UsageCollectionSetup; } export class MlPlugin implements Plugin { @@ -41,9 +45,11 @@ export class MlPlugin implements Plugin { coreStart, { data: pluginsStart.data, + share: pluginsStart.share, security: pluginsSetup.security, licensing: pluginsSetup.licensing, management: pluginsSetup.management, + usageCollection: pluginsSetup.usageCollection, }, { element: params.element, diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts deleted file mode 100644 index dffd95f50e0d9..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { - createMlTelemetry, - incrementFileDataVisualizerIndexCreationCount, - storeMlTelemetry, - MlTelemetry, - MlTelemetrySavedObject, - ML_TELEMETRY_DOC_ID, -} from './ml_telemetry'; -export { makeMlUsageCollector } from './make_ml_usage_collector'; diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts deleted file mode 100644 index 15a430a08eac1..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { SavedObjectsServiceStart } from 'kibana/server'; -import { - createMlTelemetry, - ML_TELEMETRY_DOC_ID, - MlTelemetry, - MlTelemetrySavedObject, -} from './ml_telemetry'; - -export function makeMlUsageCollector( - usageCollection: UsageCollectionSetup | undefined, - savedObjects: SavedObjectsServiceStart -): void { - if (!usageCollection) { - return; - } - - const mlUsageCollector = usageCollection.makeUsageCollector({ - type: 'ml', - isReady: () => true, - fetch: async (): Promise => { - try { - const mlTelemetrySavedObject: MlTelemetrySavedObject = await savedObjects - .createInternalRepository() - .get('ml-telemetry', ML_TELEMETRY_DOC_ID); - - return mlTelemetrySavedObject.attributes; - } catch (err) { - return createMlTelemetry(); - } - }, - }); - - usageCollection.registerCollector(mlUsageCollector); -} diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts deleted file mode 100644 index cda160877f7ae..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -// import { -// createMlTelemetry, -// incrementFileDataVisualizerIndexCreationCount, -// ML_TELEMETRY_DOC_ID, -// MlTelemetry, -// storeMlTelemetry, -// } from './ml_telemetry'; - -describe('ml_telemetry', () => { - describe('createMlTelemetry', () => { - it('should create a MlTelemetry object', () => { - // const mlTelemetry = createMlTelemetry(1); - // expect(mlTelemetry.file_data_visualizer.index_creation_count).toBe(1); - }); - it('should ignore undefined or unknown values', () => { - // const mlTelemetry = createMlTelemetry(undefined); - // expect(mlTelemetry.file_data_visualizer.index_creation_count).toBe(0); - }); - }); - - describe('storeMlTelemetry', () => { - // let mlTelemetry: MlTelemetry; - // let internalRepository: any; - - // beforeEach(() => { - // internalRepository = { create: jest.fn(), get: jest.fn() }; - // mlTelemetry = { - // file_data_visualizer: { - // index_creation_count: 1, - // }, - // }; - // }); - - it('should call internalRepository create with the given MlTelemetry object', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][1]).toBe(mlTelemetry); - }); - - it('should call internalRepository create with the ml-telemetry document type and ID', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(internalRepository.create.mock.calls[0][2].id).toBe(ML_TELEMETRY_DOC_ID); - }); - - it('should call internalRepository create with overwrite: true', () => { - // storeMlTelemetry(internalRepository, mlTelemetry); - // expect(internalRepository.create.mock.calls[0][2].overwrite).toBe(true); - }); - }); - - describe('incrementFileDataVisualizerIndexCreationCount', () => { - // let savedObjectsClient: any; - - // function createSavedObjectsClientInstance( - // telemetryEnabled?: boolean, - // indexCreationCount?: number - // ) { - // return { - // create: jest.fn(), - // get: jest.fn(obj => { - // switch (obj) { - // case 'telemetry': - // if (telemetryEnabled === undefined) { - // throw Error; - // } - // return { - // attributes: { - // enabled: telemetryEnabled, - // }, - // }; - // case 'ml-telemetry': - // // emulate that a non-existing saved object will throw an error - // if (indexCreationCount === undefined) { - // throw Error; - // } - // return { - // attributes: { - // file_data_visualizer: { - // index_creation_count: indexCreationCount, - // }, - // }, - // }; - // } - // }), - // }; - // } - - // function mockInit(telemetryEnabled?: boolean, indexCreationCount?: number): void { - // savedObjectsClient = createSavedObjectsClientInstance(telemetryEnabled, indexCreationCount); - // } - - it('should not increment if telemetry status cannot be determined', async () => { - // mockInit(); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls).toHaveLength(0); - }); - - it('should not increment if telemetry status is disabled', async () => { - // mockInit(false); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls).toHaveLength(0); - }); - - it('should initialize index_creation_count with 1', async () => { - // mockInit(true); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(savedObjectsClient.create.mock.calls[0][1]).toEqual({ - // file_data_visualizer: { index_creation_count: 1 }, - // }); - }); - - it('should increment index_creation_count to 2', async () => { - // mockInit(true, 1); - // await incrementFileDataVisualizerIndexCreationCount(savedObjectsClient); - // expect(savedObjectsClient.create.mock.calls[0][0]).toBe('ml-telemetry'); - // expect(savedObjectsClient.create.mock.calls[0][1]).toEqual({ - // file_data_visualizer: { index_creation_count: 2 }, - // }); - }); - }); -}); diff --git a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts b/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts deleted file mode 100644 index 1ca155582db11..0000000000000 --- a/x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts +++ /dev/null @@ -1,72 +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 { SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/server'; - -export interface MlTelemetry extends SavedObjectAttributes { - file_data_visualizer: { - index_creation_count: number; - }; -} - -export interface MlTelemetrySavedObject { - attributes: MlTelemetry; -} - -export const ML_TELEMETRY_DOC_ID = 'ml-telemetry'; - -export function createMlTelemetry(count: number = 0): MlTelemetry { - return { - file_data_visualizer: { - index_creation_count: count, - }, - }; -} -// savedObjects -export function storeMlTelemetry( - savedObjectsClient: SavedObjectsClientContract, - mlTelemetry: MlTelemetry -): void { - savedObjectsClient.create('ml-telemetry', mlTelemetry, { - id: ML_TELEMETRY_DOC_ID, - overwrite: true, - }); -} - -export async function incrementFileDataVisualizerIndexCreationCount( - savedObjectsClient: SavedObjectsClientContract -): Promise { - return; - try { - const { attributes } = await savedObjectsClient.get<{ enabled: boolean }>( - 'telemetry', - 'telemetry' - ); - - if (attributes.enabled === false) { - return; - } - } catch (error) { - // if we aren't allowed to get the telemetry document, - // we assume we couldn't opt in to telemetry and won't increment the index count. - return; - } - - let indicesCount = 1; - - try { - const { attributes } = (await savedObjectsClient.get( - 'ml-telemetry', - ML_TELEMETRY_DOC_ID - )) as MlTelemetrySavedObject; - indicesCount = attributes.file_data_visualizer.index_creation_count + 1; - } catch (e) { - /* silently fail, this will happen if the saved object doesn't exist yet. */ - } - - const mlTelemetry = createMlTelemetry(indicesCount); - storeMlTelemetry(savedObjectsClient, mlTelemetry); -} diff --git a/x-pack/legacy/plugins/license_management/public/np_ready/application/index.ts b/x-pack/plugins/ml/server/lib/telemetry/index.ts similarity index 70% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/index.ts rename to x-pack/plugins/ml/server/lib/telemetry/index.ts index 1f963d7f8fcce..b5ec80daf1787 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/index.ts +++ b/x-pack/plugins/ml/server/lib/telemetry/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './boot'; +export { initMlTelemetry } from './ml_usage_collector'; +export { updateTelemetry } from './telemetry'; diff --git a/x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts b/x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts new file mode 100644 index 0000000000000..a273ea4baadfa --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/internal_repository.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 { SavedObjectsServiceStart, ISavedObjectsRepository } from 'kibana/server'; + +let internalRepository: ISavedObjectsRepository | null = null; +export const setInternalRepository = ( + createInternalRepository: SavedObjectsServiceStart['createInternalRepository'] +) => { + internalRepository = createInternalRepository(); +}; +export const getInternalRepository = () => internalRepository; diff --git a/x-pack/plugins/ml/server/lib/telemetry/mappings.ts b/x-pack/plugins/ml/server/lib/telemetry/mappings.ts new file mode 100644 index 0000000000000..87e2243328422 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/mappings.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 { SavedObjectsType } from 'src/core/server'; +import { TELEMETRY_DOC_ID } from './telemetry'; + +export const mlTelemetryMappingsType: SavedObjectsType = { + name: TELEMETRY_DOC_ID, + hidden: false, + namespaceAgnostic: true, + mappings: { + properties: { + file_data_visualizer: { + properties: { + index_creation_count: { + type: 'long', + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts b/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts new file mode 100644 index 0000000000000..21e5dce8e4706 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.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 { CoreSetup } from 'kibana/server'; + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { getTelemetry, initTelemetry } from './telemetry'; +import { mlTelemetryMappingsType } from './mappings'; +import { setInternalRepository } from './internal_repository'; + +const TELEMETRY_TYPE = 'mlTelemetry'; + +export function initMlTelemetry(coreSetup: CoreSetup, usageCollection: UsageCollectionSetup) { + coreSetup.savedObjects.registerType(mlTelemetryMappingsType); + registerMlUsageCollector(usageCollection); + coreSetup.getStartServices().then(([core]) => { + setInternalRepository(core.savedObjects.createInternalRepository); + }); +} + +function registerMlUsageCollector(usageCollection: UsageCollectionSetup): void { + const mlUsageCollector = usageCollection.makeUsageCollector({ + type: TELEMETRY_TYPE, + isReady: () => true, + fetch: async () => (await getTelemetry()) || initTelemetry(), + }); + + usageCollection.registerCollector(mlUsageCollector); +} diff --git a/x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts b/x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts new file mode 100644 index 0000000000000..f41c4fda93a54 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/telemetry.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 { getTelemetry, updateTelemetry } from './telemetry'; + +const internalRepository = () => ({ + get: jest.fn(() => null), + create: jest.fn(() => ({ attributes: 'test' })), + update: jest.fn(() => ({ attributes: 'test' })), +}); + +function mockInit(getVal: any = { attributes: {} }): any { + return { + ...internalRepository(), + get: jest.fn(() => getVal), + }; +} + +describe('ml plugin telemetry', () => { + describe('getTelemetry', () => { + it('should get existing telemetry', async () => { + const internalRepo = mockInit(); + await getTelemetry(internalRepo); + expect(internalRepo.update.mock.calls.length).toBe(0); + expect(internalRepo.get.mock.calls.length).toBe(1); + expect(internalRepo.create.mock.calls.length).toBe(0); + }); + }); + + describe('updateTelemetry', () => { + it('should update existing telemetry', async () => { + const internalRepo = mockInit({ + attributes: { + file_data_visualizer: { + index_creation_count: 2, + }, + }, + }); + + await updateTelemetry(internalRepo); + expect(internalRepo.update.mock.calls.length).toBe(1); + expect(internalRepo.get.mock.calls.length).toBe(1); + expect(internalRepo.create.mock.calls.length).toBe(0); + }); + }); +}); diff --git a/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts b/x-pack/plugins/ml/server/lib/telemetry/telemetry.ts new file mode 100644 index 0000000000000..bc56e8b2a4372 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/telemetry.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 _ from 'lodash'; +import { ISavedObjectsRepository } from 'kibana/server'; + +import { getInternalRepository } from './internal_repository'; + +export const TELEMETRY_DOC_ID = 'ml-telemetry'; + +interface Telemetry { + file_data_visualizer: { + index_creation_count: number; + }; +} + +export interface TelemetrySavedObject { + attributes: Telemetry; +} + +export function initTelemetry(): Telemetry { + return { + file_data_visualizer: { + index_creation_count: 0, + }, + }; +} + +export async function getTelemetry( + internalRepository?: ISavedObjectsRepository +): Promise { + if (internalRepository === undefined) { + return null; + } + + let telemetrySavedObject; + + try { + telemetrySavedObject = await internalRepository.get( + TELEMETRY_DOC_ID, + TELEMETRY_DOC_ID + ); + } catch (e) { + // Fail silently + } + + return telemetrySavedObject ? telemetrySavedObject.attributes : null; +} + +export async function updateTelemetry(internalRepo?: ISavedObjectsRepository) { + const internalRepository = internalRepo || getInternalRepository(); + if (internalRepository === null) { + return; + } + + let telemetry = await getTelemetry(internalRepository); + // Create if doesn't exist + if (telemetry === null || _.isEmpty(telemetry)) { + const newTelemetrySavedObject = await internalRepository.create( + TELEMETRY_DOC_ID, + initTelemetry(), + { id: TELEMETRY_DOC_ID } + ); + telemetry = newTelemetrySavedObject.attributes; + } + + if (telemetry !== null) { + await internalRepository.update(TELEMETRY_DOC_ID, TELEMETRY_DOC_ID, incrementCounts(telemetry)); + } +} + +function incrementCounts(telemetry: Telemetry) { + return { + file_data_visualizer: { + index_creation_count: telemetry.file_data_visualizer.index_creation_count + 1, + }, + }; +} diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 01d0bcc867019..8948d232b9e5e 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -16,7 +16,7 @@ import { PluginsSetup, RouteInitialization } from './types'; import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; import { elasticsearchJsPlugin } from './client/elasticsearch_ml'; -import { makeMlUsageCollector } from './lib/ml_telemetry'; +import { initMlTelemetry } from './lib/telemetry'; import { initMlServerLog } from './client/log'; import { initSampleDataSets } from './lib/sample_data_sets'; @@ -130,9 +130,7 @@ export class MlServerPlugin implements Plugin { - makeMlUsageCollector(plugins.usageCollection, core.savedObjects); - }); + initMlTelemetry(coreSetup, plugins.usageCollection); return createSharedServices(this.mlLicense, plugins.spaces, plugins.cloud); } 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 a14d51ae61b05..fcfd6e121c9f1 100644 --- a/x-pack/plugins/ml/server/routes/file_data_visualizer.ts +++ b/x-pack/plugins/ml/server/routes/file_data_visualizer.ts @@ -19,7 +19,7 @@ import { } from '../models/file_data_visualizer'; import { RouteInitialization } from '../types'; -import { incrementFileDataVisualizerIndexCreationCount } from '../lib/ml_telemetry'; +import { updateTelemetry } from '../lib/telemetry'; function analyzeFiles(context: RequestHandlerContext, data: InputData, overrides: InputOverrides) { const { analyzeFile } = fileDataVisualizerProvider(context.ml!.mlClient.callAsCurrentUser); @@ -132,7 +132,7 @@ export function fileDataVisualizerRoutes({ router, mlLicense }: RouteInitializat // follow-up import calls to just add additional data will include the `id` of the created // index, we'll ignore those and don't increment the counter. if (id === undefined) { - await incrementFileDataVisualizerIndexCreationCount(context.core.savedObjects.client); + await updateTelemetry(); } const result = await importData( diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts index acde73dcd8190..eedd63e228523 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.test.ts @@ -28,7 +28,7 @@ describe('Put payload schema', () => { kibana: [{ feature: { foo: ['!foo'] } }], }) ).toThrowErrorMatchingInlineSnapshot( - `"[kibana.0.feature.foo]: only a-z, A-Z, 0-9, '_', and '-' are allowed"` + `"[kibana.0.feature.foo.0]: only a-z, A-Z, 0-9, '_', and '-' are allowed"` ); }); diff --git a/x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap b/x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap index e1cfafd897234..a9fd636776a4f 100644 --- a/x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap +++ b/x-pack/plugins/spaces/public/space_selector/__snapshots__/space_selector.test.tsx.snap @@ -17,7 +17,7 @@ exports[`it renders without crashing 1`] = ` > { - + diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index eb208e67ccfec..86a6f958cf258 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2380,7 +2380,6 @@ "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える", "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "全画面を終了", "kibana-react.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", "newsfeed.emptyPrompt.noNewsText": "Kibanaインスタンスがインターネットにアクセスできない場合、管理者にこの機能を無効にするように依頼してください。そうでない場合は、ニュースを取り込み続けます。", "newsfeed.emptyPrompt.noNewsTitle": "ニュースがない場合", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f85714a5913ad..c580eb533feb9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2381,7 +2381,6 @@ "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例", "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例", "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}切换选项", - "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "退出全屏", "kibana-react.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", "newsfeed.emptyPrompt.noNewsText": "如果您的 Kibana 实例没有 Internet 连接,请让您的管理员禁用此功能。否则,我们将不断尝试获取新闻。", "newsfeed.emptyPrompt.noNewsTitle": "无新闻?", diff --git a/x-pack/plugins/triggers_actions_ui/README.md b/x-pack/plugins/triggers_actions_ui/README.md index 0d667f477f936..e6af63ecd4359 100644 --- a/x-pack/plugins/triggers_actions_ui/README.md +++ b/x-pack/plugins/triggers_actions_ui/README.md @@ -7,12 +7,6 @@ As a developer you can reuse and extend built-in alerts and actions UI functiona - Create and register a new Action Type. - Embed the Create Alert flyout within any Kibana plugin. -To enable Alerts and Actions UIs, the following configuration settings are needed: -``` -xpack.triggers_actions_ui.enabled: true -xpack.triggers_actions_ui.createAlertUiEnabled: true -``` - ----- diff --git a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx index 51ed3c1ebafad..70945350c3cfa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/app.tsx @@ -30,7 +30,6 @@ export interface AppDeps { chrome: ChromeStart; docLinks: DocLinksStart; toastNotifications: ToastsSetup; - injectedMetadata: any; http: HttpSetup; uiSettings: IUiSettingsClient; setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx index a458472c6d753..c157f923e4447 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/boot.tsx @@ -9,7 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { SavedObjectsClientContract } from 'src/core/public'; import { App, AppDeps } from './app'; -import { setSavedObjectsClient } from '../application/components/builtin_alert_types/threshold/lib/api'; +import { setSavedObjectsClient } from '../common/lib/index_threshold_api'; interface BootDeps extends AppDeps { element: HTMLElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx index d44787f0c4ed6..f1d4790e67bbe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx @@ -9,6 +9,7 @@ import { TypeRegistry } from '../../type_registry'; import { registerBuiltInActionTypes } from './index'; import { ActionTypeModel, ActionParamsProps } from '../../../types'; import { IndexActionParams, EsIndexActionConnector } from './types'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; const ACTION_TYPE_ID = '.index'; let actionTypeModel: ActionTypeModel; @@ -38,16 +39,15 @@ describe('index connector validation', () => { name: 'es_index', config: { index: 'test_es_index', + refresh: false, + executionTimeField: '1', }, } as EsIndexActionConnector; expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: {}, - }); - - delete actionConnector.config.index; - expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ - errors: {}, + errors: { + index: [], + }, }); }); }); @@ -55,9 +55,6 @@ describe('index connector validation', () => { describe('action params validation', () => { test('action params validation succeeds when action params is valid', () => { const actionParams = { - index: 'test', - refresh: false, - executionTimeField: '1', documents: ['test'], }; @@ -75,6 +72,8 @@ describe('action params validation', () => { describe('IndexActionConnectorFields renders', () => { test('all connector fields is rendered', () => { + const mocks = coreMock.createSetup(); + expect(actionTypeModel.actionConnectorFields).not.toBeNull(); if (!actionTypeModel.actionConnectorFields) { return; @@ -87,23 +86,21 @@ describe('IndexActionConnectorFields renders', () => { name: 'es_index', config: { index: 'test', + refresh: false, + executionTimeField: 'test1', }, } as EsIndexActionConnector; const wrapper = mountWithIntl( {}} editActionSecrets={() => {}} + http={mocks.http} /> ); - expect(wrapper.find('[data-test-subj="indexInput"]').length > 0).toBeTruthy(); - expect( - wrapper - .find('[data-test-subj="indexInput"]') - .first() - .prop('value') - ).toBe('test'); + expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); }); }); @@ -117,8 +114,6 @@ describe('IndexParamsFields renders', () => { ActionParamsProps >; const actionParams = { - index: 'test_index', - refresh: false, documents: ['test'], }; const wrapper = mountWithIntl( @@ -129,13 +124,11 @@ describe('IndexParamsFields renders', () => { index={0} /> ); - expect(wrapper.find('[data-test-subj="indexInput"]').length > 0).toBeTruthy(); expect( wrapper - .find('[data-test-subj="indexInput"]') + .find('[data-test-subj="actionIndexDoc"]') .first() .prop('value') - ).toBe('test_index'); - expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy(); + ).toBe('"test"'); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx index 6af54d2bf15b4..b3e62e022c412 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx @@ -3,8 +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 React, { Fragment } from 'react'; -import { EuiFieldText, EuiFormRow, EuiSwitch, EuiSpacer } from '@elastic/eui'; +import React, { Fragment, useState, useEffect } from 'react'; +import { + EuiFormRow, + EuiSwitch, + EuiSpacer, + EuiCodeEditor, + EuiComboBox, + EuiComboBoxOptionOption, + EuiSelect, + EuiTitle, + EuiIconTip, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { @@ -14,6 +24,13 @@ import { ActionParamsProps, } from '../../../types'; import { IndexActionParams, EsIndexActionConnector } from './types'; +import { getTimeFieldOptions } from '../../../common/lib/get_time_options'; +import { + firstFieldOption, + getFields, + getIndexOptions, + getIndexPatterns, +} from '../../../common/index_controls'; export function getActionType(): ActionTypeModel { return { @@ -25,8 +42,23 @@ export function getActionType(): ActionTypeModel { defaultMessage: 'Index data into Elasticsearch.', } ), - validateConnector: (): ValidationResult => { - return { errors: {} }; + validateConnector: (action: EsIndexActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + index: new Array(), + }; + validationResult.errors = errors; + if (!action.config.index) { + errors.index.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.error.requiredIndexText', + { + defaultMessage: 'Index is required.', + } + ) + ); + } + return validationResult; }, actionConnectorFields: IndexActionConnectorFields, actionParamsFields: IndexParamsFields, @@ -38,33 +70,189 @@ export function getActionType(): ActionTypeModel { const IndexActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig }) => { - const { index } = action.config; +>> = ({ action, editActionConfig, errors, http }) => { + const { index, refresh, executionTimeField } = action.config; + const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState( + executionTimeField !== undefined + ); + + const [indexPatterns, setIndexPatterns] = useState([]); + const [indexOptions, setIndexOptions] = useState([]); + const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]); + const [isIndiciesLoading, setIsIndiciesLoading] = useState(false); + + useEffect(() => { + const indexPatternsFunction = async () => { + setIndexPatterns(await getIndexPatterns()); + if (index) { + const currentEsFields = await getFields(http!, [index]); + const timeFields = getTimeFieldOptions(currentEsFields as any); + setTimeFieldOptions([firstFieldOption, ...timeFields]); + } + }; + indexPatternsFunction(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( - - + +
+ +
+
+ + ) => { - editActionConfig('index', e.target.value); - }} - onBlur={() => { - if (!index) { - editActionConfig('index', ''); + label={ + + } + isInvalid={errors.index.length > 0 && index !== undefined} + error={errors.index} + helpText={ + + } + > + 0 && index !== undefined} + noSuggestions={!indexOptions.length} + options={indexOptions} + data-test-subj="connectorIndexesComboBox" + selectedOptions={ + index + ? [ + { + value: index, + label: index, + }, + ] + : [] } + onChange={async (selected: EuiComboBoxOptionOption[]) => { + editActionConfig('index', selected[0].value); + const indices = selected.map(s => s.value as string); + + // reset time field and expression fields if indices are deleted + if (indices.length === 0) { + setTimeFieldOptions([]); + return; + } + const currentEsFields = await getFields(http!, indices); + const timeFields = getTimeFieldOptions(currentEsFields as any); + + setTimeFieldOptions([firstFieldOption, ...timeFields]); + }} + onSearchChange={async search => { + setIsIndiciesLoading(true); + setIndexOptions(await getIndexOptions(http!, search, indexPatterns)); + setIsIndiciesLoading(false); + }} + onBlur={() => { + if (!index) { + editActionConfig('index', ''); + } + }} + /> + + + { + editActionConfig('refresh', e.target.checked); }} + label={ + <> + {' '} + + + } + /> + + { + setTimeFieldCheckboxState(!hasTimeFieldCheckbox); + }} + label={ + <> + + + + } /> -
+ + {hasTimeFieldCheckbox ? ( + <> + + } + > + { + editActionConfig('executionTimeField', e.target.value); + }} + onBlur={() => { + if (executionTimeField === undefined) { + editActionConfig('executionTimeField', ''); + } + }} + /> + + + ) : null} + ); }; @@ -73,47 +261,48 @@ const IndexParamsFields: React.FunctionComponent { - const { refresh } = actionParams; + const { documents } = actionParams; + + function onDocumentsChange(updatedDocuments: string) { + try { + const documentsJSON = JSON.parse(updatedDocuments); + editAction('documents', [documentsJSON], index); + // eslint-disable-next-line no-empty + } catch (e) {} + } return ( - ) => { - editAction('index', e.target.value, index); + 0 ? documents[0] : {}, null, 2)} + onChange={onDocumentsChange} + width="100%" + height="auto" + minLines={6} + maxLines={30} + isReadOnly={false} + setOptions={{ + showLineNumbers: true, + tabSize: 2, }} - onBlur={() => { - if (!actionParams.index) { - editAction('index', '', index); - } + editorProps={{ + $blockScrolling: Infinity, }} + showGutter={true} /> - - { - editAction('refresh', e.target.checked, index); - }} - label={ - - } - /> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts index 45a08b2d5263a..c0ddd6791e90e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts @@ -39,9 +39,6 @@ export interface PagerDutyActionParams { } export interface IndexActionParams { - index?: string; - refresh?: boolean; - executionTimeField?: string; documents: string[]; } @@ -85,7 +82,9 @@ export interface EmailActionConnector extends ActionConnector { } interface EsIndexConfig { - index?: string; + index: string; + executionTimeField?: string; + refresh?: boolean; } export interface EsIndexActionConnector extends ActionConnector { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx index 2bf779e550618..5c7f48de81f75 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/expression.tsx @@ -23,12 +23,13 @@ import { EuiEmptyPrompt, EuiText, } from '@elastic/eui'; -import { COMPARATORS, builtInComparators } from '../../../../common/constants'; import { - getMatchingIndicesForThresholdAlertType, - getThresholdAlertTypeFields, - loadIndexPatterns, -} from './lib/api'; + firstFieldOption, + getIndexPatterns, + getIndexOptions, + getFields, +} from '../../../../common/index_controls'; +import { COMPARATORS, builtInComparators } from '../../../../common/constants'; import { getTimeFieldOptions } from '../../../../common/lib/get_time_options'; import { ThresholdVisualization } from './visualization'; import { WhenExpression } from '../../../../common'; @@ -95,15 +96,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 ); - const getIndexPatterns = async () => { - const indexPatternObjects = await loadIndexPatterns(); - const titles = indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); - setIndexPatterns(titles); - }; - const expressionErrorMessage = i18n.translate( 'xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage', { @@ -150,7 +136,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent 0) { - const currentEsFields = await getFields(index); + const currentEsFields = await getFields(http, index); const timeFields = getTimeFieldOptions(currentEsFields as any); setEsFields(currentEsFields); @@ -158,12 +144,11 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent { - return await getThresholdAlertTypeFields({ indexes, http }); - }; - useEffect(() => { - getIndexPatterns(); + const indexPatternsFunction = async () => { + setIndexPatterns(await getIndexPatterns()); + }; + indexPatternsFunction(); }, []); useEffect(() => { @@ -171,60 +156,6 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent; - } - - const getIndexOptions = async (pattern: string, indexPatternsParam: string[]) => { - const options: IOption[] = []; - - if (!pattern) { - return options; - } - - const matchingIndices = (await getMatchingIndicesForThresholdAlertType({ - pattern, - http, - })) as string[]; - const matchingIndexPatterns = indexPatternsParam.filter(anIndexPattern => { - return anIndexPattern.includes(pattern); - }) as string[]; - - if (matchingIndices.length || matchingIndexPatterns.length) { - const matchingOptions = _.uniq([...matchingIndices, ...matchingIndexPatterns]); - - options.push({ - label: i18n.translate( - 'xpack.triggersActionsUI.sections.alertAdd.threshold.indicesAndIndexPatternsLabel', - { - defaultMessage: 'Based on your indices and index patterns', - } - ), - options: matchingOptions.map(match => { - return { - label: match, - value: match, - }; - }), - }); - } - - options.push({ - label: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.threshold.chooseLabel', { - defaultMessage: 'Choose…', - }), - options: [ - { - value: pattern, - label: pattern, - }, - ], - }); - - return options; - }; - const indexPopover = ( @@ -285,7 +216,7 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent { setIsIndiciesLoading(true); - setIndexOptions(await getIndexOptions(search, indexPatterns)); + setIndexOptions(await getIndexOptions(http, search, indexPatterns)); setIsIndiciesLoading(false); }} onBlur={() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts index d5b64f1489b8d..356b0fbbc0845 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/types.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - TimeSeriesResult, - TimeSeriesResultRow, - MetricResult, -} from '../../../../../../alerting_builtins/common/alert_types/index_threshold'; - export interface Comparator { text: string; value: string; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx index f27e35fe7609d..0bcaa83127468 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/visualization.tsx @@ -23,7 +23,7 @@ import { import moment from 'moment-timezone'; import { EuiCallOut, EuiLoadingChart, EuiSpacer, EuiEmptyPrompt, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getThresholdAlertVisualizationData } from './lib/api'; +import { getThresholdAlertVisualizationData } from '../../../../common/lib/index_threshold_api'; import { AggregationType, Comparator } from '../../../../common/types'; import { AlertsContextValue } from '../../../context/alerts_context'; import { IndexThresholdAlertParams } from './types'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx index f68cc5759fb54..1c70e42e7ae72 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx @@ -68,6 +68,7 @@ describe('action_connector_form', () => { dispatch={() => {}} errors={{ name: [] }} actionTypeRegistry={deps.actionTypeRegistry} + http={deps.http} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index e221fff64048e..57333d8032793 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -15,6 +15,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { HttpSetup } from 'kibana/public'; import { ReducerAction } from './connector_reducer'; import { ActionConnector, IErrorObject, ActionTypeModel } from '../../../types'; import { TypeRegistry } from '../../type_registry'; @@ -47,6 +48,7 @@ interface ActionConnectorProps { }; errors: IErrorObject; actionTypeRegistry: TypeRegistry; + http: HttpSetup; } export const ActionConnectorForm = ({ @@ -56,6 +58,7 @@ export const ActionConnectorForm = ({ serverError, errors, actionTypeRegistry, + http, }: ActionConnectorProps) => { const setActionProperty = (key: string, value: any) => { dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); @@ -148,6 +151,7 @@ export const ActionConnectorForm = ({ errors={errors} editActionConfig={setActionConfigProperty} editActionSecrets={setActionSecretsProperty} + http={http} /> ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index f265a1de6f56a..9aea2419ec619 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -104,6 +104,7 @@ export const ConnectorAddFlyout = ({ dispatch={dispatch} errors={errors} actionTypeRegistry={actionTypeRegistry} + http={http} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx index c7f52fb462cc0..977a908fd86f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_modal.tsx @@ -152,6 +152,7 @@ export const ConnectorAddModal = ({ serverError={serverError} errors={errors} actionTypeRegistry={actionTypeRegistry} + http={http} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index d0dcff9ef6a94..39c0b7255a7b9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -135,6 +135,7 @@ export const ConnectorEditFlyout = ({ actionTypeName={connector.actionType} dispatch={dispatch} actionTypeRegistry={actionTypeRegistry} + http={http} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index 509bd7131394e..f94efc0d06729 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -58,7 +58,6 @@ describe('actions_connectors_list component empty', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: mockes.injectedMetadata, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -155,7 +154,6 @@ describe('actions_connectors_list component with items', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: mockes.injectedMetadata, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -239,7 +237,6 @@ describe('actions_connectors_list component empty with show only capability', () dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: mockes.injectedMetadata, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -328,7 +325,6 @@ describe('actions_connectors_list with show only capability', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: mockes.injectedMetadata, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 1177b41788bd6..fc524debe7443 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -43,7 +43,6 @@ describe('alert_add', () => { const mockes = coreMock.createSetup(); deps = { toastNotifications: mockes.notifications.toasts, - injectedMetadata: mockes.injectedMetadata, http: mockes.http, uiSettings: mockes.uiSettings, dataPlugin: dataPluginMock.createStartContract(), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index aa56b565ef324..1fa620c5394a1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -155,6 +155,7 @@ export const AlertForm = ({ alert, canChangeTrigger = true, dispatch, errors }: onClick={() => { setAlertProperty('alertTypeId', item.id); setAlertTypeModel(item); + setAlertProperty('params', {}); if (alertTypesIndex && alertTypesIndex[item.id]) { setDefaultActionGroupId(alertTypesIndex[item.id].defaultActionGroupId); } @@ -194,6 +195,7 @@ export const AlertForm = ({ alert, canChangeTrigger = true, dispatch, errors }: onClick={() => { setAlertProperty('alertTypeId', null); setAlertTypeModel(null); + setAlertProperty('params', {}); }} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 865ab6ea04cea..a80daf544f34e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -92,13 +92,6 @@ describe('alerts_list component empty', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: { - getInjectedVar(name: string) { - if (name === 'createAlertUiEnabled') { - return true; - } - }, - } as any, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -220,13 +213,6 @@ describe('alerts_list component with items', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: { - getInjectedVar(name: string) { - if (name === 'createAlertUiEnabled') { - return true; - } - }, - } as any, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -242,6 +228,8 @@ describe('alerts_list component with items', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(true); + wrapper = mountWithIntl( @@ -257,11 +245,15 @@ describe('alerts_list component with items', () => { expect(loadActionTypes).toHaveBeenCalled(); } - it('renders table of connectors', async () => { + it('renders table of alerts', async () => { await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); expect(wrapper.find('EuiTableRow')).toHaveLength(2); }); + it('renders edit button for registered alert types', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBeGreaterThan(0); + }); }); describe('alerts_list component empty with show only capability', () => { @@ -309,13 +301,6 @@ describe('alerts_list component empty with show only capability', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: { - getInjectedVar(name: string) { - if (name === 'createAlertUiEnabled') { - return true; - } - }, - } as any, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -433,13 +418,6 @@ describe('alerts_list with show only capability', () => { dataPlugin: dataPluginMock.createStartContract(), charts: chartPluginMock.createStartContract(), toastNotifications: mockes.notifications.toasts, - injectedMetadata: { - getInjectedVar(name: string) { - if (name === 'createAlertUiEnabled') { - return true; - } - }, - } as any, http: mockes.http, uiSettings: mockes.uiSettings, capabilities: { @@ -455,6 +433,8 @@ describe('alerts_list with show only capability', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(false); + wrapper = mountWithIntl( @@ -473,4 +453,8 @@ describe('alerts_list with show only capability', () => { expect(wrapper.find('EuiTableRow')).toHaveLength(2); // TODO: check delete button }); + it('not renders edit button for non registered alert types', async () => { + await setup(); + expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBe(0); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 2975b1ef6eba2..c409dead7c850 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -52,7 +52,6 @@ export const AlertsList: React.FunctionComponent = () => { const history = useHistory(); const { http, - injectedMetadata, toastNotifications, capabilities, alertTypeRegistry, @@ -63,7 +62,6 @@ export const AlertsList: React.FunctionComponent = () => { } = useAppDependencies(); const canDelete = hasDeleteAlertsCapability(capabilities); const canSave = hasSaveAlertsCapability(capabilities); - const createAlertUiEnabled = injectedMetadata.getInjectedVar('createAlertUiEnabled'); const [actionTypes, setActionTypes] = useState([]); const [selectedIds, setSelectedIds] = useState([]); @@ -225,7 +223,7 @@ export const AlertsList: React.FunctionComponent = () => { ? [ { render: (item: AlertTableItem) => { - return ( + return alertTypeRegistry.has(item.alertTypeId) ? ( { id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editLinkTitle" /> + ) : ( + <> ); }, }, @@ -271,7 +271,7 @@ export const AlertsList: React.FunctionComponent = () => { />, ]; - if (canSave && createAlertUiEnabled) { + if (canSave) { toolsRight.push( ; +} + +export const getIndexPatterns = async () => { + // TODO: Implement a possibility to retrive index patterns different way to be able to expose this in consumer plugins + if (getSavedObjectsClient()) { + const indexPatternObjects = await loadIndexPatterns(); + return indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); + } + return []; +}; + +export const getIndexOptions = async ( + http: HttpSetup, + pattern: string, + indexPatternsParam: string[] +) => { + const options: IOption[] = []; + + if (!pattern) { + return options; + } + + const matchingIndices = (await getMatchingIndicesForThresholdAlertType({ + pattern, + http, + })) as string[]; + const matchingIndexPatterns = indexPatternsParam.filter(anIndexPattern => { + return anIndexPattern.includes(pattern); + }) as string[]; + + if (matchingIndices.length || matchingIndexPatterns.length) { + const matchingOptions = _.uniq([...matchingIndices, ...matchingIndexPatterns]); + + options.push({ + label: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.indicesAndIndexPatternsLabel', + { + defaultMessage: 'Based on your index patterns', + } + ), + options: matchingOptions.map(match => { + return { + label: match, + value: match, + }; + }), + }); + } + + options.push({ + label: i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.chooseLabel', + { + defaultMessage: 'Choose…', + } + ), + options: [ + { + value: pattern, + label: pattern, + }, + ], + }); + + return options; +}; + +export const getFields = async (http: HttpSetup, indexes: string[]) => { + return await getThresholdAlertTypeFields({ indexes, http }); +}; + +export const firstFieldOption = { + text: i18n.translate('xpack.triggersActionsUI.sections.alertAdd.threshold.timeFieldOptionLabel', { + defaultMessage: 'Select a field', + }), + value: '', +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts similarity index 96% rename from x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts rename to x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts index 064f05b415d42..9ec198a43646f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/threshold/lib/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/lib/index_threshold_api.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { HttpSetup } from 'kibana/public'; -import { TimeSeriesResult } from '../types'; -export { TimeSeriesResult } from '../types'; +import { TimeSeriesResult } from '../../../../alerting_builtins/common/alert_types/index_threshold'; const INDEX_THRESHOLD_API_ROOT = '/api/alerting_builtins/index_threshold'; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 9f975cba3c0d1..f4d8c478efaf2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -82,7 +82,6 @@ export class Plugin charts: plugins.charts, element: params.element, toastNotifications: core.notifications.toasts, - injectedMetadata: core.injectedMetadata, http: core.http, uiSettings: core.uiSettings, docLinks: core.docLinks, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index d9681e2474f00..900521830571c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.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 { HttpSetup } from 'kibana/public'; import { ActionGroup } from '../../alerting/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; @@ -20,11 +21,12 @@ export type AlertTypeIndex = Record; export type ActionTypeRegistryContract = PublicMethodsOf>; export type AlertTypeRegistryContract = PublicMethodsOf>; -export interface ActionConnectorFieldsProps { - action: TActionCOnnector; +export interface ActionConnectorFieldsProps { + action: TActionConnector; editActionConfig: (property: string, value: any) => void; editActionSecrets: (property: string, value: any) => void; errors: { [key: string]: string[] }; + http?: HttpSetup; } export interface ActionParamsProps { 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 ca9da51cc7ba8..1783c6e91df34 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 @@ -89,7 +89,7 @@ const statusCountBody = (filters: any): any => { String id = doc["monitor.id"][0]; String idLenDelim = Integer.toHexString(id.length()) + ":" + id; String idLoc = loc == null ? idLenDelim : idLenDelim + loc; - + String status = doc["summary.down"][0] > 0 ? "d" : "u"; String timeAndStatus = doc["@timestamp"][0].toInstant().toEpochMilli().toString() + status; state.locStatus[idLoc] = timeAndStatus; @@ -111,7 +111,7 @@ const statusCountBody = (filters: any): any => { locStatus.merge(entry.getKey(), entry.getValue(), (a,b) -> a.compareTo(b) > 0 ? a : b) } } - + HashMap locTotals = new HashMap(); int total = 0; int down = 0; @@ -130,7 +130,7 @@ const statusCountBody = (filters: any): any => { String id = idLoc.substring(colonIndex + 1, idEnd); String loc = idLoc.substring(idEnd, idLoc.length()); String status = timeStatus.substring(timeStatus.length() - 1); - + // Here we increment counters for the up/down key per location // We also create a new hashmap in locTotals if we've never seen this location // before. @@ -141,7 +141,7 @@ const statusCountBody = (filters: any): any => { res.put('up', 0); res.put('down', 0); } - + if (status == 'u') { res.up++; } else { @@ -150,8 +150,8 @@ const statusCountBody = (filters: any): any => { return res; }); - - + + // We've encountered a new ID if (curId != id) { total++; @@ -171,7 +171,7 @@ const statusCountBody = (filters: any): any => { } } } - + Map result = new HashMap(); result.total = total; result.location_totals = locTotals; 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 cc65749153c1d..806d6e789a890 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,10 +6,11 @@ import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/index_pattern', + path: API_URLS.INDEX_PATTERN, validate: false, options: { tags: ['access:uptime'], 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 44799aa19c140..d4d76c86870ee 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,11 +6,11 @@ import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: REST_API_URLS.INDEX_STATUS, + path: API_URLS.INDEX_STATUS, validate: false, options: { tags: ['access:uptime'], 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 f8c7666f53f7d..131b3cbe2ab44 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,10 +7,11 @@ 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'; export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/monitor/locations', + path: API_URLS.MONITOR_LOCATIONS, validate: { query: schema.object({ monitorId: schema.string(), 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 ca88dd965c1ad..66e952813eb3e 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,10 +7,11 @@ 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'; export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/monitor/details', + path: API_URLS.MONITOR_DETAILS, validate: { query: schema.object({ monitorId: schema.string(), 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 63e74175609ad..f4a4cadc99976 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,10 +7,12 @@ 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'; export const createGetMonitorDurationRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/monitor/duration', + path: API_URLS.MONITOR_DURATION, + validate: { query: schema.object({ monitorId: schema.string(), diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/status.ts index 8dac50c9f5905..08cbc2d70e515 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/status.ts @@ -7,10 +7,12 @@ 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'; export const createGetMonitorRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/monitor/selected', + path: API_URLS.MONITOR_SELECTED, + validate: { query: schema.object({ monitorId: schema.string(), @@ -32,7 +34,8 @@ export const createGetMonitorRoute: UMRestApiRouteFactory = (libs: UMServerLibs) export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/monitor/status', + path: API_URLS.MONITOR_STATUS, + validate: { query: schema.object({ monitorId: schema.string(), 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 02e54cb441838..5525771539c63 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,6 +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'; const arrayOrStringType = schema.maybe( schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) @@ -15,7 +16,7 @@ const arrayOrStringType = schema.maybe( export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/filters', + path: API_URLS.FILTERS, validate: { query: schema.object({ dateRangeStart: schema.string(), diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts index 21168edfc9744..e301a2cbf9af9 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts @@ -7,10 +7,11 @@ 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'; export const createGetAllRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/pings', + path: API_URLS.PINGS, validate: { query: schema.object({ dateRangeStart: 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 93ba4490fa31f..dfaabcdf93a06 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,10 +7,11 @@ 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'; export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/ping/histogram', + path: API_URLS.PING_HISTOGRAM, validate: { query: schema.object({ dateStart: schema.string(), 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 e57951c98b6fc..458107dd87a77 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 @@ -7,10 +7,11 @@ 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'; export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/pings', + path: API_URLS.PINGS, validate: { query: schema.object({ dateRangeStart: schema.string(), 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 c51806e323307..697c49dc8300b 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,10 +7,11 @@ 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'; export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', - path: '/api/uptime/snapshot/count', + path: API_URLS.SNAPSHOT_COUNT, validate: { query: schema.object({ dateRangeStart: schema.string(), diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts index 1aa0f8e2c9f16..6d76a00d39b97 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts @@ -31,7 +31,9 @@ export default function indexTest({ getService }: FtrProviderContext) { .send({ name: 'An index action', actionTypeId: '.index', - config: {}, + config: { + index: ES_TEST_INDEX_NAME, + }, secrets: {}, }) .expect(200); @@ -41,7 +43,8 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index action', actionTypeId: '.index', config: { - index: null, + index: ES_TEST_INDEX_NAME, + refresh: false, }, }); createdActionID = createdAction.id; @@ -55,10 +58,10 @@ export default function indexTest({ getService }: FtrProviderContext) { id: fetchedAction.id, name: 'An index action', actionTypeId: '.index', - config: { index: null }, + config: { index: ES_TEST_INDEX_NAME, refresh: false }, }); - // create action with index config + // create action with all config props const { body: createdActionWithIndex } = await supertest .post('/api/action') .set('kbn-xsrf', 'foo') @@ -67,6 +70,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }) .expect(200); @@ -77,6 +82,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); createdActionIDWithIndex = createdActionWithIndex.id; @@ -92,6 +99,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); }); @@ -111,20 +120,31 @@ export default function indexTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type config: [index]: types that failed validation:\n- [index.0]: expected value of type [string] but got [number]\n- [index.1]: expected value to equal [null]', + 'error validating action type config: [index]: expected value of type [string] but got [number]', }); }); }); it('should execute successly when expected for a single body', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }], - refresh: true, }, }) .expect(200); @@ -136,14 +156,25 @@ export default function indexTest({ getService }: FtrProviderContext) { }); it('should execute successly when expected for with multiple bodies', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }, { Testing: [4, 5, 6] }], - refresh: true, }, }) .expect(200); @@ -169,12 +200,25 @@ export default function indexTest({ getService }: FtrProviderContext) { }); it('should execute successly with refresh false', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: false, + executionTimeField: 'test', + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ refresh: 'not set' }], }, }) @@ -185,57 +229,32 @@ export default function indexTest({ getService }: FtrProviderContext) { items = await getTestIndexItems(es); expect(items.length).to.be.lessThan(2); - const { body: result2 } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + const { body: createdActionWithRefresh } = await supertest + .post('/api/action') .set('kbn-xsrf', 'foo') .send({ - params: { + name: 'An index action', + actionTypeId: '.index', + config: { index: ES_TEST_INDEX_NAME, - documents: [{ refresh: 'true' }], refresh: true, }, + secrets: {}, }) .expect(200); - expect(result2.status).to.eql('ok'); - - items = await getTestIndexItems(es); - expect(items.length).to.eql(2); - }); - - it('should execute unsuccessfully when expected', async () => { - let response; - let result; - - response = await supertest - .post(`/api/action/${createdActionID}/_execute`) + const { body: result2 } = await supertest + .post(`/api/action/${createdActionWithRefresh.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - indeX: ES_TEST_INDEX_NAME, - documents: [{ testing: [1, 2, 3] }], + documents: [{ refresh: 'true' }], }, }) .expect(200); - result = response.body; - expect(result.status).to.equal('error'); - expect(result.message).to.eql( - 'error validating action params: [indeX]: definition for this key is missing' - ); + expect(result2.status).to.eql('ok'); - response = await supertest - .post(`/api/action/${createdActionID}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - documents: [{ testing: [1, 2, 3] }], - }, - }) - .expect(200); - result = response.body; - expect(result.status).to.equal('error'); - expect(result.message).to.eql( - 'index param needs to be set because not set in config for action' - ); + items = await getTestIndexItems(es); + expect(items.length).to.eql(2); }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts index 79e0da3a4c68a..5cc3d7275a7bd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts @@ -31,7 +31,7 @@ export default function indexTest({ getService }: FtrProviderContext) { .send({ name: 'An index action', actionTypeId: '.index', - config: {}, + config: { index: ES_TEST_INDEX_NAME }, secrets: {}, }) .expect(200); @@ -41,7 +41,8 @@ export default function indexTest({ getService }: FtrProviderContext) { name: 'An index action', actionTypeId: '.index', config: { - index: null, + index: ES_TEST_INDEX_NAME, + refresh: false, }, }); createdActionID = createdAction.id; @@ -55,10 +56,10 @@ export default function indexTest({ getService }: FtrProviderContext) { id: fetchedAction.id, name: 'An index action', actionTypeId: '.index', - config: { index: null }, + config: { index: ES_TEST_INDEX_NAME, refresh: false }, }); - // create action with index config + // create action with all config props const { body: createdActionWithIndex } = await supertest .post('/api/action') .set('kbn-xsrf', 'foo') @@ -67,6 +68,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }) .expect(200); @@ -77,6 +80,8 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); createdActionIDWithIndex = createdActionWithIndex.id; @@ -92,19 +97,32 @@ export default function indexTest({ getService }: FtrProviderContext) { actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, + refresh: true, + executionTimeField: 'test', }, }); }); it('should execute successly when expected for a single body', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'An index action', + actionTypeId: '.index', + config: { + index: ES_TEST_INDEX_NAME, + refresh: true, + }, + secrets: {}, + }) + .expect(200); const { body: result } = await supertest - .post(`/api/action/${createdActionID}/_execute`) + .post(`/api/action/${createdAction.id}/_execute`) .set('kbn-xsrf', 'foo') .send({ params: { - index: ES_TEST_INDEX_NAME, documents: [{ testing: [1, 2, 3] }], - refresh: true, }, }) .expect(200); diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index 5f18bdd9bea02..49e527fa3e7e8 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -7,9 +7,9 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; /** - * The number of alert documents in the es archive. + * The number of host documents in the es archive. */ -const numberOfEndpointsInFixture = 3; +const numberOfHostsInFixture = 3; export default function({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); @@ -24,7 +24,7 @@ export default function({ getService }: FtrProviderContext) { .send() .expect(200); expect(body.total).to.eql(0); - expect(body.endpoints.length).to.eql(0); + expect(body.hosts.length).to.eql(0); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -33,14 +33,14 @@ export default function({ getService }: FtrProviderContext) { describe('POST /api/endpoint/metadata when index is not empty', () => { before(() => esArchiver.load('endpoint/metadata/api_feature')); after(() => esArchiver.unload('endpoint/metadata/api_feature')); - it('metadata api should return one entry for each endpoint with default paging', async () => { + it('metadata api should return one entry for each host with default paging', async () => { const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') .send() .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(numberOfHostsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -60,8 +60,8 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(1); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(1); expect(body.request_page_index).to.eql(1); }); @@ -84,8 +84,8 @@ export default function({ getService }: FtrProviderContext) { ], }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(0); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(0); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(30); }); @@ -115,7 +115,7 @@ export default function({ getService }: FtrProviderContext) { .send({ filter: 'not host.ip:10.100.170.247' }) .expect(200); expect(body.total).to.eql(2); - expect(body.endpoints.length).to.eql(2); + expect(body.hosts.length).to.eql(2); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -139,7 +139,7 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(2); const resultIps: string[] = [].concat( - ...body.endpoints.map((metadata: Record) => metadata.host.ip) + ...body.hosts.map((metadata: Record) => metadata.host.ip) ); expect(resultIps).to.eql([ '10.48.181.222', @@ -150,7 +150,7 @@ export default function({ getService }: FtrProviderContext) { '10.128.235.38', ]); expect(resultIps).not.include.eql(notIncludedIp); - expect(body.endpoints.length).to.eql(2); + expect(body.hosts.length).to.eql(2); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -166,10 +166,10 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(1); const resultOsVariantValue: Set = new Set( - body.endpoints.map((metadata: Record) => metadata.host.os.variant) + body.hosts.map((metadata: Record) => metadata.host.os.variant) ); expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); - expect(body.endpoints.length).to.eql(1); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); @@ -184,17 +184,17 @@ export default function({ getService }: FtrProviderContext) { }) .expect(200); expect(body.total).to.eql(1); - const resultIp: string = body.endpoints[0].host.ip.filter( + const resultIp: string = body.hosts[0].host.ip.filter( (ip: string) => ip === targetEndpointIp ); expect(resultIp).to.eql([targetEndpointIp]); - expect(body.endpoints[0].event.created).to.eql(1584044335459); - expect(body.endpoints.length).to.eql(1); + expect(body.hosts[0].event.created).to.eql(1584044335459); + expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); - it('metadata api should return all endpoints when filter is empty string', async () => { + it('metadata api should return all hosts when filter is empty string', async () => { const { body } = await supertest .post('/api/endpoint/metadata') .set('kbn-xsrf', 'xxx') @@ -202,8 +202,8 @@ export default function({ getService }: FtrProviderContext) { filter: '', }) .expect(200); - expect(body.total).to.eql(numberOfEndpointsInFixture); - expect(body.endpoints.length).to.eql(numberOfEndpointsInFixture); + expect(body.total).to.eql(numberOfHostsInFixture); + expect(body.hosts.length).to.eql(numberOfHostsInFixture); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); 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 15666acab2335..91ea1bedb061a 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 { REST_API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../../legacy/plugins/uptime/common/constants'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestWithoutAuth'); @@ -30,7 +30,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) const basePath = spaceId ? `/s/${spaceId}` : ''; return await supertest - .get(basePath + REST_API_URLS.INDEX_STATUS) + .get(basePath + API_URLS.INDEX_STATUS) .auth(username, password) .set('kbn-xsrf', 'foo') .then((response: any) => ({ error: undefined, response })) 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 1f5322f581b39..3f42511dd165c 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,14 +5,14 @@ */ import { FtrProviderContext } from '../../../ftr_provider_context'; import { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql'; -import { REST_API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; export default function({ getService }: FtrProviderContext) { describe('docCount query', () => { const supertest = getService('supertest'); it(`will fetch the index's count`, async () => { - const apiResponse = await supertest.get(REST_API_URLS.INDEX_STATUS); + const apiResponse = await supertest.get(API_URLS.INDEX_STATUS); const data = apiResponse.body; expectFixtureEql(data, 'doc_count'); }); diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 1dd069bb907d1..98ab4c1f15a54 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -28,7 +28,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); } - describe('security', () => { + // FLAKY: https://github.com/elastic/kibana/issues/60535 + describe.skip('security', () => { before(async () => { await esArchiver.load('discover/feature_controls/security'); await esArchiver.loadIfNeeded('logstash_functional'); diff --git a/x-pack/test/functional/apps/endpoint/alerts.ts b/x-pack/test/functional/apps/endpoint/alerts.ts index 1ce7eb41e6690..759574702c0f1 100644 --- a/x-pack/test/functional/apps/endpoint/alerts.ts +++ b/x-pack/test/functional/apps/endpoint/alerts.ts @@ -18,8 +18,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await esArchiver.load('endpoint/alerts/api_feature'); await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/alerts'); }); - - it('loads in the browser', async () => { + it('loads the Alert List Page', async () => { await testSubjects.existOrFail('alertListPage'); }); it('contains the Alert List Page title', async () => { @@ -57,6 +56,12 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { it('loads the Alert List Flyout correctly', async () => { await testSubjects.existOrFail('alertDetailFlyout'); }); + + it('loads the resolver component and renders at least a single node', async () => { + await testSubjects.click('overviewResolverTab'); + await testSubjects.existOrFail('alertResolver'); + await testSubjects.existOrFail('resolverNode'); + }); }); after(async () => { diff --git a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts index 287892903dd2b..bf3d642307d8c 100644 --- a/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts +++ b/x-pack/test/functional/apps/endpoint/feature_controls/endpoint_spaces.ts @@ -41,18 +41,13 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('welcomeTitle'); }); - it(`endpoint management shows 'Manage Endpoints'`, async () => { - await pageObjects.common.navigateToUrlWithBrowserHistory( - 'endpoint', - '/management', - undefined, - { - basePath: '/s/custom_space', - ensureCurrentUrl: false, - shouldLoginIfPrompted: false, - } - ); - await testSubjects.existOrFail('managementViewTitle'); + it(`endpoint management shows 'Hosts'`, async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts', undefined, { + basePath: '/s/custom_space', + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + }); + await testSubjects.existOrFail('hostListTitle'); }); }); diff --git a/x-pack/test/functional/apps/endpoint/header_nav.ts b/x-pack/test/functional/apps/endpoint/header_nav.ts index 2368ad077cf64..d1fa7311d61e8 100644 --- a/x-pack/test/functional/apps/endpoint/header_nav.ts +++ b/x-pack/test/functional/apps/endpoint/header_nav.ts @@ -19,19 +19,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the tabs when the app loads', async () => { const homeTabText = await testSubjects.getVisibleText('homeEndpointTab'); - const managementTabText = await testSubjects.getVisibleText('managementEndpointTab'); + const hostsTabText = await testSubjects.getVisibleText('hostsEndpointTab'); const alertsTabText = await testSubjects.getVisibleText('alertsEndpointTab'); const policiesTabText = await testSubjects.getVisibleText('policiesEndpointTab'); expect(homeTabText.trim()).to.be('Home'); - expect(managementTabText.trim()).to.be('Management'); + expect(hostsTabText.trim()).to.be('Hosts'); expect(alertsTabText.trim()).to.be('Alerts'); expect(policiesTabText.trim()).to.be('Policies'); }); - it('renders the management page when the Management tab is selected', async () => { - await (await testSubjects.find('managementEndpointTab')).click(); - await testSubjects.existOrFail('managementViewTitle'); + it('renders the hosts page when the Hosts tab is selected', async () => { + await (await testSubjects.find('hostsEndpointTab')).click(); + await testSubjects.existOrFail('hostListTitle'); }); it('renders the alerts page when the Alerts tab is selected', async () => { @@ -45,8 +45,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders the home page when Home tab is selected after selecting another tab', async () => { - await (await testSubjects.find('managementEndpointTab')).click(); - await testSubjects.existOrFail('managementViewTitle'); + await (await testSubjects.find('hostsEndpointTab')).click(); + await testSubjects.existOrFail('hostListTitle'); await (await testSubjects.find('homeEndpointTab')).click(); await testSubjects.existOrFail('welcomeTitle'); diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/host_list.ts similarity index 58% rename from x-pack/test/functional/apps/endpoint/management.ts rename to x-pack/test/functional/apps/endpoint/host_list.ts index 640f6264c3a09..baace0f7670e1 100644 --- a/x-pack/test/functional/apps/endpoint/management.ts +++ b/x-pack/test/functional/apps/endpoint/host_list.ts @@ -12,15 +12,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); - describe('Endpoint Management List', function() { + describe('host list', function() { this.tags('ciGroup7'); before(async () => { await esArchiver.load('endpoint/metadata/api_feature'); - await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts'); }); it('finds title', async () => { - const title = await testSubjects.getVisibleText('managementViewTitle'); + const title = await testSubjects.getVisibleText('hostListTitle'); expect(title).to.equal('Hosts'); }); @@ -67,21 +67,70 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'xxxx', ], ]; - const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + const tableData = await pageObjects.endpoint.getEndpointAppTableData('hostListTable'); expect(tableData).to.eql(expectedData); }); - it('displays no items found', async () => { + it('display details flyout when the hostname is clicked on', async () => { + await (await testSubjects.find('hostnameCellLink')).click(); + await testSubjects.existOrFail('hostDetailsUpperList'); + await testSubjects.existOrFail('hostDetailsLowerList'); + }); + + it('displays no items found when empty', async () => { // clear out the data and reload the page await esArchiver.unload('endpoint/metadata/api_feature'); - await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/hosts'); // get the table data and verify no entries appear - const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + const tableData = await pageObjects.endpoint.getEndpointAppTableData('hostListTable'); expect(tableData[1][0]).to.equal('No items found'); // reload the data so the other tests continue to pass await esArchiver.load('endpoint/metadata/api_feature'); }); + describe('has a url with a host id', () => { + before(async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'endpoint', + '/hosts', + 'selected_host=cbe80003-6964-4e0f-aba1-f94c32b44e95' + ); + }); + + it('shows a flyout', async () => { + await testSubjects.existOrFail('hostDetailsFlyout'); + }); + + it('displays details row headers', async () => { + const expectedData = [ + 'OS', + 'Last Seen', + 'Alerts', + 'Policy', + 'Policy Status', + 'IP Address', + 'Hostname', + 'Sensor Version', + ]; + const keys = await pageObjects.endpoint.hostFlyoutDescriptionKeys('hostDetailsFlyout'); + expect(keys).to.eql(expectedData); + }); + + it('displays details row descriptions', async () => { + const values = await pageObjects.endpoint.hostFlyoutDescriptionValues('hostDetailsFlyout'); + + expect(values).to.eql([ + 'Windows Server 2012', + '', + '0', + 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', + 'active', + '10.48.181.22210.116.62.6210.102.83.30', + 'Host-cxz5glsoup', + '6.6.9', + ]); + }); + }); after(async () => { await esArchiver.unload('endpoint/metadata/api_feature'); }); diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts index 15ce522ce56ba..4d55b3af4956e 100644 --- a/x-pack/test/functional/apps/endpoint/index.ts +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -12,7 +12,7 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./landing_page')); loadTestFile(require.resolve('./header_nav')); - loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./host_list')); loadTestFile(require.resolve('./policy_list')); loadTestFile(require.resolve('./policy_details')); loadTestFile(require.resolve('./alerts')); diff --git a/x-pack/test/functional/apps/maps/blended_vector_layer.js b/x-pack/test/functional/apps/maps/blended_vector_layer.js new file mode 100644 index 0000000000000..a01f796fe3455 --- /dev/null +++ b/x-pack/test/functional/apps/maps/blended_vector_layer.js @@ -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 expect from '@kbn/expect'; + +export default function({ getPageObjects, getService }) { + const PageObjects = getPageObjects(['maps']); + const inspector = getService('inspector'); + + describe('blended vector layer', () => { + before(async () => { + await PageObjects.maps.loadSavedMap('blended document example'); + }); + + it('should request documents when zoomed to smaller regions showing less data', async () => { + const hits = await PageObjects.maps.getHits(); + expect(hits).to.equal('33'); + }); + + it('should request clusters when zoomed to larger regions showing lots of data', async () => { + await PageObjects.maps.setView(20, -90, 2); + await inspector.open(); + await inspector.openInspectorRequestsView(); + const requestStats = await inspector.getTableData(); + const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits'); + const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)'); + await inspector.close(); + + expect(hits).to.equal('0'); + expect(totalHits).to.equal('14000'); + }); + + it('should request documents when query narrows data', async () => { + await PageObjects.maps.setAndSubmitQuery('bytes > 19000'); + const hits = await PageObjects.maps.getHits(); + expect(hits).to.equal('75'); + }); + }); +} diff --git a/x-pack/test/functional/apps/maps/index.js b/x-pack/test/functional/apps/maps/index.js index 44a7c4c9a5f86..ae7de986cf867 100644 --- a/x-pack/test/functional/apps/maps/index.js +++ b/x-pack/test/functional/apps/maps/index.js @@ -30,6 +30,7 @@ export default function({ loadTestFile, getService }) { describe('', function() { this.tags('ciGroup7'); loadTestFile(require.resolve('./documents_source')); + loadTestFile(require.resolve('./blended_vector_layer')); loadTestFile(require.resolve('./saved_object_management')); loadTestFile(require.resolve('./sample_data')); loadTestFile(require.resolve('./feature_controls/maps_security')); diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts index 60b72f122f113..5dcfd876f5b53 100644 --- a/x-pack/test/functional/apps/transform/index.ts +++ b/x-pack/test/functional/apps/transform/index.ts @@ -8,7 +8,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getService, loadTestFile }: FtrProviderContext) { const transform = getService('transform'); - describe('transform', function() { + // prevent test failures with current ES snapshot, see https://github.com/elastic/kibana/issues/60516 + describe.skip('transform', function() { this.tags(['ciGroup9', 'transform']); before(async () => { diff --git a/x-pack/test/functional/es_archives/maps/kibana/data.json b/x-pack/test/functional/es_archives/maps/kibana/data.json index e50ec593cc990..cb3598652a39a 100644 --- a/x-pack/test/functional/es_archives/maps/kibana/data.json +++ b/x-pack/test/functional/es_archives/maps/kibana/data.json @@ -288,7 +288,7 @@ "title" : "document example top hits split with scripted field", "description" : "", "mapStateJSON" : "{\"zoom\":4.1,\"center\":{\"lon\":-100.61091,\"lat\":33.23887},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-24T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[]}", - "layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"useTopHits\":true,\"topHitsSplitField\":\"hour_of_day\",\"topHitsSize\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\",\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"airfield\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", + "layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{}},\"type\":\"VECTOR_TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":\"logstash\",\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[],\"scalingType\":\"TOP_HITS\",\"topHitsSplitField\":\"hour_of_day\",\"topHitsSize\":1,\"sortField\":\"@timestamp\",\"sortOrder\":\"desc\",\"applyGlobalQuery\":true,\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"airfield\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]", "uiStateJSON" : "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}", "bounds" : { "type" : "Polygon", @@ -861,6 +861,62 @@ } } +{ + "type": "doc", + "value": { + "id": "map:279e1f20-6883-11ea-952a-b102add99cf8", + "index": ".kibana", + "source": { + "map" : { + "title" : "blended document example", + "description" : "", + "mapStateJSON" : "{\"zoom\":10.27,\"center\":{\"lon\":-83.70716,\"lat\":32.73679},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-23T00:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":0},\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filters\":[]}", + "layerListJSON" : "[{\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"isAutoSelect\":true},\"id\":\"43a70a86-00fd-43af-9e84-4d9fe2d7513d\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"alpha\":1,\"visible\":true,\"style\":{},\"type\":\"VECTOR_TILE\"},{\"id\":\"307c8495-89f7-431b-83d8-78724d9a8f72\",\"label\":\"logstash-*\",\"sourceDescriptor\":{\"geoField\":\"geo.coordinates\",\"id\":\"20fc58c3-3c0a-4c7b-9cdc-37552cafdc21\",\"tooltipProperties\":[],\"type\":\"ES_SEARCH\",\"scalingType\":\"CLUSTERS\",\"indexPatternRefName\":\"layer_1_source_index_pattern\"},\"type\":\"BLENDED_VECTOR\",\"visible\":true,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"icon\":{\"type\":\"STATIC\",\"options\":{\"value\":\"airfield\"}},\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#54B399\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#41937c\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":6}},\"iconOrientation\":{\"type\":\"STATIC\",\"options\":{\"orientation\":0}},\"labelText\":{\"type\":\"STATIC\",\"options\":{\"value\":\"\"}},\"labelColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#000000\"}},\"labelSize\":{\"type\":\"STATIC\",\"options\":{\"size\":14}},\"labelBorderColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"symbolizeAs\":{\"options\":{\"value\":\"circle\"}},\"labelBorderSize\":{\"options\":{\"size\":\"SMALL\"}}},\"isTimeAware\":true}}]", + "uiStateJSON" : "{\"isLayerTOCOpen\":true,\"openTOCDetails\":[]}", + "bounds" : { + "type" : "Polygon", + "coordinates" : [ + [ + [ + -84.07816, + 32.95327 + ], + [ + -84.07816, + 32.51978 + ], + [ + -83.33616, + 32.51978 + ], + [ + -83.33616, + 32.95327 + ], + [ + -84.07816, + 32.95327 + ] + ] + ] + } + }, + "type" : "map", + "references" : [ + { + "name" : "layer_1_source_index_pattern", + "type" : "index-pattern", + "id" : "c698b940-e149-11e8-a35a-370a8516603a" + } + ], + "migrationVersion" : { + "map" : "7.7.0" + }, + "updated_at" : "2020-03-17T19:11:50.290Z" + } + } +} + { "type": "doc", "value": { diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts index 6350f51f707f4..4becbf797abc0 100644 --- a/x-pack/test/functional/page_objects/endpoint_page.ts +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -63,9 +63,42 @@ export function EndpointPageProvider({ getService }: FtrProviderContext) { async waitForTableToHaveData(dataTestSubj: string) { await retry.waitForWithTimeout('table to have data', 2000, async () => { const tableData = await this.getEndpointAppTableData(dataTestSubj); - if (tableData[1][0] === 'No items found') return false; + if (tableData[1][0] === 'No items found') { + return false; + } return true; }); }, + + async hostFlyoutDescriptionKeys(dataTestSubj: string) { + await testSubjects.exists(dataTestSubj); + const detailsData: WebElementWrapper = await testSubjects.find(dataTestSubj); + const $ = await detailsData.parseDomContent(); + return $('dt') + .toArray() + .map(key => + $(key) + .text() + .replace(/ /g, '') + .trim() + ); + }, + + async hostFlyoutDescriptionValues(dataTestSubj: string) { + await testSubjects.exists(dataTestSubj); + const detailsData: WebElementWrapper = await testSubjects.find(dataTestSubj); + const $ = await detailsData.parseDomContent(); + return $('dd') + .toArray() + .map((value, index) => { + if (index === 1) { + return ''; + } + return $(value) + .text() + .replace(/ /g, '') + .trim(); + }); + }, }; } diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 1a9736b0b4773..b19ec95c68916 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -49,8 +49,6 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { `--plugin-path=${join(__dirname, 'fixtures', 'plugins', 'alerts')}`, '--xpack.actions.enabled=true', '--xpack.alerting.enabled=true', - '--xpack.triggers_actions_ui.enabled=true', - '--xpack.triggers_actions_ui.createAlertUiEnabled=true', ], }, }; diff --git a/yarn.lock b/yarn.lock index 6dfd5a993af7b..d71f2141c5cae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1916,10 +1916,10 @@ once "^1.4.0" pump "^3.0.0" -"@elastic/ems-client@7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.6.0.tgz#ca548aba1a1f5170a1892de129b537b5248c74be" - integrity sha512-oBtLH24qIgTaMhlSske49FTd35Y0nv+PlZCZaHkBhOH+ScsTDL3LO2lbIcSmcYQod43Ly34v/xwJvFCTxojVEQ== +"@elastic/ems-client@7.7.0": + version "7.7.0" + resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.7.0.tgz#7d36d716dd941f060b9fcdae94f186a9aecc5cc2" + integrity sha512-JatsSyLik/8MTEOEimzEZ3NYjvGL1YzjbGujuSCgaXhPRqzu/wvMLEL8dlVpmYFZ7ALbGNsVdho4Hr8tngsIMw== dependencies: lodash "^4.17.15" node-fetch "^1.7.3" @@ -4678,6 +4678,11 @@ dependencies: "@types/sizzle" "*" +"@types/js-cookie@2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e" + integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg== + "@types/js-search@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@types/js-search/-/js-search-1.4.0.tgz#f2d4afa176a4fc7b17fb46a1593847887fa1fb7b" @@ -4889,10 +4894,10 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@10.12.27", "@types/node@8.10.54", "@types/node@>=8.9.0", "@types/node@^10.12.27", "@types/node@^12.0.2": - version "10.12.27" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.27.tgz#eb3843f15d0ba0986cc7e4d734d2ee8b50709ef8" - integrity sha512-e9wgeY6gaY21on3ve0xAjgBVjGDWq/xUteK0ujsE53bUoxycMkqfnkUgMt6ffZtykZ5X12Mg3T7Pw4TRCObDKg== +"@types/node@*", "@types/node@8.10.54", "@types/node@>=10.17.17 <10.20.0", "@types/node@>=8.9.0", "@types/node@^12.0.2": + version "10.17.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.17.tgz#7a183163a9e6ff720d86502db23ba4aade5999b8" + integrity sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q== "@types/nodemailer@^6.2.1": version "6.2.1" @@ -5768,10 +5773,10 @@ dependencies: tslib "^1.9.3" -"@xobotyi/scrollbar-width@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.5.0.tgz#488210bff634548040dc22a72f62722a85b134e1" - integrity sha512-BK+HR1D00F2xh7n4+5en8/dMkG13uvIXLmEbsjtc1702b7+VwXkvlBDKoRPJMbkRN5hD7VqWa3nS9fNT8JG3CA== +"@xobotyi/scrollbar-width@1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb" + integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -13643,10 +13648,10 @@ fast-safe-stringify@2.x.x, fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0. resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-shallow-equal@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-0.1.1.tgz#44d01324d7fd31e00a67bb02b9396e283d526c22" - integrity sha512-XVP6nhaXLYOH6JZCWBcNaeEer9GJ5/8cJWUP+OLmgwWgEkJp5Kpl/fdpJ01zl0mpLxrk7f5J3hIv+GmjTCi7Mg== +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== fast-stream-to-buffer@^1.0.0: version "1.0.0" @@ -17542,7 +17547,7 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" -is-path-inside@^3.0.1: +is-path-inside@^3.0.1, is-path-inside@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== @@ -18469,6 +18474,11 @@ js-base64@^2.1.8: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.5.tgz#e293cd3c7c82f070d700fc7a1ca0a2e69f101f92" integrity sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ== +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-levenshtein@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.3.tgz#3ef627df48ec8cf24bacf05c0f184ff30ef413c5" @@ -24880,16 +24890,18 @@ react-transition-group@^2.2.1: prop-types "^15.6.2" react-lifecycles-compat "^3.0.4" -react-use@^13.13.0: - version "13.13.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.13.0.tgz#5d133c4d4d8d3f21f6ccf4ccbe54fbcd6fdafb36" - integrity sha512-J3/h5wvL6vXmecAvEnninCC3DviLMRWcQrEnouTliwws1b376DQKEgIFuTXlF8c3SKpXBQJdDDm1RpluokW6ag== +react-use@^13.27.0: + version "13.27.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa" + integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ== dependencies: - "@xobotyi/scrollbar-width" "1.5.0" + "@types/js-cookie" "2.2.5" + "@xobotyi/scrollbar-width" "1.9.4" copy-to-clipboard "^3.2.0" - fast-shallow-equal "^0.1.1" + fast-deep-equal "^3.1.1" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" nano-css "^5.2.1" - react-fast-compare "^2.0.4" resize-observer-polyfill "^1.5.1" screenfull "^5.0.0" set-harmonic-interval "^1.0.1"