From 9318862f1967d6fefdfca5e94417ff1617e5ba06 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Tue, 17 Mar 2020 12:30:17 -0400 Subject: [PATCH 01/35] Allow kbn-config-schema to ignore unknown keys (#59560) * allow kbn-config-schema to ignore unknown keys * Consolidate unknown key configuration * updates following merge Co-authored-by: Elastic Machine --- ...plugin-core-server.routeconfig.validate.md | 4 +- packages/kbn-config-schema/README.md | 4 +- .../src/types/object_type.test.ts | 43 ++++++++++++++++--- .../src/types/object_type.ts | 21 ++++++--- src/core/server/http/router/route.ts | 4 +- src/core/server/http/router/router.test.ts | 2 +- .../server/ui_settings/routes/set_many.ts | 2 +- .../server/ui_settings/ui_settings_config.ts | 2 +- .../autocomplete/value_suggestions_route.ts | 4 +- src/plugins/data/server/search/routes.ts | 6 +-- src/plugins/timelion/config.ts | 2 +- src/plugins/timelion/server/routes/run.ts | 12 ++---- .../vis_type_timeseries/server/routes/vis.ts | 2 +- .../plugins/rendering_plugin/server/plugin.ts | 2 +- .../api/license/register_license_route.ts | 2 +- .../plugins/rollup/server/routes/api/jobs.ts | 2 +- .../signals/signal_params_schema.ts | 6 +-- .../lib/framework/kibana_framework_adapter.ts | 2 +- .../apm/server/routes/create_api/index.ts | 2 +- .../canvas/server/routes/workpad/update.ts | 2 +- .../plugins/case/server/routes/api/utils.ts | 2 +- .../file_upload/server/routes/file_upload.js | 12 +++--- x-pack/plugins/graph/server/routes/explore.ts | 2 +- x-pack/plugins/graph/server/routes/search.ts | 2 +- .../routes/api/templates/validate_schemas.ts | 6 +-- .../framework/kibana_framework_adapter.ts | 2 +- .../server/routes/inventory_metadata/index.ts | 2 +- .../results/log_entry_categories.ts | 2 +- .../results/log_entry_category_datasets.ts | 2 +- .../results/log_entry_category_examples.ts | 2 +- .../log_analysis/results/log_entry_rate.ts | 2 +- .../routes/log_analysis/validation/indices.ts | 2 +- .../server/routes/log_entries/entries.ts | 2 +- .../server/routes/log_entries/highlights.ts | 2 +- .../infra/server/routes/log_entries/item.ts | 2 +- .../server/routes/log_entries/summary.ts | 2 +- .../routes/log_entries/summary_highlights.ts | 2 +- .../infra/server/routes/metadata/index.ts | 2 +- .../server/routes/metrics_explorer/index.ts | 2 +- .../infra/server/routes/node_details/index.ts | 2 +- .../infra/server/routes/snapshot/index.ts | 2 +- .../lens/server/routes/existing_fields.ts | 2 +- .../plugins/lens/server/routes/field_stats.ts | 6 +-- .../searchprofiler/server/routes/profile.ts | 2 +- .../server/routes/authentication/common.ts | 2 +- .../server/routes/authentication/oidc.ts | 6 +-- .../server/routes/role_mapping/post.ts | 4 +- .../security/server/routes/views/login.ts | 2 +- .../server/routes/api/validate_schemas.ts | 10 ++--- .../routes/api/indices/register_get_route.ts | 2 +- .../api/watch/register_execute_route.ts | 4 +- .../routes/api/watch/register_save_route.ts | 2 +- .../api/watch/register_visualize_route.ts | 4 +- 53 files changed, 132 insertions(+), 96 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md b/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md index 204d8a786fede..3bbabc04f2500 100644 --- a/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md +++ b/docs/development/core/server/kibana-plugin-core-server.routeconfig.validate.md @@ -14,7 +14,7 @@ validate: RouteValidatorFullConfig | false; ## Remarks -You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify `validate: false`. In this case request params, query, and body will be \*\*empty\*\* objects and have no access to raw values. In some cases you may want to use another validation library. To do this, you need to instruct the `@kbn/config-schema` library to output \*\*non-validated values\*\* with setting schema as `schema.object({}, { allowUnknowns: true })`; +You \*must\* specify a validation schema to be able to read: - url path segments - request query - request body To opt out of validating the request, specify `validate: false`. In this case request params, query, and body will be \*\*empty\*\* objects and have no access to raw values. In some cases you may want to use another validation library. To do this, you need to instruct the `@kbn/config-schema` library to output \*\*non-validated values\*\* with setting schema as `schema.object({}, { unknowns: 'allow' })`; ## Example @@ -49,7 +49,7 @@ router.get({ path: 'path/{id}', validate: { // handler has access to raw non-validated params in runtime - params: schema.object({}, { allowUnknowns: true }) + params: schema.object({}, { unknowns: 'allow' }) }, }, (context, req, res,) { diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index 8719a2ae558ab..a4f2c1f6458cf 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -239,7 +239,7 @@ __Output type:__ `{ [K in keyof TProps]: TypeOf } as TObject` __Options:__ * `defaultValue: TObject | Reference | (() => TObject)` - defines a default value, see [Default values](#default-values) section for more details. * `validate: (value: TObject) => string | void` - defines a custom validator function, see [Custom validation](#custom-validation) section for more details. - * `allowUnknowns: boolean` - indicates whether unknown object properties should be allowed. It's `false` by default. + * `unknowns: 'allow' | 'ignore' | 'forbid'` - indicates whether unknown object properties should be allowed, ignored, or forbidden. It's `forbid` by default. __Usage:__ ```typescript @@ -250,7 +250,7 @@ const valueSchema = schema.object({ ``` __Notes:__ -* Using `allowUnknowns` is discouraged and should only be used in exceptional circumstances. Consider using `schema.recordOf()` instead. +* Using `unknowns: 'allow'` is discouraged and should only be used in exceptional circumstances. Consider using `schema.recordOf()` instead. * Currently `schema.object()` always has a default value of `{}`, but this may change in the near future. Try to not rely on this behaviour and specify default value explicitly or use `schema.maybe()` if the value is optional. * `schema.object()` also supports a json string as input if it can be safely parsed using `JSON.parse` and if the resulting value is a plain object. diff --git a/packages/kbn-config-schema/src/types/object_type.test.ts b/packages/kbn-config-schema/src/types/object_type.test.ts index 29e341983fde9..47a0f5f7a5491 100644 --- a/packages/kbn-config-schema/src/types/object_type.test.ts +++ b/packages/kbn-config-schema/src/types/object_type.test.ts @@ -276,10 +276,10 @@ test('individual keys can validated', () => { ); }); -test('allow unknown keys when allowUnknowns = true', () => { +test('allow unknown keys when unknowns = `allow`', () => { const type = schema.object( { foo: schema.string({ defaultValue: 'test' }) }, - { allowUnknowns: true } + { unknowns: 'allow' } ); expect( @@ -292,10 +292,10 @@ test('allow unknown keys when allowUnknowns = true', () => { }); }); -test('allowUnknowns = true affects only own keys', () => { +test('unknowns = `allow` affects only own keys', () => { const type = schema.object( { foo: schema.object({ bar: schema.string() }) }, - { allowUnknowns: true } + { unknowns: 'allow' } ); expect(() => @@ -308,10 +308,10 @@ test('allowUnknowns = true affects only own keys', () => { ).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`); }); -test('does not allow unknown keys when allowUnknowns = false', () => { +test('does not allow unknown keys when unknowns = `forbid`', () => { const type = schema.object( { foo: schema.string({ defaultValue: 'test' }) }, - { allowUnknowns: false } + { unknowns: 'forbid' } ); expect(() => type.validate({ @@ -319,3 +319,34 @@ test('does not allow unknown keys when allowUnknowns = false', () => { }) ).toThrowErrorMatchingInlineSnapshot(`"[bar]: definition for this key is missing"`); }); + +test('allow and remove unknown keys when unknowns = `ignore`', () => { + const type = schema.object( + { foo: schema.string({ defaultValue: 'test' }) }, + { unknowns: 'ignore' } + ); + + expect( + type.validate({ + bar: 'baz', + }) + ).toEqual({ + foo: 'test', + }); +}); + +test('unknowns = `ignore` affects only own keys', () => { + const type = schema.object( + { foo: schema.object({ bar: schema.string() }) }, + { unknowns: 'ignore' } + ); + + expect(() => + type.validate({ + foo: { + bar: 'bar', + baz: 'baz', + }, + }) + ).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`); +}); diff --git a/packages/kbn-config-schema/src/types/object_type.ts b/packages/kbn-config-schema/src/types/object_type.ts index f34acd0d2ce65..5a50e714a5931 100644 --- a/packages/kbn-config-schema/src/types/object_type.ts +++ b/packages/kbn-config-schema/src/types/object_type.ts @@ -30,17 +30,25 @@ export type TypeOf> = RT['type']; // this might not have perfect _rendering_ output, but it will be typed. export type ObjectResultType

= Readonly<{ [K in keyof P]: TypeOf }>; +interface UnknownOptions { + /** + * Options for dealing with unknown keys: + * - allow: unknown keys will be permitted + * - ignore: unknown keys will not fail validation, but will be stripped out + * - forbid (default): unknown keys will fail validation + */ + unknowns?: 'allow' | 'ignore' | 'forbid'; +} + export type ObjectTypeOptions

= TypeOptions< { [K in keyof P]: TypeOf } -> & { - /** Should uknown keys not be defined in the schema be allowed. Defaults to `false` */ - allowUnknowns?: boolean; -}; +> & + UnknownOptions; export class ObjectType

extends Type> { private props: Record; - constructor(props: P, { allowUnknowns = false, ...typeOptions }: ObjectTypeOptions

= {}) { + constructor(props: P, { unknowns = 'forbid', ...typeOptions }: ObjectTypeOptions

= {}) { const schemaKeys = {} as Record; for (const [key, value] of Object.entries(props)) { schemaKeys[key] = value.getSchema(); @@ -50,7 +58,8 @@ export class ObjectType

extends Type> .keys(schemaKeys) .default() .optional() - .unknown(Boolean(allowUnknowns)); + .unknown(unknowns === 'allow') + .options({ stripUnknown: { objects: unknowns === 'ignore' } }); super(schema, typeOptions); this.props = schemaKeys; diff --git a/src/core/server/http/router/route.ts b/src/core/server/http/router/route.ts index bb0a8616e7222..9789d266587af 100644 --- a/src/core/server/http/router/route.ts +++ b/src/core/server/http/router/route.ts @@ -179,7 +179,7 @@ export interface RouteConfig { * access to raw values. * In some cases you may want to use another validation library. To do this, you need to * instruct the `@kbn/config-schema` library to output **non-validated values** with - * setting schema as `schema.object({}, { allowUnknowns: true })`; + * setting schema as `schema.object({}, { unknowns: 'allow' })`; * * @example * ```ts @@ -212,7 +212,7 @@ export interface RouteConfig { * path: 'path/{id}', * validate: { * // handler has access to raw non-validated params in runtime - * params: schema.object({}, { allowUnknowns: true }) + * params: schema.object({}, { unknowns: 'allow' }) * }, * }, * (context, req, res,) { diff --git a/src/core/server/http/router/router.test.ts b/src/core/server/http/router/router.test.ts index a936da6a40a9f..9655e2153b863 100644 --- a/src/core/server/http/router/router.test.ts +++ b/src/core/server/http/router/router.test.ts @@ -59,7 +59,7 @@ describe('Router', () => { { path: '/', options: { body: { output: 'file' } } as any, // We explicitly don't support 'file' - validate: { body: schema.object({}, { allowUnknowns: true }) }, + validate: { body: schema.object({}, { unknowns: 'allow' }) }, }, (context, req, res) => res.ok({}) ) diff --git a/src/core/server/ui_settings/routes/set_many.ts b/src/core/server/ui_settings/routes/set_many.ts index 5623c3fe11b80..d19a36a7ce768 100644 --- a/src/core/server/ui_settings/routes/set_many.ts +++ b/src/core/server/ui_settings/routes/set_many.ts @@ -24,7 +24,7 @@ import { CannotOverrideError } from '../ui_settings_errors'; const validate = { body: schema.object({ - changes: schema.object({}, { allowUnknowns: true }), + changes: schema.object({}, { unknowns: 'allow' }), }), }; diff --git a/src/core/server/ui_settings/ui_settings_config.ts b/src/core/server/ui_settings/ui_settings_config.ts index a54d482a0296a..a0ac48e2dd089 100644 --- a/src/core/server/ui_settings/ui_settings_config.ts +++ b/src/core/server/ui_settings/ui_settings_config.ts @@ -39,7 +39,7 @@ const configSchema = schema.object({ }) ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }); diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts index 03dbd40984412..b7569a22e9fc9 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -39,7 +39,7 @@ export function registerValueSuggestionsRoute( { index: schema.string(), }, - { allowUnknowns: false } + { unknowns: 'allow' } ), body: schema.object( { @@ -47,7 +47,7 @@ export function registerValueSuggestionsRoute( query: schema.string(), boolFilter: schema.maybe(schema.any()), }, - { allowUnknowns: false } + { unknowns: 'allow' } ), }, }, diff --git a/src/plugins/data/server/search/routes.ts b/src/plugins/data/server/search/routes.ts index e618f99084aed..b90d7d4ff80ce 100644 --- a/src/plugins/data/server/search/routes.ts +++ b/src/plugins/data/server/search/routes.ts @@ -28,9 +28,9 @@ export function registerSearchRoute(router: IRouter): void { validate: { params: schema.object({ strategy: schema.string() }), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }, }, async (context, request, res) => { @@ -64,7 +64,7 @@ export function registerSearchRoute(router: IRouter): void { id: schema.string(), }), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), }, }, async (context, request, res) => { diff --git a/src/plugins/timelion/config.ts b/src/plugins/timelion/config.ts index 561fb4de9f58d..eaea1aaca1b7b 100644 --- a/src/plugins/timelion/config.ts +++ b/src/plugins/timelion/config.ts @@ -25,7 +25,7 @@ export const configSchema = schema.object( graphiteUrls: schema.maybe(schema.arrayOf(schema.string())), }, // This option should be removed as soon as we entirely migrate config from legacy Timelion plugin. - { allowUnknowns: true } + { unknowns: 'allow' } ); export type ConfigSchema = TypeOf; diff --git a/src/plugins/timelion/server/routes/run.ts b/src/plugins/timelion/server/routes/run.ts index b7a4179da768e..b773bba68ea81 100644 --- a/src/plugins/timelion/server/routes/run.ts +++ b/src/plugins/timelion/server/routes/run.ts @@ -78,15 +78,11 @@ export function runRoute( es: schema.object({ filter: schema.object({ bool: schema.object({ - filter: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) - ), - must: schema.maybe(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), - should: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) - ), + filter: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + must: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + should: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), must_not: schema.maybe( - schema.arrayOf(schema.object({}, { allowUnknowns: true })) + schema.arrayOf(schema.object({}, { unknowns: 'allow' })) ), }), }), diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_type_timeseries/server/routes/vis.ts index e2d1e4d114ad5..9abbc4ad617dc 100644 --- a/src/plugins/vis_type_timeseries/server/routes/vis.ts +++ b/src/plugins/vis_type_timeseries/server/routes/vis.ts @@ -23,7 +23,7 @@ import { getVisData, GetVisDataOptions } from '../lib/get_vis_data'; import { visPayloadSchema } from './post_vis_schema'; import { Framework, ValidationTelemetryServiceSetup } from '../index'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const visDataRoutes = ( router: IRouter, diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts index fad19728b7514..3f6a8e8773e04 100644 --- a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts +++ b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts @@ -33,7 +33,7 @@ export class RenderingPlugin implements Plugin { { includeUserSettings: schema.boolean({ defaultValue: true }), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), params: schema.object({ id: schema.maybe(schema.string()), diff --git a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts index cdc929a2f3bb3..03ec583a34166 100644 --- a/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts +++ b/x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_license_route.ts @@ -15,7 +15,7 @@ export function registerLicenseRoute(server: Server, legacy: Legacy, xpackInfo: validate: { query: schema.object({ acknowledge: schema.string() }), body: schema.object({ - license: schema.object({}, { allowUnknowns: true }), + license: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts index e58bc95b9a375..e45713e2b807c 100644 --- a/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts +++ b/x-pack/legacy/plugins/rollup/server/routes/api/jobs.ts @@ -127,7 +127,7 @@ export function registerJobsRoute(deps: RouteDependencies, legacy: ServerShim) { { id: schema.string(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }), }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts index d1726f93108c7..adbb5fa618957 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_params_schema.ts @@ -26,13 +26,13 @@ export const signalParamsSchema = () => savedId: schema.nullable(schema.string()), timelineId: schema.nullable(schema.string()), timelineTitle: schema.nullable(schema.string()), - meta: schema.nullable(schema.object({}, { allowUnknowns: true })), + meta: schema.nullable(schema.object({}, { unknowns: 'allow' })), query: schema.nullable(schema.string()), - filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + filters: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), maxSignals: schema.number({ defaultValue: DEFAULT_MAX_SIGNALS }), riskScore: schema.number(), severity: schema.string(), - threat: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), + threat: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), to: schema.string(), type: schema.string(), references: schema.arrayOf(schema.string(), { defaultValue: [] }), diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 7d42149223b32..004ac36bad5b4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -61,7 +61,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { this.router.post( { path: routePath, - validate: { body: configSchema.object({}, { allowUnknowns: true }) }, + validate: { body: configSchema.object({}, { unknowns: 'allow' }) }, options: { tags: ['access:siem'], }, diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index a84a24cea17d2..e216574f8a02e 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -71,7 +71,7 @@ export function createApi() { body: bodyRt || t.null }; - const anyObject = schema.object({}, { allowUnknowns: true }); + const anyObject = schema.object({}, { unknowns: 'allow' }); (router[routerMethod] as RouteRegistrar)( { diff --git a/x-pack/plugins/canvas/server/routes/workpad/update.ts b/x-pack/plugins/canvas/server/routes/workpad/update.ts index 83b8fef48e9be..64736bcd57fd5 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/update.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/update.ts @@ -120,7 +120,7 @@ export function initializeUpdateWorkpadAssetsRoute(deps: RouteInitializerDeps) { // ToDo: Currently the validation must be a schema.object // Because we don't know what keys the assets will have, we have to allow // unknowns and then validate in the handler - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }, options: { body: { diff --git a/x-pack/plugins/case/server/routes/api/utils.ts b/x-pack/plugins/case/server/routes/api/utils.ts index 04fe426bb2ecc..27ee6fc58e20a 100644 --- a/x-pack/plugins/case/server/routes/api/utils.ts +++ b/x-pack/plugins/case/server/routes/api/utils.ts @@ -141,4 +141,4 @@ export const sortToSnake = (sortField: string): SortFieldCase => { } }; -export const escapeHatch = schema.object({}, { allowUnknowns: true }); +export const escapeHatch = schema.object({}, { unknowns: 'allow' }); diff --git a/x-pack/plugins/file_upload/server/routes/file_upload.js b/x-pack/plugins/file_upload/server/routes/file_upload.js index acbc907729d95..d75f03132b404 100644 --- a/x-pack/plugins/file_upload/server/routes/file_upload.js +++ b/x-pack/plugins/file_upload/server/routes/file_upload.js @@ -28,12 +28,12 @@ export const bodySchema = schema.object( {}, { defaultValue: {}, - allowUnknowns: true, + unknowns: 'allow', } ) ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); const options = { @@ -48,7 +48,7 @@ export const idConditionalValidation = (body, boolHasId) => .object( { data: boolHasId - ? schema.arrayOf(schema.object({}, { allowUnknowns: true }), { minSize: 1 }) + ? schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { minSize: 1 }) : schema.any(), settings: boolHasId ? schema.any() @@ -58,7 +58,7 @@ export const idConditionalValidation = (body, boolHasId) => defaultValue: { number_of_shards: 1, }, - allowUnknowns: true, + unknowns: 'allow', } ), mappings: boolHasId @@ -67,11 +67,11 @@ export const idConditionalValidation = (body, boolHasId) => {}, { defaultValue: {}, - allowUnknowns: true, + unknowns: 'allow', } ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ) .validate(body); diff --git a/x-pack/plugins/graph/server/routes/explore.ts b/x-pack/plugins/graph/server/routes/explore.ts index 125378891151b..ceced840bdbc6 100644 --- a/x-pack/plugins/graph/server/routes/explore.ts +++ b/x-pack/plugins/graph/server/routes/explore.ts @@ -23,7 +23,7 @@ export function registerExploreRoute({ validate: { body: schema.object({ index: schema.string(), - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/graph/server/routes/search.ts b/x-pack/plugins/graph/server/routes/search.ts index 91b404dc7cb91..6e9fe508af3d3 100644 --- a/x-pack/plugins/graph/server/routes/search.ts +++ b/x-pack/plugins/graph/server/routes/search.ts @@ -21,7 +21,7 @@ export function registerSearchRoute({ validate: { body: schema.object({ index: schema.string(), - body: schema.object({}, { allowUnknowns: true }), + body: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts index fb5d41870eece..8bf2774ac38b3 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts @@ -11,9 +11,9 @@ export const templateSchema = schema.object({ indexPatterns: schema.arrayOf(schema.string()), version: schema.maybe(schema.number()), order: schema.maybe(schema.number()), - settings: schema.maybe(schema.object({}, { allowUnknowns: true })), - aliases: schema.maybe(schema.object({}, { allowUnknowns: true })), - mappings: schema.maybe(schema.object({}, { allowUnknowns: true })), + settings: schema.maybe(schema.object({}, { unknowns: 'allow' })), + aliases: schema.maybe(schema.object({}, { unknowns: 'allow' })), + mappings: schema.maybe(schema.object({}, { unknowns: 'allow' })), ilmPolicy: schema.maybe( schema.object({ name: schema.maybe(schema.string()), diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 6ff749c040220..e2ff93ce356e6 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -76,7 +76,7 @@ export class KibanaFramework { public registerGraphQLEndpoint(routePath: string, gqlSchema: GraphQLSchema) { // These endpoints are validated by GraphQL at runtime and with GraphQL generated types - const body = schema.object({}, { allowUnknowns: true }); + const body = schema.object({}, { unknowns: 'allow' }); type Body = TypeOf; const routeOptions = { diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts index 33328bdfebaf4..7e9b7ada28c8e 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/index.ts @@ -18,7 +18,7 @@ import { } from '../../../common/http_api/inventory_meta_api'; import { getCloudMetadata } from './lib/get_cloud_metadata'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initInventoryMetaRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts index 7eb7de57b2f92..6852a102afc86 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_categories.ts @@ -19,7 +19,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoriesRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts index 8132633028277..730e32dee2fbe 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_datasets.ts @@ -19,7 +19,7 @@ import { throwErrors } from '../../../../common/runtime_types'; import { InfraBackendLibs } from '../../../lib/infra_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoryDatasetsRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts index 67c6c9f5b9924..44f466cc77c89 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_category_examples.ts @@ -19,7 +19,7 @@ import { throwErrors } from '../../../../common/runtime_types'; import { InfraBackendLibs } from '../../../lib/infra_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryCategoryExamplesRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts index 6551316fd0c64..38dc0a790a7a3 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts @@ -20,7 +20,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; import { NoLogAnalysisResultsIndexError } from '../../../lib/log_analysis'; -const anyObject = schema.object({}, { allowUnknowns: true }); +const anyObject = schema.object({}, { unknowns: 'allow' }); export const initGetLogEntryRateRoute = ({ framework, logEntryRateAnalysis }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts index fe579124cfe10..54ae0b4529daa 100644 --- a/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts +++ b/x-pack/plugins/infra/server/routes/log_analysis/validation/indices.ts @@ -19,7 +19,7 @@ import { import { throwErrors } from '../../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initValidateLogAnalysisIndicesRoute = ({ framework }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/entries.ts b/x-pack/plugins/infra/server/routes/log_entries/entries.ts index 361535886ab22..93802468dd267 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/entries.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/entries.ts @@ -22,7 +22,7 @@ import { import { parseFilterQuery } from '../../utils/serialized_query'; import { LogEntriesParams } from '../../lib/domains/log_entries_domain'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/highlights.ts index 8af81a6ee313d..8ee412d5acdd5 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/highlights.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/highlights.ts @@ -22,7 +22,7 @@ import { import { parseFilterQuery } from '../../utils/serialized_query'; import { LogEntriesParams } from '../../lib/domains/log_entries_domain'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesHighlightsRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/item.ts b/x-pack/plugins/infra/server/routes/log_entries/item.ts index 22663cb2001f0..3a6bdaf3804e3 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/item.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/item.ts @@ -20,7 +20,7 @@ import { logEntriesItemResponseRT, } from '../../../common/http_api'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesItemRoute = ({ framework, sources, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary.ts b/x-pack/plugins/infra/server/routes/log_entries/summary.ts index 05643adbe781f..3f5bc8e364a58 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/summary.ts @@ -21,7 +21,7 @@ import { } from '../../../common/http_api/log_entries'; import { parseFilterQuery } from '../../utils/serialized_query'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesSummaryRoute = ({ framework, logEntries }: InfraBackendLibs) => { framework.registerRoute( diff --git a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts b/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts index ecccd931bb371..6c6f7a5a3dcd3 100644 --- a/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts +++ b/x-pack/plugins/infra/server/routes/log_entries/summary_highlights.ts @@ -21,7 +21,7 @@ import { } from '../../../common/http_api/log_entries'; import { parseFilterQuery } from '../../utils/serialized_query'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initLogEntriesSummaryHighlightsRoute = ({ framework, diff --git a/x-pack/plugins/infra/server/routes/metadata/index.ts b/x-pack/plugins/infra/server/routes/metadata/index.ts index a1f6311a103eb..03d28110d612a 100644 --- a/x-pack/plugins/infra/server/routes/metadata/index.ts +++ b/x-pack/plugins/infra/server/routes/metadata/index.ts @@ -23,7 +23,7 @@ import { getCloudMetricsMetadata } from './lib/get_cloud_metric_metadata'; import { getNodeInfo } from './lib/get_node_info'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initMetadataRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts index 64cdb9318b6e1..c22095a31195a 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/index.ts @@ -15,7 +15,7 @@ import { populateSeriesWithTSVBData } from './lib/populate_series_with_tsvb_data import { metricsExplorerRequestBodyRT, metricsExplorerResponseRT } from '../../../common/http_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initMetricExplorerRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/node_details/index.ts b/x-pack/plugins/infra/server/routes/node_details/index.ts index 4a09615f0a17c..36906f6f4125b 100644 --- a/x-pack/plugins/infra/server/routes/node_details/index.ts +++ b/x-pack/plugins/infra/server/routes/node_details/index.ts @@ -18,7 +18,7 @@ import { } from '../../../common/http_api/node_details_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initNodeDetailsRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts index 5f28e41d80c25..e45b9884967d0 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts @@ -14,7 +14,7 @@ import { parseFilterQuery } from '../../utils/serialized_query'; import { SnapshotRequestRT, SnapshotNodeResponseRT } from '../../../common/http_api/snapshot_api'; import { throwErrors } from '../../../common/runtime_types'; -const escapeHatch = schema.object({}, { allowUnknowns: true }); +const escapeHatch = schema.object({}, { unknowns: 'allow' }); export const initSnapshotRoute = (libs: InfraBackendLibs) => { const { framework } = libs; diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 57c1680413537..b1964a9150982 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -55,7 +55,7 @@ export async function existingFieldsRoute(setup: CoreSetup) { indexPatternId: schema.string(), }), body: schema.object({ - dslQuery: schema.object({}, { allowUnknowns: true }), + dslQuery: schema.object({}, { unknowns: 'allow' }), fromDate: schema.maybe(schema.string()), toDate: schema.maybe(schema.string()), timeFieldName: schema.maybe(schema.string()), diff --git a/x-pack/plugins/lens/server/routes/field_stats.ts b/x-pack/plugins/lens/server/routes/field_stats.ts index 786aba5efe3fb..5c91be9dfbd78 100644 --- a/x-pack/plugins/lens/server/routes/field_stats.ts +++ b/x-pack/plugins/lens/server/routes/field_stats.ts @@ -24,7 +24,7 @@ export async function initFieldsRoute(setup: CoreSetup) { }), body: schema.object( { - dslQuery: schema.object({}, { allowUnknowns: true }), + dslQuery: schema.object({}, { unknowns: 'allow' }), fromDate: schema.string(), toDate: schema.string(), timeFieldName: schema.maybe(schema.string()), @@ -34,10 +34,10 @@ export async function initFieldsRoute(setup: CoreSetup) { type: schema.string(), esTypes: schema.maybe(schema.arrayOf(schema.string())), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, }, diff --git a/x-pack/plugins/searchprofiler/server/routes/profile.ts b/x-pack/plugins/searchprofiler/server/routes/profile.ts index c47ab81b2ab7e..4af3f0519cbc0 100644 --- a/x-pack/plugins/searchprofiler/server/routes/profile.ts +++ b/x-pack/plugins/searchprofiler/server/routes/profile.ts @@ -12,7 +12,7 @@ export const register = ({ router, getLicenseStatus, log }: RouteDependencies) = path: '/api/searchprofiler/profile', validate: { body: schema.object({ - query: schema.object({}, { allowUnknowns: true }), + query: schema.object({}, { unknowns: 'allow' }), index: schema.string(), }), }, diff --git a/x-pack/plugins/security/server/routes/authentication/common.ts b/x-pack/plugins/security/server/routes/authentication/common.ts index c9856e9dff7f1..19d197b63f540 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.ts @@ -21,7 +21,7 @@ export function defineCommonRoutes({ router, authc, basePath, logger }: RouteDef path, // Allow unknown query parameters as this endpoint can be hit by the 3rd-party with any // set of query string parameters (e.g. SAML/OIDC logout request parameters). - validate: { query: schema.object({}, { allowUnknowns: true }) }, + validate: { query: schema.object({}, { unknowns: 'allow' }) }, options: { authRequired: false }, }, async (context, request, response) => { diff --git a/x-pack/plugins/security/server/routes/authentication/oidc.ts b/x-pack/plugins/security/server/routes/authentication/oidc.ts index 232fdd26f7838..96c36af20e982 100644 --- a/x-pack/plugins/security/server/routes/authentication/oidc.ts +++ b/x-pack/plugins/security/server/routes/authentication/oidc.ts @@ -103,7 +103,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route // The client MUST ignore unrecognized response parameters according to // https://openid.net/specs/openid-connect-core-1_0.html#AuthResponseValidation and // https://tools.ietf.org/html/rfc6749#section-4.1.2. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, @@ -178,7 +178,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route }, // Other parameters MAY be sent, if defined by extensions. Any parameters used that are not understood MUST // be ignored by the Client according to https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, @@ -217,7 +217,7 @@ export function defineOIDCRoutes({ router, logger, authc, csp, basePath }: Route }, // Other parameters MAY be sent, if defined by extensions. Any parameters used that are not understood MUST // be ignored by the Client according to https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin. - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, diff --git a/x-pack/plugins/security/server/routes/role_mapping/post.ts b/x-pack/plugins/security/server/routes/role_mapping/post.ts index bf9112be4ad3f..11149f38069a7 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/post.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/post.ts @@ -36,8 +36,8 @@ export function defineRoleMappingPostRoutes(params: RouteDefinitionParams) { // and keeping this in sync (and testable!) with ES could prove problematic. // We do not interpret any of these rules within this route handler; // they are simply passed to ES for processing. - rules: schema.object({}, { allowUnknowns: true }), - metadata: schema.object({}, { allowUnknowns: true }), + rules: schema.object({}, { unknowns: 'allow' }), + metadata: schema.object({}, { unknowns: 'allow' }), }), }, }, diff --git a/x-pack/plugins/security/server/routes/views/login.ts b/x-pack/plugins/security/server/routes/views/login.ts index e2e162d298e45..ee1fe01ab1b22 100644 --- a/x-pack/plugins/security/server/routes/views/login.ts +++ b/x-pack/plugins/security/server/routes/views/login.ts @@ -28,7 +28,7 @@ export function defineLoginRoutes({ next: schema.maybe(schema.string()), msg: schema.maybe(schema.string()), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), }, options: { authRequired: false }, diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index f6f8bb4de4d83..e5df0ec33db0b 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -37,9 +37,9 @@ export const policySchema = schema.object({ config: schema.maybe(snapshotConfigSchema), retention: schema.maybe(snapshotRetentionSchema), isManagedPolicy: schema.boolean(), - stats: schema.maybe(schema.object({}, { allowUnknowns: true })), - lastFailure: schema.maybe(schema.object({}, { allowUnknowns: true })), - lastSuccess: schema.maybe(schema.object({}, { allowUnknowns: true })), + stats: schema.maybe(schema.object({}, { unknowns: 'allow' })), + lastFailure: schema.maybe(schema.object({}, { unknowns: 'allow' })), + lastSuccess: schema.maybe(schema.object({}, { unknowns: 'allow' })), }); const fsRepositorySettings = schema.object({ @@ -100,7 +100,7 @@ const hdsRepositorySettings = schema.object( readonly: schema.maybe(schema.boolean()), ['security.principal']: schema.maybe(schema.string()), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); const hdsfRepository = schema.object({ @@ -158,7 +158,7 @@ const sourceRepository = schema.object({ { delegateType: schema.string(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ), ]), }); diff --git a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts index df6f62135baeb..a1184cbebd139 100644 --- a/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/indices/register_get_route.ts @@ -11,7 +11,7 @@ import { isEsError } from '../../../lib/is_es_error'; import { RouteDependencies } from '../../../types'; import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; -const bodySchema = schema.object({ pattern: schema.string() }, { allowUnknowns: true }); +const bodySchema = schema.object({ pattern: schema.string() }, { unknowns: 'allow' }); function getIndexNamesFromAliasesResponse(json: Record) { return reduce( diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts index 7aaa77c05a5f0..14a14a6f64d7b 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_execute_route.ts @@ -19,8 +19,8 @@ import { Watch } from '../../../models/watch/index'; import { WatchHistoryItem } from '../../../models/watch_history_item/index'; const bodySchema = schema.object({ - executeDetails: schema.object({}, { allowUnknowns: true }), - watch: schema.object({}, { allowUnknowns: true }), + executeDetails: schema.object({}, { unknowns: 'allow' }), + watch: schema.object({}, { unknowns: 'allow' }), }); function executeWatch(dataClient: IScopedClusterClient, executeDetails: any, watchJson: any) { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts index 572790f12a5f8..61d167bb9bbcd 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_save_route.ts @@ -22,7 +22,7 @@ const bodySchema = schema.object( type: schema.string(), isNew: schema.boolean(), }, - { allowUnknowns: true } + { unknowns: 'allow' } ); function fetchWatch(dataClient: IScopedClusterClient, watchId: string) { diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts index 200b35953b6f2..90550731bf23a 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts @@ -16,8 +16,8 @@ import { Watch } from '../../../models/watch/index'; import { VisualizeOptions } from '../../../models/visualize_options/index'; const bodySchema = schema.object({ - watch: schema.object({}, { allowUnknowns: true }), - options: schema.object({}, { allowUnknowns: true }), + watch: schema.object({}, { unknowns: 'allow' }), + options: schema.object({}, { unknowns: 'allow' }), }); function fetchVisualizeData(dataClient: IScopedClusterClient, index: any, body: any) { From 156066dc6fa512169f1af4244f5c4cdefb61be14 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Tue, 17 Mar 2020 12:34:03 -0400 Subject: [PATCH 02/35] [Fleet] Add config revision to fleet agents (#60292) --- .../common/types/models/agent.ts | 3 +- .../sections/fleet/agent_list_page/index.tsx | 45 +++++-- .../ingest_manager/server/saved_objects.ts | 3 +- .../server/services/agents/acks.ts | 14 +++ .../server/services/agents/checkin.test.ts | 117 ++++++++++++++++++ .../server/services/agents/checkin.ts | 35 +++++- .../server/services/agents/enroll.ts | 1 - .../server/services/agents/update.ts | 8 +- 8 files changed, 205 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index ad06e8d3c9c11..179cc3fc9eb55 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -56,8 +56,9 @@ interface AgentBase { access_api_key_id?: string; default_api_key?: string; config_id?: string; + config_revision?: number; + config_newest_revision?: number; last_checkin?: string; - config_updated_at?: string; actions: AgentAction[]; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index acf09dedc25f7..14a579eb72598 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -26,6 +26,7 @@ import { EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem, + EuiIcon, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react'; @@ -289,6 +290,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }, { field: 'active', + width: '100px', name: i18n.translate('xpack.ingestManager.agentList.statusColumnTitle', { defaultMessage: 'Status', }), @@ -299,10 +301,10 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { name: i18n.translate('xpack.ingestManager.agentList.configColumnTitle', { defaultMessage: 'Configuration', }), - render: (configId: string) => { + render: (configId: string, agent: Agent) => { const configName = agentConfigs.find(p => p.id === configId)?.name; return ( - + = () => { {configName || configId} - - - - - + {agent.config_revision && ( + + + + + + )} + {agent.config_revision && + agent.config_newest_revision && + agent.config_newest_revision > agent.config_revision && ( + + + +   + {true && ( + <> + + + )} + + + )} ); }, }, { field: 'local_metadata.agent_version', + width: '100px', name: i18n.translate('xpack.ingestManager.agentList.versionTitle', { defaultMessage: 'Version', }), diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 860b95b58c7f7..31cf173c3e4f9 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -32,7 +32,8 @@ export const savedObjectMappings = { config_id: { type: 'keyword' }, last_updated: { type: 'date' }, last_checkin: { type: 'date' }, - config_updated_at: { type: 'date' }, + config_revision: { type: 'integer' }, + config_newest_revision: { type: 'integer' }, // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 default_api_key: { type: 'keyword' }, updated_at: { type: 'date' }, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index 98a5f69f9d2b0..cf9a47979ae8b 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -51,8 +51,22 @@ export async function acknowledgeAgentActions( }); if (matchedUpdatedActions.length > 0) { + const configRevision = matchedUpdatedActions.reduce((acc, action) => { + if (action.type !== 'CONFIG_CHANGE') { + return acc; + } + const data = action.data ? JSON.parse(action.data as string) : {}; + + if (data?.config?.id !== agent.config_id) { + return acc; + } + + return data?.config?.revision > acc ? data?.config?.revision : acc; + }, agent.config_revision || 0); + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { actions: matchedUpdatedActions, + config_revision: configRevision, }); } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts new file mode 100644 index 0000000000000..d3e10fcb6b63f --- /dev/null +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shouldCreateConfigAction } from './checkin'; +import { Agent } from '../../types'; + +function getAgent(data: Partial) { + return { actions: [], ...data } as Agent; +} + +describe('Agent checkin service', () => { + describe('shouldCreateConfigAction', () => { + it('should return false if the agent do not have an assigned config', () => { + const res = shouldCreateConfigAction(getAgent({})); + + expect(res).toBeFalsy(); + }); + + it('should return true if this is agent first checkin', () => { + const res = shouldCreateConfigAction(getAgent({ config_id: 'config1' })); + + expect(res).toBeTruthy(); + }); + + it('should return false agent is already running latest revision', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 1, + }) + ); + + expect(res).toBeFalsy(); + }); + + it('should return false agent has already latest revision config change action', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + actions: [ + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 2, + }, + }), + }, + ], + }) + ); + + expect(res).toBeFalsy(); + }); + + it('should return true agent has unrelated config change actions', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + actions: [ + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config2', + revision: 2, + }, + }), + }, + { + id: 'action1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 1, + }, + }), + }, + ], + }) + ); + + expect(res).toBeTruthy(); + }); + + it('should return true if this agent has a new revision', () => { + const res = shouldCreateConfigAction( + getAgent({ + config_id: 'config1', + last_checkin: '2018-01-02T00:00:00', + config_revision: 1, + config_newest_revision: 2, + }) + ); + + expect(res).toBeTruthy(); + }); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 0ff4af4ffe351..d80fff5d8eceb 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -37,7 +37,7 @@ export async function agentCheckin( const actions = filterActionsForCheckin(agent); // Generate new agent config if config is updated - if (isNewAgentConfig(agent) && agent.config_id) { + if (agent.config_id && shouldCreateConfigAction(agent)) { const config = await agentConfigService.getFullConfig(soClient, agent.config_id); if (config) { // Assign output API keys @@ -149,12 +149,37 @@ function isActionEvent(event: AgentEvent) { ); } -function isNewAgentConfig(agent: Agent) { +export function shouldCreateConfigAction(agent: Agent): boolean { + if (!agent.config_id) { + return false; + } + const isFirstCheckin = !agent.last_checkin; - const isConfigUpdatedSinceLastCheckin = - agent.last_checkin && agent.config_updated_at && agent.last_checkin <= agent.config_updated_at; + if (isFirstCheckin) { + return true; + } + + const isAgentConfigOutdated = + agent.config_revision && + agent.config_newest_revision && + agent.config_revision < agent.config_newest_revision; + if (!isAgentConfigOutdated) { + return false; + } + + const isActionAlreadyGenerated = !!agent.actions.find(action => { + if (!action.data || action.type !== 'CONFIG_CHANGE') { + return false; + } + + const data = JSON.parse(action.data); + + return ( + data.config.id === agent.config_id && data.config.revision === agent.config_newest_revision + ); + }); - return isFirstCheckin || isConfigUpdatedSinceLastCheckin; + return !isActionAlreadyGenerated; } function filterActionsForCheckin(agent: Agent): AgentAction[] { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index 0f73f71817eb0..52547e9bcb0fb 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -37,7 +37,6 @@ export async function enroll( current_error_events: undefined, actions: [], access_api_key_id: undefined, - config_updated_at: undefined, last_checkin: undefined, default_api_key: undefined, }; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/update.ts b/x-pack/plugins/ingest_manager/server/services/agents/update.ts index 9eabf0944bdc4..59d0ad31d1a64 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/update.ts @@ -8,14 +8,18 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { unenrollAgents } from './unenroll'; +import { agentConfigService } from '../agent_config'; export async function updateAgentsForConfigId( soClient: SavedObjectsClientContract, configId: string ) { + const config = await agentConfigService.get(soClient, configId); + if (!config) { + throw new Error('Config not found'); + } let hasMore = true; let page = 1; - const now = new Date().toISOString(); while (hasMore) { const { agents } = await listAgents(soClient, { kuery: `agents.config_id:"${configId}"`, @@ -30,7 +34,7 @@ export async function updateAgentsForConfigId( const agentUpdate = agents.map(agent => ({ id: agent.id, type: AGENT_SAVED_OBJECT_TYPE, - attributes: { config_updated_at: now }, + attributes: { config_newest_revision: config.revision }, })); await soClient.bulkUpdate(agentUpdate); From cea277e7c288cb2d4bf90acca2d57b80da7438d4 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 17 Mar 2020 13:06:12 -0400 Subject: [PATCH 03/35] [SIEM][Detections Engine] - Add rule markdown field to rule create, detail, and edit flows (#60108) * add rule note markdown field to rule creation, rule details, and rule edit flows Co-authored-by: Gloria Hornero Co-authored-by: Elastic Machine --- .../signal_detection_rules.spec.ts | 108 ++-- .../siem/cypress/screens/rule_details.ts | 28 +- .../run_check_circular_deps_cli.js | 10 + .../detection_engine/rules/types.ts | 2 + .../rules/all/__mocks__/mock.ts | 153 +++++ .../__snapshots__/index.test.tsx.snap | 453 ++++++++++++++ .../description_step/helpers.test.tsx | 403 ++++++++++++ .../components/description_step/helpers.tsx | 91 ++- .../description_step/index.test.tsx | 297 ++++++++- .../components/description_step/index.tsx | 58 +- .../step_about_rule/default_value.ts | 1 + .../components/step_about_rule/index.test.tsx | 155 +++++ .../components/step_about_rule/index.tsx | 140 +++-- .../components/step_about_rule/schema.tsx | 20 +- .../step_about_rule/translations.ts | 7 + .../step_about_rule_details/index.test.tsx | 175 ++++++ .../step_about_rule_details/index.tsx | 147 +++++ .../step_about_rule_details/translations.ts | 27 + .../components/step_define_rule/index.tsx | 6 +- .../components/step_schedule_rule/index.tsx | 51 +- .../rules/create/helpers.test.ts | 589 ++++++++++++++++++ .../detection_engine/rules/create/helpers.ts | 20 +- .../detection_engine/rules/create/index.tsx | 6 +- .../detection_engine/rules/details/index.tsx | 58 +- .../detection_engine/rules/helpers.test.tsx | 291 +++++++++ .../pages/detection_engine/rules/helpers.tsx | 156 +++-- .../pages/detection_engine/rules/types.ts | 9 +- .../pages/detection_engine/translations.ts | 2 +- 28 files changed, 3166 insertions(+), 297 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.test.tsx diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index 8c384c9010665..ce73fe1b7c2a5 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -7,30 +7,30 @@ import { newRule } from '../objects/rule'; import { - ABOUT_DESCRIPTION, - ABOUT_EXPECTED_URLS, ABOUT_FALSE_POSITIVES, ABOUT_MITRE, ABOUT_RISK, - ABOUT_RULE_DESCRIPTION, ABOUT_SEVERITY, + ABOUT_STEP, ABOUT_TAGS, ABOUT_TIMELINE, + ABOUT_URLS, DEFINITION_CUSTOM_QUERY, - DEFINITION_DESCRIPTION, DEFINITION_INDEX_PATTERNS, + DEFINITION_STEP, RULE_NAME_HEADER, - SCHEDULE_DESCRIPTION, SCHEDULE_LOOPBACK, SCHEDULE_RUNS, + SCHEDULE_STEP, + ABOUT_RULE_DESCRIPTION, } from '../screens/rule_details'; import { CUSTOM_RULES_BTN, ELASTIC_RULES_BTN, RISK_SCORE, RULE_NAME, - RULES_TABLE, RULES_ROW, + RULES_TABLE, SEVERITY, } from '../screens/signal_detection_rules'; @@ -127,10 +127,25 @@ describe('Signal detection rules', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER) - .invoke('text') - .should('eql', `${newRule.name} Beta`); - + let expectedUrls = ''; + newRule.referenceUrls.forEach(url => { + expectedUrls = expectedUrls + url; + }); + let expectedFalsePositives = ''; + newRule.falsePositivesExamples.forEach(falsePositive => { + expectedFalsePositives = expectedFalsePositives + falsePositive; + }); + let expectedTags = ''; + newRule.tags.forEach(tag => { + expectedTags = expectedTags + tag; + }); + let expectedMitre = ''; + newRule.mitre.forEach(mitre => { + expectedMitre = expectedMitre + mitre.tactic; + mitre.techniques.forEach(technique => { + expectedMitre = expectedMitre + technique; + }); + }); const expectedIndexPatterns = [ 'apm-*-transaction*', 'auditbeat-*', @@ -139,77 +154,60 @@ describe('Signal detection rules', () => { 'packetbeat-*', 'winlogbeat-*', ]; - cy.get(DEFINITION_INDEX_PATTERNS).then(patterns => { - cy.wrap(patterns).each((pattern, index) => { - cy.wrap(pattern) - .invoke('text') - .should('eql', expectedIndexPatterns[index]); - }); - }); - cy.get(DEFINITION_DESCRIPTION) - .eq(DEFINITION_CUSTOM_QUERY) + + cy.get(RULE_NAME_HEADER) .invoke('text') - .should('eql', `${newRule.customQuery} `); - cy.get(ABOUT_DESCRIPTION) - .eq(ABOUT_RULE_DESCRIPTION) + .should('eql', `${newRule.name} Beta`); + + cy.get(ABOUT_RULE_DESCRIPTION) .invoke('text') .should('eql', newRule.description); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_SEVERITY) .invoke('text') .should('eql', newRule.severity); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_RISK) .invoke('text') .should('eql', newRule.riskScore); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_TIMELINE) .invoke('text') .should('eql', 'Default blank timeline'); - - let expectedUrls = ''; - newRule.referenceUrls.forEach(url => { - expectedUrls = expectedUrls + url; - }); - cy.get(ABOUT_DESCRIPTION) - .eq(ABOUT_EXPECTED_URLS) + cy.get(ABOUT_STEP) + .eq(ABOUT_URLS) .invoke('text') .should('eql', expectedUrls); - - let expectedFalsePositives = ''; - newRule.falsePositivesExamples.forEach(falsePositive => { - expectedFalsePositives = expectedFalsePositives + falsePositive; - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_FALSE_POSITIVES) .invoke('text') .should('eql', expectedFalsePositives); - - let expectedMitre = ''; - newRule.mitre.forEach(mitre => { - expectedMitre = expectedMitre + mitre.tactic; - mitre.techniques.forEach(technique => { - expectedMitre = expectedMitre + technique; - }); - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_MITRE) .invoke('text') .should('eql', expectedMitre); - - let expectedTags = ''; - newRule.tags.forEach(tag => { - expectedTags = expectedTags + tag; - }); - cy.get(ABOUT_DESCRIPTION) + cy.get(ABOUT_STEP) .eq(ABOUT_TAGS) .invoke('text') .should('eql', expectedTags); - cy.get(SCHEDULE_DESCRIPTION) + + cy.get(DEFINITION_INDEX_PATTERNS).then(patterns => { + cy.wrap(patterns).each((pattern, index) => { + cy.wrap(pattern) + .invoke('text') + .should('eql', expectedIndexPatterns[index]); + }); + }); + cy.get(DEFINITION_STEP) + .eq(DEFINITION_CUSTOM_QUERY) + .invoke('text') + .should('eql', `${newRule.customQuery} `); + + cy.get(SCHEDULE_STEP) .eq(SCHEDULE_RUNS) .invoke('text') .should('eql', '5m'); - cy.get(SCHEDULE_DESCRIPTION) + cy.get(SCHEDULE_STEP) .eq(SCHEDULE_LOOPBACK) .invoke('text') .should('eql', '1m'); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts index 46da52cd0ddd8..6c16735ba5f24 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts @@ -4,35 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ABOUT_DESCRIPTION = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; +export const ABOUT_FALSE_POSITIVES = 4; -export const ABOUT_EXPECTED_URLS = 4; +export const ABOUT_MITRE = 5; -export const ABOUT_FALSE_POSITIVES = 5; +export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]'; -export const ABOUT_MITRE = 6; +export const ABOUT_RISK = 1; -export const ABOUT_RULE_DESCRIPTION = 0; +export const ABOUT_SEVERITY = 0; -export const ABOUT_RISK = 2; +export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description'; -export const ABOUT_SEVERITY = 1; +export const ABOUT_TAGS = 6; -export const ABOUT_TAGS = 7; +export const ABOUT_TIMELINE = 2; -export const ABOUT_TIMELINE = 3; +export const ABOUT_URLS = 3; export const DEFINITION_CUSTOM_QUERY = 1; -export const DEFINITION_DESCRIPTION = - '[data-test-subj="definition"] .euiDescriptionList__description'; - export const DEFINITION_INDEX_PATTERNS = - '[data-test-subj="definition"] .euiDescriptionList__description .euiBadge__text'; + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text'; + +export const DEFINITION_STEP = + '[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description'; export const RULE_NAME_HEADER = '[data-test-subj="header-page-title"]'; -export const SCHEDULE_DESCRIPTION = '[data-test-subj="schedule"] .euiDescriptionList__description'; +export const SCHEDULE_STEP = '[data-test-subj="schedule"] .euiDescriptionList__description'; export const SCHEDULE_RUNS = 0; diff --git a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js index 8ca61b2397d8b..f3a97f5b9c9b6 100644 --- a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js +++ b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js @@ -17,6 +17,16 @@ run( [resolve(__dirname, '../../public'), resolve(__dirname, '../../common')], { fileExtensions: ['ts', 'js', 'tsx'], + excludeRegExp: [ + 'test.ts$', + 'test.tsx$', + 'containers/detection_engine/rules/types.ts$', + 'core/public/chrome/chrome_service.tsx$', + 'src/core/server/types.ts$', + 'src/core/server/saved_objects/types.ts$', + 'src/core/public/overlays/banners/banners_service.tsx$', + 'src/core/public/saved_objects/saved_objects_client.ts$', + ], } ); diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index 4d2aec4ee8740..f962204c6b1b4 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -33,6 +33,7 @@ export const NewRuleSchema = t.intersection([ threat: t.array(t.unknown), to: t.string, updated_by: t.string, + note: t.string, }), ]); @@ -86,6 +87,7 @@ export const RuleSchema = t.intersection([ status_date: t.string, timeline_id: t.string, timeline_title: t.string, + note: t.string, version: t.number, }), ]); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts index e2287e5eeeb3f..5627d33818500 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts @@ -4,7 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ +import { esFilters } from '../../../../../../../../../../src/plugins/data/public'; import { Rule, RuleError } from '../../../../../containers/detection_engine/rules'; +import { AboutStepRule, DefineStepRule, ScheduleStepRule } from '../../types'; +import { FieldValueQueryBar } from '../../components/query_bar'; + +export const mockQueryBar: FieldValueQueryBar = { + query: { + query: 'test query', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', +}; export const mockRule = (id: string): Rule => ({ created_at: '2020-01-10T21:11:45.839Z', @@ -37,9 +70,129 @@ export const mockRule = (id: string): Rule => ({ to: 'now', type: 'saved_query', threat: [], + note: '# this is some markdown documentation', version: 1, }); +export const mockRuleWithEverything = (id: string): Rule => ({ + created_at: '2020-01-10T21:11:45.839Z', + updated_at: '2020-01-10T21:11:45.839Z', + created_by: 'elastic', + description: '24/7', + enabled: true, + false_positives: ['test'], + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + from: 'now-300s', + id, + immutable: false, + index: ['auditbeat-*'], + interval: '5m', + rule_id: 'b5ba41ab-aaf3-4f43-971b-bdf9434ce0ea', + language: 'kuery', + output_index: '.siem-signals-default', + max_signals: 100, + risk_score: 21, + name: 'Query with rule-id', + query: 'user.name: root or user.name: admin', + references: ['www.test.co'], + saved_id: 'test123', + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + meta: { from: '0m' }, + severity: 'low', + updated_by: 'elastic', + tags: ['tag1', 'tag2'], + to: 'now', + type: 'saved_query', + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + note: '# this is some markdown documentation', + version: 1, +}); + +export const mockAboutStepRule = (isNew = false): AboutStepRule => ({ + isNew, + name: 'Query with rule-id', + description: '24/7', + severity: 'low', + riskScore: 21, + references: ['www.test.co'], + falsePositives: ['test'], + tags: ['tag1', 'tag2'], + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: 'Titled timeline', + }, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + note: '# this is some markdown documentation', +}); + +export const mockDefineStepRule = (isNew = false): DefineStepRule => ({ + isNew, + index: ['filebeat-'], + queryBar: mockQueryBar, +}); + +export const mockScheduleStepRule = (isNew = false, enabled = false): ScheduleStepRule => ({ + isNew, + enabled, + interval: '5m', + from: '6m', + to: 'now', +}); + export const mockRuleError = (id: string): RuleError => ({ rule_id: id, error: { status_code: 404, message: `id: "${id}" not found` }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..4d416e70a096c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap @@ -0,0 +1,453 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "multi" 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + ] + } + /> + + + +

    +
  • + + www.test.co + +
  • +
+ , + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + /> + + +`; + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "single" 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + Object { + "description": +
    +
  • + + www.test.co + +
  • +
+
, + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + /> +
+
+`; + +exports[`description_step StepRuleDescriptionComponent renders correctly against snapshot when columns is "singleSplit 1`] = ` + + + , + "title": "Severity", + }, + Object { + "description": 21, + "title": "Risk score", + }, + Object { + "description": "Titled timeline", + "title": "Timeline template", + }, + Object { + "description": +
    +
  • + + www.test.co + +
  • +
+
, + "title": "Reference URLs", + }, + Object { + "description": +
    +
  • + test +
  • +
+
, + "title": "False positive examples", + }, + Object { + "description": + + + + + + + + + + + + + + , + "title": "MITRE ATT&CK™", + }, + Object { + "description": + + + tag1 + + + + + tag2 + + + , + "title": "Tags", + }, + Object { + "description": +
+ # this is some markdown documentation +
+
, + "title": "Investigation notes", + }, + ] + } + type="column" + /> +
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx new file mode 100644 index 0000000000000..56c9d6da15607 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.test.tsx @@ -0,0 +1,403 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiLoadingSpinner } from '@elastic/eui'; + +import { coreMock } from '../../../../../../../../../../src/core/public/mocks'; +import { esFilters, FilterManager } from '../../../../../../../../../../src/plugins/data/public'; +import { SeverityBadge } from '../severity_badge'; + +import * as i18n from './translations'; +import { + isNotEmptyArray, + buildQueryBarDescription, + buildThreatDescription, + buildUnorderedListArrayDescription, + buildStringArrayDescription, + buildSeverityDescription, + buildUrlsDescription, + buildNoteDescription, +} from './helpers'; +import { ListItems } from './types'; + +const setupMock = coreMock.createSetup(); +const uiSettingsMock = (pinnedByDefault: boolean) => (key: string) => { + switch (key) { + case 'filters:pinnedByDefault': + return pinnedByDefault; + default: + throw new Error(`Unexpected uiSettings key in FilterManager mock: ${key}`); + } +}; +setupMock.uiSettings.get.mockImplementation(uiSettingsMock(true)); +const mockFilterManager = new FilterManager(setupMock.uiSettings); + +const mockQueryBar = { + query: { + query: 'test query', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', +}; + +describe('helpers', () => { + describe('isNotEmptyArray', () => { + test('returns false if empty array', () => { + const result = isNotEmptyArray([]); + expect(result).toBeFalsy(); + }); + + test('returns false if array of empty strings', () => { + const result = isNotEmptyArray(['', '']); + expect(result).toBeFalsy(); + }); + + test('returns true if array of string with space', () => { + const result = isNotEmptyArray([' ']); + expect(result).toBeTruthy(); + }); + + test('returns true if array with at least one non-empty string', () => { + const result = isNotEmptyArray(['', 'abc']); + expect(result).toBeTruthy(); + }); + }); + + describe('buildQueryBarDescription', () => { + test('returns empty array if no filters, query or savedId exist', () => { + const emptyMockQueryBar = { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: emptyMockQueryBar.filters, + filterManager: mockFilterManager, + query: emptyMockQueryBar.query, + savedId: emptyMockQueryBar.saved_id, + }); + expect(result).toEqual([]); + }); + + test('returns expected array of ListItems when filters exists, but no indexPatterns passed in', () => { + const mockQueryBarWithFilters = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithFilters.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithFilters.query, + savedId: mockQueryBarWithFilters.saved_id, + }); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual(<>{i18n.FILTERS_LABEL} ); + expect(wrapper.find(EuiLoadingSpinner).exists()).toBeTruthy(); + }); + + test('returns expected array of ListItems when filters AND indexPatterns exist', () => { + const mockQueryBarWithFilters = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithFilters.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithFilters.query, + savedId: mockQueryBarWithFilters.saved_id, + indexPatterns: { fields: [{ name: 'test name', type: 'test type' }], title: 'test title' }, + }); + const wrapper = shallow(result[0].description as React.ReactElement); + const filterLabelComponent = wrapper.find(esFilters.FilterLabel).at(0); + + expect(result[0].title).toEqual(<>{i18n.FILTERS_LABEL} ); + expect(filterLabelComponent.prop('valueLabel')).toEqual('file'); + expect(filterLabelComponent.prop('filter')).toEqual(mockQueryBar.filters[0]); + }); + + test('returns expected array of ListItems when "query.query" exists', () => { + const mockQueryBarWithQuery = { + ...mockQueryBar, + filters: [], + saved_id: '', + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithQuery.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithQuery.query, + savedId: mockQueryBarWithQuery.saved_id, + }); + expect(result[0].title).toEqual(<>{i18n.QUERY_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBarWithQuery.query.query} ); + }); + + test('returns expected array of ListItems when "savedId" exists', () => { + const mockQueryBarWithSavedId = { + ...mockQueryBar, + query: { + query: '', + language: 'kuery', + }, + filters: [], + }; + const result: ListItems[] = buildQueryBarDescription({ + field: 'queryBar', + filters: mockQueryBarWithSavedId.filters, + filterManager: mockFilterManager, + query: mockQueryBarWithSavedId.query, + savedId: mockQueryBarWithSavedId.saved_id, + }); + expect(result[0].title).toEqual(<>{i18n.SAVED_ID_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBarWithSavedId.saved_id} ); + }); + }); + + describe('buildThreatDescription', () => { + test('returns empty array if no threats', () => { + const result: ListItems[] = buildThreatDescription({ label: 'Mitre Attack', threat: [] }); + expect(result).toHaveLength(0); + }); + + test('returns empty tactic link if no corresponding tactic id found', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA000999' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual(''); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual( + 'Audio Capture (T1123)' + ); + }); + + test('returns empty technique link if no corresponding technique id found', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123456' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual( + 'Collection (TA0009)' + ); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual(''); + }); + + test('returns with corresponding tactic and technique link text', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [{ reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + expect(result[0].title).toEqual('Mitre Attack'); + expect(wrapper.find('[data-test-subj="threatTacticLink"]').text()).toEqual( + 'Collection (TA0009)' + ); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]').text()).toEqual( + 'Audio Capture (T1123)' + ); + }); + + test('returns corresponding number of tactic and technique links', () => { + const result: ListItems[] = buildThreatDescription({ + label: 'Mitre Attack', + threat: [ + { + framework: 'MITRE ATTACK', + technique: [ + { reference: 'https://test.com', name: 'Audio Capture', id: 'T1123' }, + { reference: 'https://test.com', name: 'Clipboard Data', id: 'T1115' }, + ], + tactic: { reference: 'https://test.com', name: 'Collection', id: 'TA0009' }, + }, + { + framework: 'MITRE ATTACK', + technique: [ + { reference: 'https://test.com', name: 'Automated Collection', id: 'T1119' }, + ], + tactic: { reference: 'https://test.com', name: 'Discovery', id: 'TA0007' }, + }, + ], + }); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(wrapper.find('[data-test-subj="threatTacticLink"]')).toHaveLength(2); + expect(wrapper.find('[data-test-subj="threatTechniqueLink"]')).toHaveLength(3); + }); + }); + + describe('buildUnorderedListArrayDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildUnorderedListArrayDescription( + 'Test label', + 'falsePositives', + [] + ); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildUnorderedListArrayDescription( + 'Test label', + 'falsePositives', + ['', 'falsePositive1', 'falsePositive2'] + ); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="unorderedListArrayDescriptionItem"]')).toHaveLength(2); + }); + }); + + describe('buildStringArrayDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildStringArrayDescription('Test label', 'tags', []); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildStringArrayDescription('Test label', 'tags', [ + '', + 'tag1', + 'tag2', + ]); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="stringArrayDescriptionBadgeItem"]')).toHaveLength(2); + expect( + wrapper + .find('[data-test-subj="stringArrayDescriptionBadgeItem"]') + .first() + .text() + ).toEqual('tag1'); + expect( + wrapper + .find('[data-test-subj="stringArrayDescriptionBadgeItem"]') + .at(1) + .text() + ).toEqual('tag2'); + }); + }); + + describe('buildSeverityDescription', () => { + test('returns ListItem with passed in label and SeverityBadge component', () => { + const result: ListItems[] = buildSeverityDescription('Test label', 'Test description value'); + + expect(result[0].title).toEqual('Test label'); + expect(result[0].description).toEqual(); + }); + }); + + describe('buildUrlsDescription', () => { + test('returns empty array if "values" is empty array', () => { + const result: ListItems[] = buildUrlsDescription('Test label', []); + expect(result).toHaveLength(0); + }); + + test('returns ListItem with corresponding number of valid values items', () => { + const result: ListItems[] = buildUrlsDescription('Test label', [ + 'www.test.com', + 'www.test2.com', + ]); + const wrapper = shallow(result[0].description as React.ReactElement); + + expect(result[0].title).toEqual('Test label'); + expect(wrapper.find('[data-test-subj="urlsDescriptionReferenceLinkItem"]')).toHaveLength(2); + expect( + wrapper + .find('[data-test-subj="urlsDescriptionReferenceLinkItem"]') + .first() + .text() + ).toEqual('www.test.com'); + expect( + wrapper + .find('[data-test-subj="urlsDescriptionReferenceLinkItem"]') + .at(1) + .text() + ).toEqual('www.test2.com'); + }); + }); + + describe('buildNoteDescription', () => { + test('returns ListItem with passed in label and note content', () => { + const noteSample = + 'Cras mattism. [Pellentesque](https://elastic.co). ### Malesuada adipiscing tristique'; + const result: ListItems[] = buildNoteDescription('Test label', noteSample); + const wrapper = shallow(result[0].description as React.ReactElement); + const noteElement = wrapper.find('[data-test-subj="noteDescriptionItem"]').at(0); + + expect(result[0].title).toEqual('Test label'); + expect(noteElement.exists()).toBeTruthy(); + expect(noteElement.text()).toEqual(noteSample); + }); + + test('returns empty array if passed in note is empty string', () => { + const result: ListItems[] = buildNoteDescription('Test label', ''); + + expect(result).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx index df767fbd4ff8c..bc454ecb1134a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/helpers.tsx @@ -9,9 +9,10 @@ import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, - EuiLink, EuiButtonEmpty, EuiSpacer, + EuiLink, + EuiText, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; @@ -27,8 +28,12 @@ import { BuildQueryBarDescription, BuildThreatDescription, ListItems } from './t import { SeverityBadge } from '../severity_badge'; import ListTreeIcon from './assets/list_tree_icon.svg'; -const isNotEmptyArray = (values: string[]) => - !isEmpty(values) && values.filter(val => !isEmpty(val)).length > 0; +const NoteDescriptionContainer = styled(EuiFlexItem)` + height: 105px; + overflow-y: hidden; +`; + +export const isNotEmptyArray = (values: string[]) => !isEmpty(values.join('')); const EuiBadgeWrap = styled(EuiBadge)` .euiBadge__text { @@ -106,13 +111,6 @@ const TechniqueLinkItem = styled(EuiButtonEmpty)` } `; -const ReferenceLinkItem = styled(EuiButtonEmpty)` - .euiIcon { - width: 12px; - height: 12px; - } -`; - export const buildThreatDescription = ({ label, threat }: BuildThreatDescription): ListItems[] => { if (threat.length > 0) { return [ @@ -124,7 +122,11 @@ export const buildThreatDescription = ({ label, threat }: BuildThreatDescription const tactic = tacticsOptions.find(t => t.id === singleThreat.tactic.id); return ( - + {tactic != null ? tactic.text : ''} @@ -133,6 +135,7 @@ export const buildThreatDescription = ({ label, threat }: BuildThreatDescription return ( - {values.map((val: string) => - isEmpty(val) ? null :
  • {val}
  • - )} - + +
      + {values.map(val => + isEmpty(val) ? null : ( +
    • + {val} +
    • + ) + )} +
    +
    ), }, ]; @@ -193,7 +202,9 @@ export const buildStringArrayDescription = ( {values.map((val: string) => isEmpty(val) ? null : ( - {val} + + {val} + ) )} @@ -218,21 +229,37 @@ export const buildUrlsDescription = (label: string, values: string[]): ListItems { title: label, description: ( - - {values.map((val: string) => ( - - - {val} - - - ))} - + +
      + {values + .filter(v => !isEmpty(v)) + .map((val, index) => ( +
    • + + {val} + +
    • + ))} +
    +
    + ), + }, + ]; + } + return []; +}; + +export const buildNoteDescription = (label: string, note: string): ListItems[] => { + if (note.trim() !== '') { + return [ + { + title: label, + description: ( + +
    + {note} +
    +
    ), }, ]; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx index 84c662dd00199..2c6f47fd27c44 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx @@ -3,12 +3,88 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; +import { shallow } from 'enzyme'; -import { addFilterStateIfNotThere } from './'; +import { + StepRuleDescriptionComponent, + addFilterStateIfNotThere, + buildListItems, + getDescriptionItem, +} from './'; -import { esFilters, Filter } from '../../../../../../../../../../src/plugins/data/public'; +import { + esFilters, + Filter, + FilterManager, +} from '../../../../../../../../../../src/plugins/data/public'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { coreMock } from '../../../../../../../../../../src/core/public/mocks'; +import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations'; +import * as i18n from './translations'; + +import { schema } from '../step_about_rule/schema'; +import { ListItems } from './types'; +import { AboutStepRule } from '../../types'; describe('description_step', () => { + const setupMock = coreMock.createSetup(); + const uiSettingsMock = (pinnedByDefault: boolean) => (key: string) => { + switch (key) { + case 'filters:pinnedByDefault': + return pinnedByDefault; + default: + throw new Error(`Unexpected uiSettings key in FilterManager mock: ${key}`); + } + }; + let mockFilterManager: FilterManager; + let mockAboutStep: AboutStepRule; + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + setupMock.uiSettings.get.mockImplementation(uiSettingsMock(true)); + mockFilterManager = new FilterManager(setupMock.uiSettings); + mockAboutStep = mockAboutStepRule(); + }); + + describe('StepRuleDescriptionComponent', () => { + test('renders correctly against snapshot when columns is "multi"', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(2); + }); + + test('renders correctly against snapshot when columns is "single"', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(1); + }); + + test('renders correctly against snapshot when columns is "singleSplit', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + expect(wrapper.find('[data-test-subj="listItemColumnStepRuleDescription"]')).toHaveLength(1); + expect( + wrapper + .find('[data-test-subj="singleSplitStepRuleDescriptionList"]') + .at(0) + .prop('type') + ).toEqual('column'); + }); + }); + describe('addFilterStateIfNotThere', () => { test('it does not change the state if it is global', () => { const filters: Filter[] = [ @@ -182,4 +258,221 @@ describe('description_step', () => { expect(output).toEqual(expected); }); }); + + describe('buildListItems', () => { + test('returns expected ListItems array when given valid inputs', () => { + const result: ListItems[] = buildListItems(mockAboutStep, schema, mockFilterManager); + + expect(result.length).toEqual(10); + }); + }); + + describe('getDescriptionItem', () => { + test('returns ListItem with all values enumerated when value[field] is an array', () => { + const result: ListItems[] = getDescriptionItem( + 'tags', + 'Tags label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Tags label'); + expect(typeof result[0].description).toEqual('object'); + }); + + test('returns ListItem with description of value[field] when value[field] is a string', () => { + const result: ListItems[] = getDescriptionItem( + 'description', + 'Description label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Description label'); + expect(result[0].description).toEqual('24/7'); + }); + + test('returns empty array when "value" is a non-existant property in "field"', () => { + const result: ListItems[] = getDescriptionItem( + 'jibberjabber', + 'JibberJabber label', + mockAboutStep, + mockFilterManager + ); + + expect(result.length).toEqual(0); + }); + + describe('queryBar', () => { + test('returns array of ListItems when queryBar exist', () => { + const mockQueryBar = { + isNew: false, + queryBar: { + query: { + query: 'user.name: root or user.name: admin', + language: 'kuery', + }, + filters: null, + saved_id: null, + }, + }; + const result: ListItems[] = getDescriptionItem( + 'queryBar', + 'Query bar label', + mockQueryBar, + mockFilterManager + ); + + expect(result[0].title).toEqual(<>{i18n.QUERY_LABEL} ); + expect(result[0].description).toEqual(<>{mockQueryBar.queryBar.query.query} ); + }); + }); + + describe('threat', () => { + test('returns array of ListItems when threat exist', () => { + const result: ListItems[] = getDescriptionItem( + 'threat', + 'Threat label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Threat label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + + test('filters out threats with tactic.name of "none"', () => { + const mockStep = { + ...mockAboutStep, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'none', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + const result: ListItems[] = getDescriptionItem( + 'threat', + 'Threat label', + mockStep, + mockFilterManager + ); + + expect(result.length).toEqual(0); + }); + }); + + describe('references', () => { + test('returns array of ListItems when references exist', () => { + const result: ListItems[] = getDescriptionItem( + 'references', + 'Reference label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Reference label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('falsePositives', () => { + test('returns array of ListItems when falsePositives exist', () => { + const result: ListItems[] = getDescriptionItem( + 'falsePositives', + 'False positives label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('False positives label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('severity', () => { + test('returns array of ListItems when severity exist', () => { + const result: ListItems[] = getDescriptionItem( + 'severity', + 'Severity label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Severity label'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + + describe('riskScore', () => { + test('returns array of ListItems when riskScore exist', () => { + const result: ListItems[] = getDescriptionItem( + 'riskScore', + 'Risk score label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Risk score label'); + expect(result[0].description).toEqual(21); + }); + }); + + describe('timeline', () => { + test('returns timeline title if one exists', () => { + const result: ListItems[] = getDescriptionItem( + 'timeline', + 'Timeline label', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Timeline label'); + expect(result[0].description).toEqual('Titled timeline'); + }); + + test('returns default timeline title if none exists', () => { + const mockStep = { + ...mockAboutStep, + timeline: { + id: '12345', + }, + }; + const result: ListItems[] = getDescriptionItem( + 'timeline', + 'Timeline label', + mockStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Timeline label'); + expect(result[0].description).toEqual(DEFAULT_TIMELINE_TITLE); + }); + }); + + describe('note', () => { + test('returns default "note" description', () => { + const result: ListItems[] = getDescriptionItem( + 'note', + 'Investigation notes', + mockAboutStep, + mockFilterManager + ); + + expect(result[0].title).toEqual('Investigation notes'); + expect(React.isValidElement(result[0].description)).toBeTruthy(); + }); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx index cb5c98bb23f07..1d58ef8014899 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.tsx @@ -7,6 +7,7 @@ import { EuiDescriptionList, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty, chunk, get, pick } from 'lodash/fp'; import React, { memo, useState } from 'react'; +import styled from 'styled-components'; import { IIndexPattern, @@ -28,18 +29,28 @@ import { buildThreatDescription, buildUnorderedListArrayDescription, buildUrlsDescription, + buildNoteDescription, } from './helpers'; +const DescriptionListContainer = styled(EuiDescriptionList)` + &.euiDescriptionList--column .euiDescriptionList__title { + width: 30%; + } + &.euiDescriptionList--column .euiDescriptionList__description { + width: 70%; + } +`; + interface StepRuleDescriptionProps { - direction?: 'row' | 'column'; + columns?: 'multi' | 'single' | 'singleSplit'; data: unknown; indexPatterns?: IIndexPattern; schema: FormSchema; } -const StepRuleDescriptionComponent: React.FC = ({ +export const StepRuleDescriptionComponent: React.FC = ({ data, - direction = 'row', + columns = 'multi', indexPatterns, schema, }) => { @@ -55,11 +66,14 @@ const StepRuleDescriptionComponent: React.FC = ({ [] ); - if (direction === 'row') { + if (columns === 'multi') { return ( {chunk(Math.ceil(listItems.length / 2), listItems).map((chunkListItems, index) => ( - + ))} @@ -69,8 +83,16 @@ const StepRuleDescriptionComponent: React.FC = ({ return ( - - + + {columns === 'single' ? ( + + ) : ( + + )} ); @@ -78,7 +100,7 @@ const StepRuleDescriptionComponent: React.FC = ({ export const StepRuleDescription = memo(StepRuleDescriptionComponent); -const buildListItems = ( +export const buildListItems = ( data: unknown, schema: FormSchema, filterManager: FilterManager, @@ -108,7 +130,7 @@ export const addFilterStateIfNotThere = (filters: Filter[]): Filter[] => { }); }; -const getDescriptionItem = ( +export const getDescriptionItem = ( field: string, label: string, value: unknown, @@ -132,13 +154,6 @@ const getDescriptionItem = ( (singleThreat: IMitreEnterpriseAttack) => singleThreat.tactic.name !== 'none' ); return buildThreatDescription({ label, threat }); - } else if (field === 'description') { - return [ - { - title: label, - description: get(field, value), - }, - ]; } else if (field === 'references') { const urls: string[] = get(field, value); return buildUrlsDescription(label, urls); @@ -166,14 +181,9 @@ const getDescriptionItem = ( description: timeline.title ?? DEFAULT_TIMELINE_TITLE, }, ]; - } else if (field === 'riskScore') { - const description: string = get(field, value); - return [ - { - title: label, - description, - }, - ]; + } else if (field === 'note') { + const val: string = get(field, value); + return buildNoteDescription(label, val); } const description: string = get(field, value); if (!isEmpty(description)) { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts index d15cce15877b4..417133f230610 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts @@ -29,4 +29,5 @@ export const stepAboutDefaultValue: AboutStepRule = { title: DEFAULT_TIMELINE_TITLE, }, threat: threatDefault, + note: '', }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx new file mode 100644 index 0000000000000..0ed479e235151 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.test.tsx @@ -0,0 +1,155 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import { ThemeProvider } from 'styled-components'; +import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { StepAboutRule } from './'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { StepRuleDescription } from '../description_step'; +import { stepAboutDefaultValue } from './default_value'; + +const theme = () => ({ eui: euiDarkVars, darkMode: true }); + +describe('StepAboutRuleComponent', () => { + test('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepRuleDescription).exists()).toBeTruthy(); + }); + + test('it prevents user from clicking continue if no "description" defined', () => { + const wrapper = mount( + + + + ); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + nameInput.simulate('change', { target: { value: 'Test name text' } }); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + + expect( + wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0) + .props().value + ).toEqual('Test name text'); + expect(descriptionInput.props().value).toEqual(''); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleDescription"] label') + .at(0) + .hasClass('euiFormLabel-isInvalid') + ).toBeTruthy(); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleDescription"] EuiTextArea') + .at(0) + .prop('isInvalid') + ).toBeTruthy(); + }); + + test('it prevents user from clicking continue if no "name" defined', () => { + const wrapper = mount( + + + + ); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + descriptionInput.simulate('change', { target: { value: 'Test description text' } }); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + + expect( + wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0) + .props().value + ).toEqual('Test description text'); + expect(nameInput.props().value).toEqual(''); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleName"] label') + .at(0) + .hasClass('euiFormLabel-isInvalid') + ).toBeTruthy(); + expect( + wrapper + .find('EuiFormRow[data-test-subj="detectionEngineStepAboutRuleName"] EuiFieldText') + .at(0) + .prop('isInvalid') + ).toBeTruthy(); + }); + + test('it allows user to click continue if "name" and "description" are defined', () => { + const wrapper = mount( + + + + ); + + const descriptionInput = wrapper + .find('textarea[aria-describedby="detectionEngineStepAboutRuleDescription"]') + .at(0); + descriptionInput.simulate('change', { target: { value: 'Test description text' } }); + + const nameInput = wrapper + .find('input[aria-describedby="detectionEngineStepAboutRuleName"]') + .at(0); + nameInput.simulate('change', { target: { value: 'Test name text' } }); + + const nextButton = wrapper.find('button[data-test-subj="about-continue"]').at(0); + nextButton.simulate('click'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx index 4f06d4314c1f3..bfb123f3f3204 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx @@ -39,6 +39,7 @@ import { schema } from './schema'; import * as I18n from './translations'; import { PickTimeline } from '../pick_timeline'; import { StepContentWrapper } from '../step_content_wrapper'; +import { MarkdownEditorForm } from '../../../../../components/markdown_editor/form'; const CommonUseField = getUseField({ component: Field }); @@ -46,6 +47,12 @@ interface StepAboutRuleProps extends RuleStepProps { defaultValues?: AboutStepRule | null; } +const ThreeQuartersContainer = styled.div` + max-width: 740px; +`; + +ThreeQuartersContainer.displayName = 'ThreeQuartersContainer'; + const TagContainer = styled.div` margin-top: 16px; `; @@ -75,7 +82,7 @@ const AdvancedSettingsAccordionButton = ( const StepAboutRuleComponent: FC = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isUpdateView = false, isLoading, @@ -120,68 +127,74 @@ const StepAboutRuleComponent: FC = ({ }, [form]); return isReadOnlyView && myStepData.name != null ? ( - - + + ) : ( <>
    - - + + + - - - - - - - - + + + + + + + + + + + = ({ dataTestSubj: 'detectionEngineStepAboutRuleMitreThreat', }} /> + + + + {({ severity }) => { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx index 42cf1e0d95649..7c1ab09b7309c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx @@ -95,7 +95,14 @@ export const schema: FormSchema = { label: i18n.translate( 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateLabel', { - defaultMessage: 'Investigate detections using this timeline template', + defaultMessage: 'Timeline template', + } + ), + helpText: i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateHelpText', + { + defaultMessage: + 'Select an existing timeline to use as a template when investigating generated signals.', } ), }, @@ -184,4 +191,15 @@ export const schema: FormSchema = { ), labelAppend: OptionalFieldLabel, }, + note: { + type: FIELD_TYPES.TEXTAREA, + label: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteLabel', { + defaultMessage: 'Investigation notes', + }), + helpText: i18n.translate('xpack.siem.detectionEngine.createRule.stepAboutRule.noteHelpText', { + defaultMessage: + 'Provide helpful information for analysts that are performing a signal investigation. These notes will appear on both the rule details page and in timelines created from signals generated by this rule.', + }), + labelAppend: OptionalFieldLabel, + }, }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts index 3b6680fd4e687..dfa60268e903a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/translations.ts @@ -68,3 +68,10 @@ export const URL_FORMAT_INVALID = i18n.translate( defaultMessage: 'Url is invalid format', } ); + +export const ADD_RULE_NOTE_HELP_TEXT = i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepAboutrule.noteHelpText', + { + defaultMessage: 'Add rule investigation notes...', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx new file mode 100644 index 0000000000000..4a4e96ec74902 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.test.tsx @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { mount, shallow } from 'enzyme'; +import { EuiProgress, EuiButtonGroup } from '@elastic/eui'; +import { ThemeProvider } from 'styled-components'; +import euiDarkVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { StepAboutRuleToggleDetails } from './'; +import { mockAboutStepRule } from '../../all/__mocks__/mock'; +import { HeaderSection } from '../../../../../components/header_section'; +import { StepAboutRule } from '../step_about_rule/'; +import { AboutStepRule } from '../../types'; + +const theme = () => ({ eui: euiDarkVars, darkMode: true }); + +describe('StepAboutRuleToggleDetails', () => { + let mockRule: AboutStepRule; + + beforeEach(() => { + // jest carries state between mocked implementations when using + // spyOn. So now we're doing all three of these. + // https://github.com/facebook/jest/issues/7136#issuecomment-565976599 + jest.resetAllMocks(); + jest.restoreAllMocks(); + jest.clearAllMocks(); + + mockRule = mockAboutStepRule(); + }); + + test('it renders loading component when "loading" is true', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(EuiProgress).exists()).toBeTruthy(); + expect(wrapper.find(HeaderSection).exists()).toBeTruthy(); + }); + + test('it does not render details if stepDataDetails is null', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepAboutRule).exists()).toBeFalsy(); + }); + + test('it does not render details if stepData is null', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(StepAboutRule).exists()).toBeFalsy(); + }); + + describe('note value is empty string', () => { + test('it does not render toggle buttons', () => { + const mockAboutStepWithoutNote = { + ...mockRule, + note: '', + }; + const wrapper = shallow( + + ); + + expect(wrapper.find('[data-test-subj="stepAboutDetailsToggle"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="stepAboutDetailsNoteContent"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="stepAboutDetailsContent"]').exists()).toBeTruthy(); + }); + }); + + describe('note value does exist', () => { + test('it renders toggle buttons, defaulted to "details"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find(EuiButtonGroup).exists()).toBeTruthy(); + expect( + wrapper + .find('EuiButtonToggle[id="details"]') + .at(0) + .prop('isSelected') + ).toBeTruthy(); + expect( + wrapper + .find('EuiButtonToggle[id="notes"]') + .at(0) + .prop('isSelected') + ).toBeFalsy(); + }); + + test('it allows users to toggle between "details" and "note"', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('EuiButtonGroup[idSelected="details"]').exists()).toBeTruthy(); + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeFalsy(); + + wrapper + .find('input[title="Investigation notes"]') + .at(0) + .simulate('change', { target: { value: 'notes' } }); + + expect(wrapper.find('EuiButtonGroup[idSelected="details"]').exists()).toBeFalsy(); + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeTruthy(); + }); + + test('it displays notes markdown when user toggles to "notes"', () => { + const wrapper = mount( + + + + ); + + wrapper + .find('input[title="Investigation notes"]') + .at(0) + .simulate('change', { target: { value: 'notes' } }); + + expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeTruthy(); + expect(wrapper.find('Markdown h1').text()).toEqual('this is some markdown documentation'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx new file mode 100644 index 0000000000000..c61566cb841e8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/index.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiPanel, + EuiProgress, + EuiButtonGroup, + EuiButtonGroupOption, + EuiSpacer, + EuiFlexItem, + EuiText, + EuiFlexGroup, + EuiResizeObserver, +} from '@elastic/eui'; +import React, { memo, useState } from 'react'; +import styled from 'styled-components'; +import { isEmpty } from 'lodash/fp'; + +import { HeaderSection } from '../../../../../components/header_section'; +import { Markdown } from '../../../../../components/markdown'; +import { AboutStepRule, AboutStepRuleDetails } from '../../types'; +import * as i18n from './translations'; +import { StepAboutRule } from '../step_about_rule/'; + +const MyPanel = styled(EuiPanel)` + position: relative; +`; + +const FlexGroupFullHeight = styled(EuiFlexGroup)` + height: 100%; +`; + +const VerticalOverflowContainer = styled.div((props: { maxHeight: number }) => ({ + 'max-height': `${props.maxHeight}px`, + 'overflow-y': 'hidden', +})); + +const VerticalOverflowContent = styled.div((props: { maxHeight: number }) => ({ + 'max-height': `${props.maxHeight}px`, +})); + +const AboutContent = styled.div` + height: 100%; +`; + +const toggleOptions: EuiButtonGroupOption[] = [ + { + id: 'details', + label: i18n.ABOUT_PANEL_DETAILS_TAB, + }, + { + id: 'notes', + label: i18n.ABOUT_PANEL_NOTES_TAB, + }, +]; + +interface StepPanelProps { + stepData: AboutStepRule | null; + stepDataDetails: AboutStepRuleDetails | null; + loading: boolean; +} + +const StepAboutRuleToggleDetailsComponent: React.FC = ({ + stepData, + stepDataDetails, + loading, +}) => { + const [selectedToggleOption, setToggleOption] = useState('details'); + const [aboutPanelHeight, setAboutPanelHeight] = useState(0); + + const onResize = (e: { height: number; width: number }) => { + setAboutPanelHeight(e.height); + }; + + return ( + + {loading && ( + <> + + + + )} + {stepData != null && stepDataDetails != null && ( + + + + {!isEmpty(stepDataDetails.note) && stepDataDetails.note.trim() !== '' && ( + { + setToggleOption(val); + }} + data-test-subj="stepAboutDetailsToggle" + /> + )} + + + + {selectedToggleOption === 'details' ? ( + + {resizeRef => ( + + + + + {stepDataDetails.description} + + + + + + + )} + + ) : ( + + + + + + )} + + + )} + + ); +}; + +export const StepAboutRuleToggleDetails = memo(StepAboutRuleToggleDetailsComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts new file mode 100644 index 0000000000000..fa725366210de --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule_details/translations.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; + +export const ABOUT_PANEL_DETAILS_TAB = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.detailsLabel', + { + defaultMessage: 'Details', + } +); + +export const ABOUT_TEXT = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.aboutText', + { + defaultMessage: 'About', + } +); + +export const ABOUT_PANEL_NOTES_TAB = i18n.translate( + 'xpack.siem.detectionEngine.details.stepAboutRule.investigationNotesLabel', + { + defaultMessage: 'Investigation notes', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx index 490a8d9d194cb..2327ac36a5906 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx @@ -87,7 +87,7 @@ const getStepDefaultValue = ( const StepDefineRuleComponent: FC = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isLoading, isUpdateView = false, @@ -155,9 +155,9 @@ const StepDefineRuleComponent: FC = ({ }, []); return isReadOnlyView && myStepData?.queryBar != null ? ( - + = ({ addPadding = false, defaultValues, - descriptionDirection = 'row', + descriptionColumns = 'singleSplit', isReadOnlyView, isLoading, isUpdateView = false, @@ -80,31 +85,35 @@ const StepScheduleRuleComponent: FC = ({ return isReadOnlyView && myStepData != null ? ( - + ) : ( <> - - + + + + + + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts new file mode 100644 index 0000000000000..dbc5dd9bbe29a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts @@ -0,0 +1,589 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { NewRule } from '../../../../containers/detection_engine/rules'; +import { + DefineStepRuleJson, + ScheduleStepRuleJson, + AboutStepRuleJson, + AboutStepRule, + ScheduleStepRule, + DefineStepRule, +} from '../types'; +import { + getTimeTypeValue, + formatDefineStepData, + formatScheduleStepData, + formatAboutStepData, + formatRule, +} from './helpers'; +import { + mockDefineStepRule, + mockQueryBar, + mockScheduleStepRule, + mockAboutStepRule, +} from '../all/__mocks__/mock'; + +describe('helpers', () => { + describe('getTimeTypeValue', () => { + test('returns timeObj with value 0 if no time value found', () => { + const result = getTimeTypeValue('m'); + + expect(result).toEqual({ unit: 'm', value: 0 }); + }); + + test('returns timeObj with unit set to empty string if no expected time type found', () => { + const result = getTimeTypeValue('5l'); + + expect(result).toEqual({ unit: '', value: 5 }); + }); + + test('returns timeObj with unit of s and value 5 when time is 5s ', () => { + const result = getTimeTypeValue('5s'); + + expect(result).toEqual({ unit: 's', value: 5 }); + }); + + test('returns timeObj with unit of m and value 5 when time is 5m ', () => { + const result = getTimeTypeValue('5m'); + + expect(result).toEqual({ unit: 'm', value: 5 }); + }); + + test('returns timeObj with unit of h and value 5 when time is 5h ', () => { + const result = getTimeTypeValue('5h'); + + expect(result).toEqual({ unit: 'h', value: 5 }); + }); + + test('returns timeObj with value of 5 when time is float like 5.6m ', () => { + const result = getTimeTypeValue('5m'); + + expect(result).toEqual({ unit: 'm', value: 5 }); + }); + + test('returns timeObj with value of 0 and unit of "" if random string passed in', () => { + const result = getTimeTypeValue('random'); + + expect(result).toEqual({ unit: '', value: 0 }); + }); + }); + + describe('formatDefineStepData', () => { + let mockData: DefineStepRule; + + beforeEach(() => { + mockData = mockDefineStepRule(); + }); + + test('returns formatted object as DefineStepRuleJson', () => { + const result: DefineStepRuleJson = formatDefineStepData(mockData); + const expected = { + language: 'kuery', + filters: mockQueryBar.filters, + query: 'test query', + saved_id: 'test123', + index: ['filebeat-'], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with no saved_id if no savedId provided', () => { + const mockStepData = { + ...mockData, + queryBar: { + ...mockData.queryBar, + saved_id: '', + }, + }; + const result: DefineStepRuleJson = formatDefineStepData(mockStepData); + const expected = { + language: 'kuery', + filters: mockQueryBar.filters, + query: 'test query', + index: ['filebeat-'], + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatScheduleStepData', () => { + let mockData: ScheduleStepRule; + + beforeEach(() => { + mockData = mockScheduleStepRule(); + }); + + test('returns formatted object as ScheduleStepRuleJson', () => { + const result: ScheduleStepRuleJson = formatScheduleStepData(mockData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with "to" as "now" if "to" not supplied', () => { + const mockStepData = { + ...mockData, + }; + delete mockStepData.to; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with "to" as "now" if "to" random string', () => { + const mockStepData = { + ...mockData, + to: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-660s', + to: 'now', + interval: '5m', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object if "from" random string', () => { + const mockStepData = { + ...mockData, + from: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-300s', + to: 'now', + interval: '5m', + meta: { + from: 'random', + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object if "interval" random string', () => { + const mockStepData = { + ...mockData, + interval: 'random', + }; + const result: ScheduleStepRuleJson = formatScheduleStepData(mockStepData); + const expected = { + enabled: false, + from: 'now-360s', + to: 'now', + interval: 'random', + meta: { + from: '6m', + }, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatAboutStepData', () => { + let mockData: AboutStepRule; + + beforeEach(() => { + mockData = mockAboutStepRule(); + }); + + test('returns formatted object as AboutStepRuleJson', () => { + const result: AboutStepRuleJson = formatAboutStepData(mockData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with empty falsePositive and references filtered out', () => { + const mockStepData = { + ...mockData, + falsePositives: ['', 'test', ''], + references: ['www.test.co', ''], + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without note if note is empty string', () => { + const mockStepData = { + ...mockData, + note: '', + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without timeline_id and timeline_title if timeline.id is null', () => { + const mockStepData = { + ...mockData, + }; + delete mockStepData.timeline.id; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with timeline_id and timeline_title if timeline.id is "', () => { + const mockStepData = { + ...mockData, + timeline: { + ...mockData.timeline, + id: '', + }, + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline_id: '', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object without timeline_id and timeline_title if timeline.title is null', () => { + const mockStepData = { + ...mockData, + timeline: { + ...mockData.timeline, + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + }, + }; + delete mockStepData.timeline.title; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with timeline_id and timeline_title if timeline.title is "', () => { + const mockStepData = { + ...mockData, + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: '', + }, + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { id: '1234', name: 'tactic1', reference: 'reference1' }, + technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: '', + }; + + expect(result).toEqual(expected); + }); + + test('returns formatted object with threats filtered out where tactic.name is "none"', () => { + const mockStepData = { + ...mockData, + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'none', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + }; + const result: AboutStepRuleJson = formatAboutStepData(mockStepData); + const expected = { + description: '24/7', + false_positives: ['test'], + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'MITRE ATT&CK', + tactic: { id: '1234', name: 'tactic1', reference: 'reference1' }, + technique: [{ id: '456', name: 'technique1', reference: 'technique reference' }], + }, + ], + timeline_id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + timeline_title: 'Titled timeline', + }; + + expect(result).toEqual(expected); + }); + }); + + describe('formatRule', () => { + let mockAbout: AboutStepRule; + let mockDefine: DefineStepRule; + let mockSchedule: ScheduleStepRule; + + beforeEach(() => { + mockAbout = mockAboutStepRule(); + mockDefine = mockDefineStepRule(); + mockSchedule = mockScheduleStepRule(); + }); + + test('returns NewRule with type of saved_query when saved_id exists', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule); + + expect(result.type).toEqual('saved_query'); + }); + + test('returns NewRule with type of query when saved_id does not exist', () => { + const mockDefineStepRuleWithoutSavedId = { + ...mockDefine, + queryBar: { + ...mockDefine.queryBar, + saved_id: '', + }, + }; + const result: NewRule = formatRule(mockDefineStepRuleWithoutSavedId, mockAbout, mockSchedule); + + expect(result.type).toEqual('query'); + }); + + test('returns NewRule with id set to ruleId if ruleId exists', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule, 'query-with-rule-id'); + + expect(result.id).toEqual('query-with-rule-id'); + }); + + test('returns NewRule without id if ruleId does not exist', () => { + const result: NewRule = formatRule(mockDefine, mockAbout, mockSchedule); + + expect(result.id).toBeUndefined(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts index de6678b42df6f..07578e870bf2b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts @@ -19,7 +19,7 @@ import { FormatRuleType, } from '../types'; -const getTimeTypeValue = (time: string): { unit: string; value: number } => { +export const getTimeTypeValue = (time: string): { unit: string; value: number } => { const timeObj = { unit: '', value: 0, @@ -39,7 +39,7 @@ const getTimeTypeValue = (time: string): { unit: string; value: number } => { return timeObj; }; -const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { +export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { const { queryBar, isNew, ...rest } = defineStepData; const { filters, query, saved_id: savedId } = queryBar; return { @@ -51,7 +51,7 @@ const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJso }; }; -const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRuleJson => { +export const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRuleJson => { const { isNew, ...formatScheduleData } = scheduleData; if (!isEmpty(formatScheduleData.interval) && !isEmpty(formatScheduleData.from)) { const { unit: intervalUnit, value: intervalValue } = getTimeTypeValue( @@ -71,8 +71,17 @@ const formatScheduleStepData = (scheduleData: ScheduleStepRule): ScheduleStepRul }; }; -const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => { - const { falsePositives, references, riskScore, threat, timeline, isNew, ...rest } = aboutStepData; +export const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => { + const { + falsePositives, + references, + riskScore, + threat, + timeline, + isNew, + note, + ...rest + } = aboutStepData; return { false_positives: falsePositives.filter(item => !isEmpty(item)), references: references.filter(item => !isEmpty(item)), @@ -93,6 +102,7 @@ const formatAboutStepData = (aboutStepData: AboutStepRule): AboutStepRuleJson => return { id, name, reference }; }), })), + ...(!isEmpty(note) ? { note } : {}), ...rest, }; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx index d816c7e867057..c9f44ab0048f9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/index.tsx @@ -286,7 +286,7 @@ const CreateRulePageComponent: React.FC = () => { isLoading={isLoading || loading} setForm={setStepsForm} setStepData={setStepData} - descriptionDirection="row" + descriptionColumns="singleSplit" /> @@ -315,7 +315,7 @@ const CreateRulePageComponent: React.FC = () => { { defaultValues={ (stepsData.current[RuleStep.scheduleRule].data as ScheduleStepRule) ?? null } - descriptionDirection="row" + descriptionColumns="singleSplit" isReadOnlyView={isStepRuleInReadOnlyView[RuleStep.scheduleRule]} isLoading={isLoading || loading} setForm={setStepsForm} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index e73852ec91287..a35caf4acf67b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -38,13 +38,13 @@ import { } from '../../../../containers/source'; import { SpyRoute } from '../../../../utils/route/spy_routes'; +import { StepAboutRuleToggleDetails } from '../components/step_about_rule_details/'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { SignalsHistogramPanel } from '../../components/signals_histogram_panel'; import { SignalsTable } from '../../components/signals'; import { useUserInfo } from '../../components/user_info'; import { DetectionEngineEmptyPage } from '../../detection_engine_empty_page'; import { useSignalInfo } from '../../components/signals_info'; -import { StepAboutRule } from '../components/step_about_rule'; import { StepDefineRule } from '../components/step_define_rule'; import { StepScheduleRule } from '../components/step_schedule_rule'; import { buildSignalsRuleIdFilter } from '../../components/signals/default_config'; @@ -105,13 +105,15 @@ const RuleDetailsPageComponent: FC = ({ // This is used to re-trigger api rule status when user de/activate rule const [ruleEnabled, setRuleEnabled] = useState(null); const [ruleDetailTab, setRuleDetailTab] = useState(RuleDetailTabs.signals); - const { aboutRuleData, defineRuleData, scheduleRuleData } = + const { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData } = rule != null - ? getStepsData({ - rule, - detailsView: true, - }) - : { aboutRuleData: null, defineRuleData: null, scheduleRuleData: null }; + ? getStepsData({ rule, detailsView: true }) + : { + aboutRuleData: null, + modifiedAboutRuleDetailsData: null, + defineRuleData: null, + scheduleRuleData: null, + }; const [lastSignals] = useSignalInfo({ ruleId }); const userHasNoPermissions = canUserCRUD != null && hasManageApiKey != null ? !canUserCRUD || !hasManageApiKey : false; @@ -291,16 +293,23 @@ const RuleDetailsPageComponent: FC = ({
    {ruleError} - {tabs} - {ruleDetailTab === RuleDetailTabs.signals && ( - <> - + + + + + + + {defineRuleData != null && ( = ({ )} - - - - {aboutRuleData != null && ( - - )} - - - + {scheduleRuleData != null && ( = ({ - + + + + {tabs} + + {ruleDetailTab === RuleDetailTabs.signals && ( + <> { + describe('getStepsData', () => { + test('returns object with about, define, and schedule step properties formatted', () => { + const { + defineRuleData, + modifiedAboutRuleDetailsData, + aboutRuleData, + scheduleRuleData, + }: GetStepsData = getStepsData({ + rule: mockRuleWithEverything('test-id'), + }); + const defineRuleStepData = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: 'user.name: root or user.name: admin', + language: 'kuery', + }, + filters: [ + { + $state: { + store: esFilters.FilterStateStore.GLOBAL_STATE, + }, + meta: { + alias: null, + disabled: false, + key: 'event.category', + negate: false, + params: { + query: 'file', + }, + type: 'phrase', + }, + query: { + match_phrase: { + 'event.category': 'file', + }, + }, + }, + ], + saved_id: 'test123', + }, + }; + const aboutRuleStepData = { + description: '24/7', + falsePositives: ['test'], + isNew: false, + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + riskScore: 21, + severity: 'low', + tags: ['tag1', 'tag2'], + threat: [ + { + framework: 'mockFramework', + tactic: { + id: '1234', + name: 'tactic1', + reference: 'reference1', + }, + technique: [ + { + id: '456', + name: 'technique1', + reference: 'technique reference', + }, + ], + }, + ], + timeline: { + id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', + title: 'Titled timeline', + }, + }; + const scheduleRuleStepData = { enabled: true, from: '0s', interval: '5m', isNew: false }; + const aboutRuleDataDetailsData = { + note: '# this is some markdown documentation', + description: '24/7', + }; + + expect(defineRuleData).toEqual(defineRuleStepData); + expect(aboutRuleData).toEqual(aboutRuleStepData); + expect(scheduleRuleData).toEqual(scheduleRuleStepData); + expect(modifiedAboutRuleDetailsData).toEqual(aboutRuleDataDetailsData); + }); + }); + + describe('getAboutStepsData', () => { + test('returns timeline id and title of null if they do not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.timeline_id; + delete mockedRule.timeline_title; + const result: AboutStepRule = getAboutStepsData(mockedRule, false); + + expect(result.timeline.id).toBeNull(); + expect(result.timeline.title).toBeNull(); + }); + + test('returns name, description, and note as empty string if detailsView is true', () => { + const result: AboutStepRule = getAboutStepsData(mockRuleWithEverything('test-id'), true); + + expect(result.name).toEqual(''); + expect(result.description).toEqual(''); + expect(result.note).toEqual(''); + }); + + test('returns note as empty string if property does not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.note; + const result: AboutStepRule = getAboutStepsData(mockedRule, false); + + expect(result.note).toEqual(''); + }); + }); + + describe('determineDetailsValue', () => { + test('returns name, description, and note as empty string if detailsView is true', () => { + const result: Pick = determineDetailsValue( + mockRuleWithEverything('test-id'), + true + ); + const expected = { name: '', description: '', note: '' }; + + expect(result).toEqual(expected); + }); + + test('returns name, description, and note values if detailsView is false', () => { + const mockedRule = mockRuleWithEverything('test-id'); + const result: Pick = determineDetailsValue( + mockedRule, + false + ); + const expected = { + name: mockedRule.name, + description: mockedRule.description, + note: mockedRule.note, + }; + + expect(result).toEqual(expected); + }); + + test('returns note as empty string if property does not exist on rule', () => { + const mockedRule = mockRuleWithEverything('test-id'); + delete mockedRule.note; + const result: Pick = determineDetailsValue( + mockedRule, + false + ); + const expected = { name: mockedRule.name, description: mockedRule.description, note: '' }; + + expect(result).toEqual(expected); + }); + }); + + describe('getDefineStepsData', () => { + test('returns with saved_id if value exists on rule', () => { + const result: DefineStepRule = getDefineStepsData(mockRule('test-id')); + const expected = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: "Garrett's IP", + }, + }; + + expect(result).toEqual(expected); + }); + + test('returns with saved_id of null if value does not exist on rule', () => { + const mockedRule = { + ...mockRule('test-id'), + }; + delete mockedRule.saved_id; + const result: DefineStepRule = getDefineStepsData(mockedRule); + const expected = { + isNew: false, + index: ['auditbeat-*'], + queryBar: { + query: { + query: '', + language: 'kuery', + }, + filters: [], + saved_id: null, + }, + }; + + expect(result).toEqual(expected); + }); + }); + + describe('getHumanizedDuration', () => { + test('returns from as seconds if from duration is less than a minute', () => { + const result = getHumanizedDuration('now-62s', '1m'); + + expect(result).toEqual('2s'); + }); + + test('returns from as minutes if from duration is less than an hour', () => { + const result = getHumanizedDuration('now-660s', '5m'); + + expect(result).toEqual('6m'); + }); + + test('returns from as hours if from duration is more than 60 minutes', () => { + const result = getHumanizedDuration('now-7400s', '5m'); + + expect(result).toEqual('1h'); + }); + + test('returns from as if from is not parsable as dateMath', () => { + const result = getHumanizedDuration('randomstring', '5m'); + + expect(result).toEqual('NaNh'); + }); + + test('returns from as 5m if interval is not parsable as dateMath', () => { + const result = getHumanizedDuration('now-300s', 'randomstring'); + + expect(result).toEqual('5m'); + }); + }); + + describe('getScheduleStepsData', () => { + test('returns expected ScheduleStep rule object', () => { + const mockedRule = { + ...mockRule('test-id'), + }; + const result: ScheduleStepRule = getScheduleStepsData(mockedRule); + const expected = { + isNew: false, + enabled: mockedRule.enabled, + interval: mockedRule.interval, + from: '0s', + }; + + expect(result).toEqual(expected); + }); + }); + + describe('getModifiedAboutDetailsData', () => { + test('returns object with "note" and "description" being those of passed in rule', () => { + const result: AboutStepRuleDetails = getModifiedAboutDetailsData( + mockRuleWithEverything('test-id') + ); + const aboutRuleDataDetailsData = { + note: '# this is some markdown documentation', + description: '24/7', + }; + + expect(result).toEqual(aboutRuleDataDetailsData); + }); + + test('returns "note" with empty string if "note" does not exist', () => { + const { note, ...mockRuleWithoutNote } = { ...mockRuleWithEverything('test-id') }; + const result: AboutStepRuleDetails = getModifiedAboutDetailsData(mockRuleWithoutNote); + + const aboutRuleDetailsData = { note: '', description: mockRuleWithoutNote.description }; + + expect(result).toEqual(aboutRuleDetailsData); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx index 85f3bcbd236e9..1fc8a86a476f2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx @@ -5,19 +5,26 @@ */ import dateMath from '@elastic/datemath'; -import { get, pick } from 'lodash/fp'; +import { get } from 'lodash/fp'; import moment from 'moment'; import { useLocation } from 'react-router-dom'; import { Filter } from '../../../../../../../../src/plugins/data/public'; import { Rule } from '../../../containers/detection_engine/rules'; import { FormData, FormHook, FormSchema } from '../../../shared_imports'; -import { AboutStepRule, DefineStepRule, IMitreEnterpriseAttack, ScheduleStepRule } from './types'; +import { + AboutStepRule, + AboutStepRuleDetails, + DefineStepRule, + IMitreEnterpriseAttack, + ScheduleStepRule, +} from './types'; -interface GetStepsData { - aboutRuleData: AboutStepRule | null; - defineRuleData: DefineStepRule | null; - scheduleRuleData: ScheduleStepRule | null; +export interface GetStepsData { + aboutRuleData: AboutStepRule; + modifiedAboutRuleDetailsData: AboutStepRuleDetails; + defineRuleData: DefineStepRule; + scheduleRuleData: ScheduleStepRule; } export const getStepsData = ({ @@ -27,58 +34,107 @@ export const getStepsData = ({ rule: Rule; detailsView?: boolean; }): GetStepsData => { - const defineRuleData: DefineStepRule | null = - rule != null - ? { - isNew: false, - index: rule.index, - queryBar: { - query: { query: rule.query as string, language: rule.language }, - filters: rule.filters as Filter[], - saved_id: rule.saved_id ?? null, - }, - } - : null; - const aboutRuleData: AboutStepRule | null = - rule != null - ? { - isNew: false, - ...pick(['description', 'name', 'references', 'severity', 'tags', 'threat'], rule), - ...(detailsView ? { name: '' } : {}), - threat: rule.threat as IMitreEnterpriseAttack[], - falsePositives: rule.false_positives, - riskScore: rule.risk_score, - timeline: { - id: rule.timeline_id ?? null, - title: rule.timeline_title ?? null, - }, - } - : null; - - const from = dateMath.parse(rule.from) ?? moment(); - const interval = dateMath.parse(`now-${rule.interval}`) ?? moment(); - - const fromDuration = moment.duration(interval.diff(from)); - let fromHumanize = `${Math.floor(fromDuration.asHours())}h`; + const defineRuleData: DefineStepRule = getDefineStepsData(rule); + const aboutRuleData: AboutStepRule = getAboutStepsData(rule, detailsView); + const modifiedAboutRuleDetailsData: AboutStepRuleDetails = getModifiedAboutDetailsData(rule); + const scheduleRuleData: ScheduleStepRule = getScheduleStepsData(rule); + + return { aboutRuleData, modifiedAboutRuleDetailsData, defineRuleData, scheduleRuleData }; +}; + +export const getDefineStepsData = (rule: Rule): DefineStepRule => { + const { index, query, language, filters, saved_id: savedId } = rule; + + return { + isNew: false, + index, + queryBar: { + query: { + query, + language, + }, + filters: filters as Filter[], + saved_id: savedId ?? null, + }, + }; +}; + +export const getScheduleStepsData = (rule: Rule): ScheduleStepRule => { + const { enabled, interval, from } = rule; + const fromHumanizedValue = getHumanizedDuration(from, interval); + + return { + isNew: false, + enabled, + interval, + from: fromHumanizedValue, + }; +}; + +export const getHumanizedDuration = (from: string, interval: string): string => { + const fromValue = dateMath.parse(from) ?? moment(); + const intervalValue = dateMath.parse(`now-${interval}`) ?? moment(); + + const fromDuration = moment.duration(intervalValue.diff(fromValue)); + const fromHumanize = `${Math.floor(fromDuration.asHours())}h`; if (fromDuration.asSeconds() < 60) { - fromHumanize = `${Math.floor(fromDuration.asSeconds())}s`; + return `${Math.floor(fromDuration.asSeconds())}s`; } else if (fromDuration.asMinutes() < 60) { - fromHumanize = `${Math.floor(fromDuration.asMinutes())}m`; + return `${Math.floor(fromDuration.asMinutes())}m`; } - const scheduleRuleData: ScheduleStepRule | null = - rule != null - ? { - isNew: false, - ...pick(['enabled', 'interval'], rule), - from: fromHumanize, - } - : null; + return fromHumanize; +}; + +export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRule => { + const { name, description, note } = determineDetailsValue(rule, detailsView); + const { + references, + severity, + false_positives: falsePositives, + risk_score: riskScore, + tags, + threat, + timeline_id: timelineId, + timeline_title: timelineTitle, + } = rule; + + return { + isNew: false, + name, + description, + note: note!, + references, + severity, + tags, + riskScore, + falsePositives, + threat: threat as IMitreEnterpriseAttack[], + timeline: { + id: timelineId ?? null, + title: timelineTitle ?? null, + }, + }; +}; + +export const determineDetailsValue = ( + rule: Rule, + detailsView: boolean +): Pick => { + const { name, description, note } = rule; + if (detailsView) { + return { name: '', description: '', note: '' }; + } - return { aboutRuleData, defineRuleData, scheduleRuleData }; + return { name, description, note: note ?? '' }; }; +export const getModifiedAboutDetailsData = (rule: Rule): AboutStepRuleDetails => ({ + note: rule.note ?? '', + description: rule.description, +}); + export const useQuery = () => new URLSearchParams(useLocation().search); export type PrePackagedRuleStatus = diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts index 34df20de1e461..aa50626a1231a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/types.ts @@ -36,7 +36,7 @@ export interface RuleStepData { export interface RuleStepProps { addPadding?: boolean; - descriptionDirection?: 'row' | 'column'; + descriptionColumns?: 'multi' | 'single' | 'singleSplit'; setStepData?: (step: RuleStep, data: unknown, isValid: boolean) => void; isReadOnlyView: boolean; isUpdateView?: boolean; @@ -58,6 +58,12 @@ export interface AboutStepRule extends StepRuleData { tags: string[]; timeline: FieldValueTimeline; threat: IMitreEnterpriseAttack[]; + note: string; +} + +export interface AboutStepRuleDetails { + note: string; + description: string; } export interface DefineStepRule extends StepRuleData { @@ -91,6 +97,7 @@ export interface AboutStepRuleJson { timeline_id?: string; timeline_title?: string; threat: IMitreEnterpriseAttack[]; + note?: string; } export interface ScheduleStepRuleJson { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts index dd4acaeaf5a02..39277b3d3c77e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts @@ -19,7 +19,7 @@ export const TOTAL_SIGNAL = i18n.translate('xpack.siem.detectionEngine.totalSign }); export const SIGNAL = i18n.translate('xpack.siem.detectionEngine.signalTitle', { - defaultMessage: 'Signals (SIEM Detections)', + defaultMessage: 'Detected signals', }); export const ALERT = i18n.translate('xpack.siem.detectionEngine.alertTitle', { From 79b04547dbc93aafb6b390a830a16628c286eeac Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Tue, 17 Mar 2020 18:14:02 +0100 Subject: [PATCH 04/35] [SIEM] Adds 'Closes one signal when more than one opened signals are selected' test again (#60380) * Revert "Revert "adds new test (#60064)"" This reverts commit 4a8fd0afee9262916348c51e98f2ac955e25b2ac. * waits for having 25 signals displayed --- .../cypress/integration/detections.spec.ts | 44 ++++++++++++++++++- .../siem/cypress/screens/detections.ts | 4 +- .../plugins/siem/cypress/tasks/detections.ts | 7 +++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts index 1624586d4ca14..de17f40a3ac71 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections.spec.ts @@ -5,12 +5,14 @@ */ import { NUMBER_OF_SIGNALS, + OPEN_CLOSE_SIGNALS_BTN, SELECTED_SIGNALS, SHOWING_SIGNALS, SIGNALS, } from '../screens/detections'; import { + closeFirstSignal, closeSignals, goToClosedSignals, goToOpenedSignals, @@ -26,7 +28,7 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS } from '../urls/navigation'; describe('Detections', () => { - before(() => { + beforeEach(() => { esArchiverLoad('signals'); loginAndWaitForPage(DETECTIONS); }); @@ -53,6 +55,7 @@ describe('Detections', () => { waitForSignals(); cy.reload(); waitForSignals(); + waitForSignalsToBeLoaded(); const expectedNumberOfSignalsAfterClosing = +numberOfSignals - numberOfSignalsToBeClosed; cy.get(NUMBER_OF_SIGNALS) @@ -111,4 +114,43 @@ describe('Detections', () => { .should('eql', expectedNumberOfOpenedSignals.toString()); }); }); + + it('Closes one signal when more than one opened signals are selected', () => { + waitForSignalsToBeLoaded(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .then(numberOfSignals => { + const numberOfSignalsToBeClosed = 1; + const numberOfSignalsToBeSelected = 3; + + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('have.attr', 'disabled'); + selectNumberOfSignals(numberOfSignalsToBeSelected); + cy.get(OPEN_CLOSE_SIGNALS_BTN).should('not.have.attr', 'disabled'); + + closeFirstSignal(); + cy.reload(); + waitForSignalsToBeLoaded(); + waitForSignals(); + + const expectedNumberOfSignals = +numberOfSignals - numberOfSignalsToBeClosed; + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eq', expectedNumberOfSignals.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${expectedNumberOfSignals.toString()} signals`); + + goToClosedSignals(); + waitForSignals(); + + cy.get(NUMBER_OF_SIGNALS) + .invoke('text') + .should('eql', numberOfSignalsToBeClosed.toString()); + cy.get(SHOWING_SIGNALS) + .invoke('text') + .should('eql', `Showing ${numberOfSignalsToBeClosed.toString()} signal`); + cy.get(SIGNALS).should('have.length', numberOfSignalsToBeClosed); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index 8b5ba23578807..f388ac1215d01 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -12,7 +12,9 @@ export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal export const NUMBER_OF_SIGNALS = '[data-test-subj="server-side-event-count"]'; -export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] .siemLinkIcon__label'; +export const OPEN_CLOSE_SIGNAL_BTN = '[data-test-subj="update-signal-status-button"]'; + +export const OPEN_CLOSE_SIGNALS_BTN = '[data-test-subj="openCloseSignal"] button'; export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index 21a0c136b90df..3416e3eb81de3 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -8,6 +8,7 @@ import { CLOSED_SIGNALS_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, + OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, SIGNALS, @@ -15,6 +16,12 @@ import { } from '../screens/detections'; import { REFRESH_BUTTON } from '../screens/siem_header'; +export const closeFirstSignal = () => { + cy.get(OPEN_CLOSE_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; From 4da0cb36844a3ff368dbeda25b4cc0f9738af920 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 10:24:58 -0700 Subject: [PATCH 05/35] [Ingest] Support `show_user` package registry flag (#60338) * Support registry `show_user` var definition property (elastic/package-registry#266) * Add tests --- .../ingest_manager/common/types/models/epm.ts | 1 + .../components/datasource_input_config.tsx | 7 ++- .../datasource_input_stream_config.tsx | 7 ++- .../create_datasource_page/services/index.ts | 6 ++ .../services/is_advanced_var.test.ts | 62 +++++++++++++++++++ .../services/is_advanced_var.ts | 13 ++++ 6 files changed, 90 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index 6b8403b74a759..28786530db018 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -186,6 +186,7 @@ export interface RegistryVarsEntry { description?: string; type: string; required?: boolean; + show_user?: boolean; multi?: boolean; default?: string | string[]; os?: { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx index 39f2f048ab88d..69d2194638441 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx @@ -15,6 +15,7 @@ import { EuiTitle, } from '@elastic/eui'; import { DatasourceInput, RegistryVarsEntry } from '../../../../types'; +import { isAdvancedVar } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputConfig: React.FunctionComponent<{ @@ -30,10 +31,10 @@ export const DatasourceInputConfig: React.FunctionComponent<{ if (packageInputVars) { packageInputVars.forEach(varDef => { - if (varDef.required && !varDef.default) { - requiredVars.push(varDef); - } else { + if (isAdvancedVar(varDef)) { advancedVars.push(varDef); + } else { + requiredVars.push(varDef); } }); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx index e4b138932cb53..1f483f1911bcc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx @@ -16,6 +16,7 @@ import { EuiButtonEmpty, } from '@elastic/eui'; import { DatasourceInputStream, RegistryStream, RegistryVarsEntry } from '../../../../types'; +import { isAdvancedVar } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputStreamConfig: React.FunctionComponent<{ @@ -31,10 +32,10 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ if (packageInputStream.vars && packageInputStream.vars.length) { packageInputStream.vars.forEach(varDef => { - if (varDef.required && !varDef.default) { - requiredVars.push(varDef); - } else { + if (isAdvancedVar(varDef)) { advancedVars.push(varDef); + } else { + requiredVars.push(varDef); } }); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts new file mode 100644 index 0000000000000..44e5bfa41cb9b --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { isAdvancedVar } from './is_advanced_var'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts new file mode 100644 index 0000000000000..67796d69863fa --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { isAdvancedVar } from './is_advanced_var'; + +describe('Ingest Manager - isAdvancedVar', () => { + it('returns true for vars that should be show under advanced options', () => { + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + default: 'default string', + }) + ).toBe(true); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + default: 'default string', + }) + ).toBe(true); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + }) + ).toBe(true); + }); + + it('returns false for vars that should be show by default', () => { + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + default: 'default string', + show_user: true, + }) + ).toBe(false); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + required: true, + }) + ).toBe(false); + + expect( + isAdvancedVar({ + name: 'mock_var', + type: 'text', + show_user: true, + }) + ).toBe(false); + }); +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts new file mode 100644 index 0000000000000..398f1d675c5df --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/is_advanced_var.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { RegistryVarsEntry } from '../../../../types'; + +export const isAdvancedVar = (varDef: RegistryVarsEntry): boolean => { + if (varDef.show_user || (varDef.required && !varDef.default)) { + return false; + } + return true; +}; From 6b7731bb74b1f133a39730c1e4de847c655e9d7a Mon Sep 17 00:00:00 2001 From: Joel Griffith Date: Tue, 17 Mar 2020 10:41:06 -0700 Subject: [PATCH 06/35] [Reporting] Wholesale moves client to newest-platform (#58945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move over to new plugin space, working implementation * Fixing tests for report_listing snapshots * WIP: Fixing react-component tests * Fixing report_info_button tests * Fixing download linksies * WIP: Final working implementation * Fixing attachAction API + API URLs * Let the past die. Kill it if you have to. That’s the only way to become what you were meant to be. * Fixing stream-client for new platform APIs * Fixing types and tests * Fix broken mock * Adds back in warnings to report info button * kibana.json line-breaks on required plugins * Fixing broked snapshots * Fix license checks in client-side components * Adding back in warnings to report_listing component * Fix danglig unused import * Adds license checks for basic to our csv panel action * Fixes issues from prior fork * Move relative pathing to absolute * Fix POST URL copying as we've moved from static methods * Fix layoutId props * Fixes types for layoutId Co-authored-by: Elastic Machine --- .../workpad_header/workpad_export/index.ts | 2 +- x-pack/legacy/plugins/reporting/index.ts | 16 +- .../report_info_button.test.mocks.ts | 8 - .../public/components/report_listing.test.tsx | 77 --------- .../public/constants/job_statuses.tsx | 13 -- .../reporting/public/lib/download_report.ts | 23 --- .../reporting/public/lib/job_queue_client.ts | 89 ---------- .../reporting/public/lib/reporting_client.ts | 37 ----- .../reporting/public/register_feature.ts | 27 ---- .../public/views/management/index.js | 7 - .../public/views/management/jobs.html | 3 - .../reporting/public/views/management/jobs.js | 59 ------- .../public/views/management/management.js | 44 ----- x-pack/legacy/plugins/reporting/types.d.ts | 16 -- x-pack/plugins/reporting/common/poller.ts | 96 +++++++++++ x-pack/plugins/reporting/constants.ts | 25 ++- x-pack/plugins/reporting/index.d.ts | 16 ++ x-pack/plugins/reporting/kibana.json | 10 +- .../report_info_button.test.tsx.snap | 0 .../report_listing.test.tsx.snap | 0 .../public/components/general_error.tsx | 2 +- .../public/components/job_failure.tsx | 2 +- .../components/job_queue_client.test.mocks.ts | 17 ++ .../public/components/job_success.tsx | 2 +- .../components/job_warning_formulas.tsx | 2 +- .../components/job_warning_max_size.tsx | 2 +- .../public/components/report_error_button.tsx | 5 +- .../components/report_info_button.test.tsx | 32 ++-- .../public/components/report_info_button.tsx | 12 +- .../public/components/report_listing.test.tsx | 85 ++++++++++ .../public/components/report_listing.tsx | 83 ++++++---- .../components/reporting_panel_content.tsx | 55 ++++--- .../screen_capture_panel_content.tsx | 6 + x-pack/plugins/reporting/public/index.ts | 4 +- .../__snapshots__/stream_handler.test.ts.snap | 4 +- .../lib/job_completion_notifications.ts | 2 +- .../plugins/reporting/public/lib/job_queue.ts | 27 ---- .../public/lib/license_check.test.ts | 50 ++++++ .../reporting/public/lib/license_check.ts | 52 ++++++ .../public/lib/reporting_api_client.ts | 152 ++++++++++++++++++ .../public/lib/stream_handler.test.ts | 68 +++----- .../reporting/public/lib/stream_handler.ts | 47 +++--- .../panel_actions/get_csv_panel_action.tsx | 65 ++++---- x-pack/plugins/reporting/public/plugin.tsx | 127 +++++++++++++-- .../register_csv_reporting.tsx | 43 +++-- .../register_pdf_png_reporting.tsx} | 120 ++++++++------ 46 files changed, 920 insertions(+), 714 deletions(-) delete mode 100644 x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx delete mode 100644 x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/download_report.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/register_feature.ts delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/index.js delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/jobs.html delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/jobs.js delete mode 100644 x-pack/legacy/plugins/reporting/public/views/management/management.js create mode 100644 x-pack/plugins/reporting/common/poller.ts rename x-pack/{legacy => }/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap (100%) create mode 100644 x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts rename x-pack/{legacy => }/plugins/reporting/public/components/report_error_button.tsx (92%) rename x-pack/{legacy => }/plugins/reporting/public/components/report_info_button.test.tsx (60%) rename x-pack/{legacy => }/plugins/reporting/public/components/report_info_button.tsx (95%) create mode 100644 x-pack/plugins/reporting/public/components/report_listing.test.tsx rename x-pack/{legacy => }/plugins/reporting/public/components/report_listing.tsx (85%) rename x-pack/{legacy => }/plugins/reporting/public/components/reporting_panel_content.tsx (88%) rename x-pack/{legacy => }/plugins/reporting/public/components/screen_capture_panel_content.tsx (91%) rename x-pack/{legacy => }/plugins/reporting/public/lib/job_completion_notifications.ts (98%) delete mode 100644 x-pack/plugins/reporting/public/lib/job_queue.ts create mode 100644 x-pack/plugins/reporting/public/lib/license_check.test.ts create mode 100644 x-pack/plugins/reporting/public/lib/license_check.ts create mode 100644 x-pack/plugins/reporting/public/lib/reporting_api_client.ts rename x-pack/{legacy => }/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx (72%) rename x-pack/{legacy => }/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx (61%) rename x-pack/{legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx => plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx} (57%) diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts index 7f81adad6bf9b..949264fcc9fdb 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts @@ -6,7 +6,7 @@ import { connect } from 'react-redux'; import { compose, withProps } from 'recompose'; -import * as jobCompletionNotifications from '../../../../../reporting/public/lib/job_completion_notifications'; +import { jobCompletionNotifications } from '../../../../../../../plugins/reporting/public'; // @ts-ignore Untyped local import { getWorkpad, getPages } from '../../../state/selectors/workpad'; // @ts-ignore Untyped local diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index 9ce4e807f8ef8..89e98302cddc9 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -10,7 +10,7 @@ import { resolve } from 'path'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; import { config as reportingConfig } from './config'; import { legacyInit } from './server/legacy'; -import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types'; +import { ReportingPluginSpecOptions } from './types'; const kbToBase64Length = (kb: number) => { return Math.floor((kb * 1024 * 8) / 6); @@ -25,20 +25,6 @@ export const reporting = (kibana: any) => { config: reportingConfig, uiExports: { - shareContextMenuExtensions: [ - 'plugins/reporting/share_context_menu/register_csv_reporting', - 'plugins/reporting/share_context_menu/register_reporting', - ], - embeddableActions: ['plugins/reporting/panel_actions/get_csv_panel_action'], - home: ['plugins/reporting/register_feature'], - managementSections: ['plugins/reporting/views/management'], - injectDefaultVars(server: Legacy.Server, options?: ReportingConfigOptions) { - const config = server.config(); - return { - reportingPollConfig: options ? options.poll : {}, - enablePanelActionDownload: config.get('xpack.reporting.csv.enablePanelActionDownload'), - }; - }, uiSettingDefaults: { [UI_SETTINGS_CUSTOM_PDF_LOGO]: { name: i18n.translate('xpack.reporting.pdfFooterImageLabel', { diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts b/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts deleted file mode 100644 index 9dd7cbb5fc567..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.mocks.ts +++ /dev/null @@ -1,8 +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 mockJobQueueClient = { list: jest.fn(), total: jest.fn(), getInfo: jest.fn() }; -jest.mock('../lib/job_queue_client', () => ({ jobQueueClient: mockJobQueueClient })); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx b/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx deleted file mode 100644 index d78eb5c409c1f..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/components/report_listing.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -interface JobData { - _index: string; - _id: string; - _source: { - browser_type: string; - created_at: string; - jobtype: string; - created_by: string; - payload: { - type: string; - title: string; - }; - kibana_name?: string; // undefined if job is pending (not yet claimed by an instance) - kibana_id?: string; // undefined if job is pending (not yet claimed by an instance) - output?: { content_type: string; size: number }; // undefined if job is incomplete - completed_at?: string; // undefined if job is incomplete - }; -} - -jest.mock('ui/chrome', () => ({ - getInjected() { - return { - jobsRefresh: { - interval: 10, - intervalErrorMultiplier: 2, - }, - }; - }, -})); - -jest.mock('ui/kfetch', () => ({ - kfetch: ({ pathname }: { pathname: string }): Promise => { - if (pathname === '/api/reporting/jobs/list') { - return Promise.resolve([ - { _index: '.reporting-2019.08.18', _id: 'jzoik8dh1q2i89fb5f19znm6', _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.869Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik7tn1q2i89fb5f60e5ve', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.155Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik5tb1q2i89fb5fckchny', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:21.551Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik5a11q2i89fb5f130t2m', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:20.857Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik3ka1q2i89fb5fdx93g7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:18.634Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik2vt1q2i89fb5ffw723n', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:17.753Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoik1851q2i89fb5fdge6e7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1080, height: 720 } }, type: 'canvas workpad', title: 'My Canvas Workpad - Dark', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:15.605Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoijyre1q2i89fb5fa7xzvi', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:12.410Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jzoijv5h1q2i89fb5ffklnhx', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:07.733Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore - { _index: '.reporting-2019.08.18', _id: 'jznhgk7r1bx789fb5f6hxok7', _score: null, _source: { kibana_name: 'spicy.local', browser_type: 'chromium', created_at: '2019-08-23T02:15:47.799Z', jobtype: 'printable_pdf', created_by: 'elastic', kibana_id: 'ca75e26c-2b7d-464f-aef0-babb67c735a0', output: { content_type: 'application/pdf', size: 877114 }, completed_at: '2019-08-23T02:15:57.707Z', payload: { type: 'dashboard (legacy)', title: 'tests-panels', }, max_attempts: 3, started_at: '2019-08-23T02:15:48.794Z', attempts: 1, status: 'completed', }, }, // prettier-ignore - ]); - } - - // query for jobs count - return Promise.resolve(18); - }, -})); - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReportListing } from './report_listing'; - -describe('ReportListing', () => { - it('Report job listing with some items', () => { - const wrapper = mountWithIntl( - - ); - wrapper.update(); - const input = wrapper.find('[data-test-subj="reportJobListing"]'); - expect(input).toMatchSnapshot(); - }); -}); diff --git a/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx b/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx deleted file mode 100644 index 29c51217a5c64..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/constants/job_statuses.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export enum JobStatuses { - PENDING = 'pending', - PROCESSING = 'processing', - COMPLETED = 'completed', - FAILED = 'failed', - CANCELLED = 'cancelled', -} diff --git a/x-pack/legacy/plugins/reporting/public/lib/download_report.ts b/x-pack/legacy/plugins/reporting/public/lib/download_report.ts deleted file mode 100644 index 54194c87afabc..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/download_report.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { API_BASE_URL } from '../../common/constants'; - -const { core } = npStart; - -export function getReportURL(jobId: string) { - const apiBaseUrl = core.http.basePath.prepend(API_BASE_URL); - const downloadLink = `${apiBaseUrl}/jobs/download/${jobId}`; - - return downloadLink; -} - -export function downloadReport(jobId: string) { - const location = getReportURL(jobId); - - window.open(location); -} diff --git a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts deleted file mode 100644 index 87d4174168b7f..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts +++ /dev/null @@ -1,89 +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 { npStart } from 'ui/new_platform'; -import { API_LIST_URL } from '../../common/constants'; - -const { core } = npStart; - -export interface JobQueueEntry { - _id: string; - _source: any; -} - -export interface JobContent { - content: string; - content_type: boolean; -} - -export interface JobInfo { - kibana_name: string; - kibana_id: string; - browser_type: string; - created_at: string; - priority: number; - jobtype: string; - created_by: string; - timeout: number; - output: { - content_type: string; - size: number; - warnings: string[]; - }; - process_expiration: string; - completed_at: string; - payload: { - layout: { id: string; dimensions: { width: number; height: number } }; - objects: Array<{ relativeUrl: string }>; - type: string; - title: string; - forceNow: string; - browserTimezone: string; - }; - meta: { - layout: string; - objectType: string; - }; - max_attempts: number; - started_at: string; - attempts: number; - status: string; -} - -class JobQueueClient { - public list = (page = 0, jobIds: string[] = []): Promise => { - const query = { page } as any; - if (jobIds.length > 0) { - // Only getting the first 10, to prevent URL overflows - query.ids = jobIds.slice(0, 10).join(','); - } - - return core.http.get(`${API_LIST_URL}/list`, { - query, - asSystemRequest: true, - }); - }; - - public total(): Promise { - return core.http.get(`${API_LIST_URL}/count`, { - asSystemRequest: true, - }); - } - - public getContent(jobId: string): Promise { - return core.http.get(`${API_LIST_URL}/output/${jobId}`, { - asSystemRequest: true, - }); - } - - public getInfo(jobId: string): Promise { - return core.http.get(`${API_LIST_URL}/info/${jobId}`, { - asSystemRequest: true, - }); - } -} - -export const jobQueueClient = new JobQueueClient(); diff --git a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts b/x-pack/legacy/plugins/reporting/public/lib/reporting_client.ts deleted file mode 100644 index d471dc57fc9e1..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/lib/reporting_client.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 { stringify } from 'query-string'; -import { npStart } from 'ui/new_platform'; -// @ts-ignore -import rison from 'rison-node'; -import { add } from './job_completion_notifications'; - -const { core } = npStart; -const API_BASE_URL = '/api/reporting/generate'; - -interface JobParams { - [paramName: string]: any; -} - -export const getReportingJobPath = (exportType: string, jobParams: JobParams) => { - const params = stringify({ jobParams: rison.encode(jobParams) }); - - return `${core.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; -}; - -export const createReportingJob = async (exportType: string, jobParams: any) => { - const jobParamsRison = rison.encode(jobParams); - const resp = await core.http.post(`${API_BASE_URL}/${exportType}`, { - method: 'POST', - body: JSON.stringify({ - jobParams: jobParamsRison, - }), - }); - - add(resp.job.id); - - return resp; -}; diff --git a/x-pack/legacy/plugins/reporting/public/register_feature.ts b/x-pack/legacy/plugins/reporting/public/register_feature.ts deleted file mode 100644 index 4e8d32facfcec..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/register_feature.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 { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -const { - plugins: { home }, -} = npSetup; - -home.featureCatalogue.register({ - id: 'reporting', - title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { - defaultMessage: 'Reporting', - }), - description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { - defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', - }), - icon: 'reportingApp', - path: '/app/kibana#/management/kibana/reporting', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, -}); diff --git a/x-pack/legacy/plugins/reporting/public/views/management/index.js b/x-pack/legacy/plugins/reporting/public/views/management/index.js deleted file mode 100644 index 0ed6fe09ef80a..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './management'; diff --git a/x-pack/legacy/plugins/reporting/public/views/management/jobs.html b/x-pack/legacy/plugins/reporting/public/views/management/jobs.html deleted file mode 100644 index 5471513d64d95..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/jobs.html +++ /dev/null @@ -1,3 +0,0 @@ - -
    -
    \ No newline at end of file diff --git a/x-pack/legacy/plugins/reporting/public/views/management/jobs.js b/x-pack/legacy/plugins/reporting/public/views/management/jobs.js deleted file mode 100644 index 7205fad8cca53..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/jobs.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import routes from 'ui/routes'; -import template from 'plugins/reporting/views/management/jobs.html'; - -import { ReportListing } from '../../components/report_listing'; -import { i18n } from '@kbn/i18n'; -import { I18nContext } from 'ui/i18n'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; - -const REACT_ANCHOR_DOM_ELEMENT_ID = 'reportListingAnchor'; - -routes.when('/management/kibana/reporting', { - template, - k7Breadcrumbs: () => [ - MANAGEMENT_BREADCRUMB, - { - text: i18n.translate('xpack.reporting.breadcrumb', { - defaultMessage: 'Reporting', - }), - }, - ], - controllerAs: 'jobsCtrl', - controller($scope, kbnUrl) { - $scope.$$postDigest(() => { - const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID); - if (!node) { - return; - } - - render( - - - , - node - ); - }); - - $scope.$on('$destroy', () => { - const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID); - if (node) { - unmountComponentAtNode(node); - } - }); - }, -}); diff --git a/x-pack/legacy/plugins/reporting/public/views/management/management.js b/x-pack/legacy/plugins/reporting/public/views/management/management.js deleted file mode 100644 index 8643e6fa8b8b4..0000000000000 --- a/x-pack/legacy/plugins/reporting/public/views/management/management.js +++ /dev/null @@ -1,44 +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 { i18n } from '@kbn/i18n'; -import routes from 'ui/routes'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -import 'plugins/reporting/views/management/jobs'; - -routes.defaults(/\/management/, { - resolve: { - reportingManagementSection: function() { - const kibanaManagementSection = management.getSection('kibana'); - const showReportingLinks = xpackInfo.get('features.reporting.management.showLinks'); - - kibanaManagementSection.deregister('reporting'); - if (showReportingLinks) { - const enableReportingLinks = xpackInfo.get('features.reporting.management.enableLinks'); - const tooltipMessage = xpackInfo.get('features.reporting.management.message'); - - let url; - let tooltip; - if (enableReportingLinks) { - url = '#/management/kibana/reporting'; - } else { - tooltip = tooltipMessage; - } - - return kibanaManagementSection.register('reporting', { - order: 15, - display: i18n.translate('xpack.reporting.management.reportingTitle', { - defaultMessage: 'Reporting', - }), - url, - tooltip, - }); - } - }, - }, -}); diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index b4d49fd21f230..917e9d7daae40 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -23,22 +23,6 @@ export type Job = EventEmitter & { }; }; -export interface ReportingConfigOptions { - browser: BrowserConfig; - poll: { - jobCompletionNotifier: { - interval: number; - intervalErrorMultiplier: number; - }; - jobsRefresh: { - interval: number; - intervalErrorMultiplier: number; - }; - }; - queue: QueueConfig; - capture: CaptureConfig; -} - export interface NetworkPolicyRule { allow: boolean; protocol: string; diff --git a/x-pack/plugins/reporting/common/poller.ts b/x-pack/plugins/reporting/common/poller.ts new file mode 100644 index 0000000000000..919d7273062a8 --- /dev/null +++ b/x-pack/plugins/reporting/common/poller.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { PollerOptions } from '..'; + +// @TODO Maybe move to observables someday +export class Poller { + private readonly functionToPoll: () => Promise; + private readonly successFunction: (...args: any) => any; + private readonly errorFunction: (error: Error) => any; + private _isRunning: boolean; + private _timeoutId: NodeJS.Timeout | null; + private pollFrequencyInMillis: number; + private trailing: boolean; + private continuePollingOnError: boolean; + private pollFrequencyErrorMultiplier: number; + + constructor(options: PollerOptions) { + this.functionToPoll = options.functionToPoll; // Must return a Promise + this.successFunction = options.successFunction || _.noop; + this.errorFunction = options.errorFunction || _.noop; + this.pollFrequencyInMillis = options.pollFrequencyInMillis; + this.trailing = options.trailing || false; + this.continuePollingOnError = options.continuePollingOnError || false; + this.pollFrequencyErrorMultiplier = options.pollFrequencyErrorMultiplier || 1; + + this._timeoutId = null; + this._isRunning = false; + } + + getPollFrequency() { + return this.pollFrequencyInMillis; + } + + _poll() { + return this.functionToPoll() + .then(this.successFunction) + .then(() => { + if (!this._isRunning) { + return; + } + + this._timeoutId = setTimeout(this._poll.bind(this), this.pollFrequencyInMillis); + }) + .catch(e => { + this.errorFunction(e); + if (!this._isRunning) { + return; + } + + if (this.continuePollingOnError) { + this._timeoutId = setTimeout( + this._poll.bind(this), + this.pollFrequencyInMillis * this.pollFrequencyErrorMultiplier + ); + } else { + this.stop(); + } + }); + } + + start() { + if (this._isRunning) { + return; + } + + this._isRunning = true; + if (this.trailing) { + this._timeoutId = setTimeout(this._poll.bind(this), this.pollFrequencyInMillis); + } else { + this._poll(); + } + } + + stop() { + if (!this._isRunning) { + return; + } + + this._isRunning = false; + + if (this._timeoutId) { + clearTimeout(this._timeoutId); + } + + this._timeoutId = null; + } + + isRunning() { + return this._isRunning; + } +} diff --git a/x-pack/plugins/reporting/constants.ts b/x-pack/plugins/reporting/constants.ts index fe5673a0b74b5..8f47a0a6b2ac1 100644 --- a/x-pack/plugins/reporting/constants.ts +++ b/x-pack/plugins/reporting/constants.ts @@ -14,8 +14,31 @@ export const JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG = { }, }; -export const API_BASE_URL = '/api/reporting/jobs'; +// Routes +export const API_BASE_URL = '/api/reporting'; +export const API_LIST_URL = `${API_BASE_URL}/jobs`; +export const API_BASE_GENERATE = `${API_BASE_URL}/generate`; +export const API_GENERATE_IMMEDIATE = `${API_BASE_URL}/v1/generate/immediate/csv/saved-object`; export const REPORTING_MANAGEMENT_HOME = '/app/kibana#/management/kibana/reporting'; +// Statuses export const JOB_STATUS_FAILED = 'failed'; export const JOB_STATUS_COMPLETED = 'completed'; + +export enum JobStatuses { + PENDING = 'pending', + PROCESSING = 'processing', + COMPLETED = 'completed', + FAILED = 'failed', + CANCELLED = 'cancelled', +} + +// Types +export const PDF_JOB_TYPE = 'printable_pdf'; +export const PNG_JOB_TYPE = 'PNG'; +export const CSV_JOB_TYPE = 'csv'; +export const CSV_FROM_SAVEDOBJECT_JOB_TYPE = 'csv_from_savedobject'; +export const USES_HEADLESS_JOB_TYPES = [PDF_JOB_TYPE, PNG_JOB_TYPE]; + +// Actions +export const CSV_REPORTING_ACTION = 'downloadCsvReport'; diff --git a/x-pack/plugins/reporting/index.d.ts b/x-pack/plugins/reporting/index.d.ts index 9559de4a5bb03..7c1a2ebd7d9de 100644 --- a/x-pack/plugins/reporting/index.d.ts +++ b/x-pack/plugins/reporting/index.d.ts @@ -57,3 +57,19 @@ export type DownloadReportFn = (jobId: JobId) => DownloadLink; type ManagementLink = string; export type ManagementLinkFn = () => ManagementLink; + +export interface PollerOptions { + functionToPoll: () => Promise; + pollFrequencyInMillis: number; + trailing?: boolean; + continuePollingOnError?: boolean; + pollFrequencyErrorMultiplier?: number; + successFunction?: (...args: any) => any; + errorFunction?: (error: Error) => any; +} + +export interface LicenseCheckResults { + enableLinks: boolean; + showLinks: boolean; + message: string; +} diff --git a/x-pack/plugins/reporting/kibana.json b/x-pack/plugins/reporting/kibana.json index 50f552b0d9fb0..a7e2bd288f0b1 100644 --- a/x-pack/plugins/reporting/kibana.json +++ b/x-pack/plugins/reporting/kibana.json @@ -2,7 +2,15 @@ "id": "reporting", "version": "8.0.0", "kibanaVersion": "kibana", - "requiredPlugins": [], + "requiredPlugins": [ + "home", + "management", + "licensing", + "uiActions", + "embeddable", + "share", + "kibanaLegacy" + ], "server": false, "ui": true } diff --git a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap rename to x-pack/plugins/reporting/public/components/__snapshots__/report_info_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap rename to x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap diff --git a/x-pack/plugins/reporting/public/components/general_error.tsx b/x-pack/plugins/reporting/public/components/general_error.tsx index feb0ea0062ace..bc1ec901cc475 100644 --- a/x-pack/plugins/reporting/public/components/general_error.tsx +++ b/x-pack/plugins/reporting/public/components/general_error.tsx @@ -7,7 +7,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; export const getGeneralErrorToast = (errorText: string, err: Error): ToastInput => ({ diff --git a/x-pack/plugins/reporting/public/components/job_failure.tsx b/x-pack/plugins/reporting/public/components/job_failure.tsx index 7544cbf906458..628ecb56b9c21 100644 --- a/x-pack/plugins/reporting/public/components/job_failure.tsx +++ b/x-pack/plugins/reporting/public/components/job_failure.tsx @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobSummary, ManagementLinkFn } from '../../index.d'; diff --git a/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts b/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts new file mode 100644 index 0000000000000..5e9614e27e2fd --- /dev/null +++ b/x-pack/plugins/reporting/public/components/job_queue_client.test.mocks.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const mockAPIClient = { + http: jest.fn(), + list: jest.fn(), + total: jest.fn(), + getInfo: jest.fn(), + getContent: jest.fn(), + getReportURL: jest.fn(), + downloadReport: jest.fn(), +}; + +jest.mock('../lib/reporting_api_client', () => mockAPIClient); diff --git a/x-pack/plugins/reporting/public/components/job_success.tsx b/x-pack/plugins/reporting/public/components/job_success.tsx index b538cef030e0d..c2feac382ca7a 100644 --- a/x-pack/plugins/reporting/public/components/job_success.tsx +++ b/x-pack/plugins/reporting/public/components/job_success.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx index 7981237c9b781..22f656dbe738c 100644 --- a/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx +++ b/x-pack/plugins/reporting/public/components/job_warning_formulas.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx index caeda6fc01678..1abba8888bb81 100644 --- a/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx +++ b/x-pack/plugins/reporting/public/components/job_warning_max_size.tsx @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastInput } from '../../../../../src/core/public'; +import { ToastInput } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { JobId, JobSummary } from '../../index.d'; import { ReportLink } from './report_link'; diff --git a/x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx b/x-pack/plugins/reporting/public/components/report_error_button.tsx similarity index 92% rename from x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx rename to x-pack/plugins/reporting/public/components/report_error_button.tsx index 3e6fd07847f2c..252dee9c619a9 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_error_button.tsx +++ b/x-pack/plugins/reporting/public/components/report_error_button.tsx @@ -7,11 +7,12 @@ import { EuiButtonIcon, EuiCallOut, EuiPopover } from '@elastic/eui'; import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component } from 'react'; -import { JobContent, jobQueueClient } from '../lib/job_queue_client'; +import { JobContent, ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { jobId: string; intl: InjectedIntl; + apiClient: ReportingAPIClient; } interface State { @@ -90,7 +91,7 @@ class ReportErrorButtonUi extends Component { private loadError = async () => { this.setState({ isLoading: true }); try { - const reportContent: JobContent = await jobQueueClient.getContent(this.props.jobId); + const reportContent: JobContent = await this.props.apiClient.getContent(this.props.jobId); if (this.mounted) { this.setState({ isLoading: false, error: reportContent.content }); } diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx b/x-pack/plugins/reporting/public/components/report_info_button.test.tsx similarity index 60% rename from x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx rename to x-pack/plugins/reporting/public/components/report_info_button.test.tsx index 3b9c2a8485423..2edd59e6de7a3 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.test.tsx +++ b/x-pack/plugins/reporting/public/components/report_info_button.test.tsx @@ -4,27 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mockJobQueueClient } from './report_info_button.test.mocks'; - import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ReportInfoButton } from './report_info_button'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; -describe('ReportInfoButton', () => { - beforeEach(() => { - mockJobQueueClient.getInfo = jest.fn(() => ({ - payload: { title: 'Test Job' }, - })); - }); +jest.mock('../lib/reporting_api_client'); +const httpSetup = {} as any; +const apiClient = new ReportingAPIClient(httpSetup); + +describe('ReportInfoButton', () => { it('handles button click flyout on click', () => { - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); expect(input).toMatchSnapshot(); }); - it('opens flyout with info', () => { - const wrapper = mountWithIntl(); + it('opens flyout with info', async () => { + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); input.simulate('click'); @@ -32,17 +30,17 @@ describe('ReportInfoButton', () => { const flyout = wrapper.find('[data-test-subj="reportInfoFlyout"]'); expect(flyout).toMatchSnapshot(); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledTimes(1); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledWith('abc-456'); + expect(apiClient.getInfo).toHaveBeenCalledTimes(1); + expect(apiClient.getInfo).toHaveBeenCalledWith('abc-456'); }); it('opens flyout with fetch error info', () => { // simulate fetch failure - mockJobQueueClient.getInfo = jest.fn(() => { + apiClient.getInfo = jest.fn(() => { throw new Error('Could not fetch the job info'); }); - const wrapper = mountWithIntl(); + const wrapper = mountWithIntl(); const input = wrapper.find('[data-test-subj="reportInfoButton"]').hostNodes(); input.simulate('click'); @@ -50,7 +48,7 @@ describe('ReportInfoButton', () => { const flyout = wrapper.find('[data-test-subj="reportInfoFlyout"]'); expect(flyout).toMatchSnapshot(); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledTimes(1); - expect(mockJobQueueClient.getInfo).toHaveBeenCalledWith('abc-789'); + expect(apiClient.getInfo).toHaveBeenCalledTimes(1); + expect(apiClient.getInfo).toHaveBeenCalledWith('abc-789'); }); }); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx b/x-pack/plugins/reporting/public/components/report_info_button.tsx similarity index 95% rename from x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx rename to x-pack/plugins/reporting/public/components/report_info_button.tsx index 7f5d070948e50..81a5af3b87957 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx +++ b/x-pack/plugins/reporting/public/components/report_info_button.tsx @@ -17,11 +17,12 @@ import { } from '@elastic/eui'; import React, { Component, Fragment } from 'react'; import { get } from 'lodash'; -import { USES_HEADLESS_JOB_TYPES } from '../../common/constants'; -import { JobInfo, jobQueueClient } from '../lib/job_queue_client'; +import { USES_HEADLESS_JOB_TYPES } from '../../constants'; +import { JobInfo, ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { jobId: string; + apiClient: ReportingAPIClient; } interface State { @@ -171,6 +172,7 @@ export class ReportInfoButton extends Component { description: USES_HEADLESS_JOB_TYPES.includes(jobType) ? info.browser_type || UNKNOWN : NA, }, ]; + if (warnings) { jobInfoStatus.push({ title: 'Errors', @@ -261,17 +263,17 @@ export class ReportInfoButton extends Component { private loadInfo = async () => { this.setState({ isLoading: true }); try { - const info: JobInfo = await jobQueueClient.getInfo(this.props.jobId); + const info: JobInfo = await this.props.apiClient.getInfo(this.props.jobId); if (this.mounted) { this.setState({ isLoading: false, info }); } - } catch (kfetchError) { + } catch (err) { if (this.mounted) { this.setState({ isLoading: false, calloutTitle: 'Unable to fetch report info', info: null, - error: kfetchError, + error: err, }); } } diff --git a/x-pack/plugins/reporting/public/components/report_listing.test.tsx b/x-pack/plugins/reporting/public/components/report_listing.test.tsx new file mode 100644 index 0000000000000..5cf894580eae0 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/report_listing.test.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { ReportListing } from './report_listing'; +import { Observable } from 'rxjs'; +import { ILicense } from '../../../licensing/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; + +const reportingAPIClient = { + list: () => + Promise.resolve([ + { _index: '.reporting-2019.08.18', _id: 'jzoik8dh1q2i89fb5f19znm6', _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.869Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik7tn1q2i89fb5f60e5ve', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1635, height: 792 } }, type: 'dashboard', title: 'Names', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:24.155Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik5tb1q2i89fb5fckchny', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:21.551Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik5a11q2i89fb5f130t2m', _score: null, _source: { payload: { layout: { id: 'png', dimensions: { width: 1898, height: 876 } }, title: 'cool dashboard', type: 'dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:20.857Z', jobtype: 'PNG', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik3ka1q2i89fb5fdx93g7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:18.634Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik2vt1q2i89fb5ffw723n', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1898, height: 876 } }, type: 'dashboard', title: 'cool dashboard', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:17.753Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoik1851q2i89fb5fdge6e7', _score: null, _source: { payload: { layout: { id: 'preserve_layout', dimensions: { width: 1080, height: 720 } }, type: 'canvas workpad', title: 'My Canvas Workpad - Dark', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:15.605Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoijyre1q2i89fb5fa7xzvi', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:12.410Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jzoijv5h1q2i89fb5ffklnhx', _score: null, _source: { payload: { type: 'dashboard', title: 'tests-panels', }, max_attempts: 3, browser_type: 'chromium', created_at: '2019-08-23T19:34:07.733Z', jobtype: 'printable_pdf', created_by: 'elastic', attempts: 0, status: 'pending', }, }, // prettier-ignore + { _index: '.reporting-2019.08.18', _id: 'jznhgk7r1bx789fb5f6hxok7', _score: null, _source: { kibana_name: 'spicy.local', browser_type: 'chromium', created_at: '2019-08-23T02:15:47.799Z', jobtype: 'printable_pdf', created_by: 'elastic', kibana_id: 'ca75e26c-2b7d-464f-aef0-babb67c735a0', output: { content_type: 'application/pdf', size: 877114 }, completed_at: '2019-08-23T02:15:57.707Z', payload: { type: 'dashboard (legacy)', title: 'tests-panels', }, max_attempts: 3, started_at: '2019-08-23T02:15:48.794Z', attempts: 1, status: 'completed', }, }, // prettier-ignore + ]), + total: () => Promise.resolve(18), +} as any; + +const validCheck = { + check: () => ({ + state: 'VALID', + message: '', + }), +}; + +const license$ = { + subscribe: (handler: any) => { + return handler(validCheck); + }, +} as Observable; + +const toasts = { + addDanger: jest.fn(), +} as any; + +describe('ReportListing', () => { + it('Report job listing with some items', () => { + const wrapper = mountWithIntl( + + ); + wrapper.update(); + const input = wrapper.find('[data-test-subj="reportJobListing"]'); + expect(input).toMatchSnapshot(); + }); + + it('subscribes to license changes, and unsubscribes on dismount', () => { + const unsubscribeMock = jest.fn(); + const subMock = { + subscribe: jest.fn().mockReturnValue({ + unsubscribe: unsubscribeMock, + }), + } as any; + + const wrapper = mountWithIntl( + } + redirect={jest.fn()} + toasts={toasts} + /> + ); + wrapper.update(); + expect(subMock.subscribe).toHaveBeenCalled(); + expect(unsubscribeMock).not.toHaveBeenCalled(); + wrapper.unmount(); + expect(unsubscribeMock).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx b/x-pack/plugins/reporting/public/components/report_listing.tsx similarity index 85% rename from x-pack/legacy/plugins/reporting/public/components/report_listing.tsx rename to x-pack/plugins/reporting/public/components/report_listing.tsx index 54061eda94dce..13fca019f3284 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_listing.tsx +++ b/x-pack/plugins/reporting/public/components/report_listing.tsx @@ -6,11 +6,11 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; -import moment from 'moment'; import { get } from 'lodash'; +import moment from 'moment'; import React, { Component } from 'react'; -import chrome from 'ui/chrome'; -import { toastNotifications } from 'ui/notify'; +import { Subscription } from 'rxjs'; + import { EuiBasicTable, EuiButtonIcon, @@ -21,10 +21,13 @@ import { EuiTitle, EuiToolTip, } from '@elastic/eui'; -import { Poller } from '../../../../common/poller'; -import { JobStatuses } from '../constants/job_statuses'; -import { downloadReport } from '../lib/download_report'; -import { jobQueueClient, JobQueueEntry } from '../lib/job_queue_client'; + +import { ToastsSetup, ApplicationStart } from 'src/core/public'; +import { LicensingPluginSetup, ILicense } from '../../../licensing/public'; +import { Poller } from '../../common/poller'; +import { JobStatuses, JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG } from '../../constants'; +import { ReportingAPIClient, JobQueueEntry } from '../lib/reporting_api_client'; +import { checkLicense } from '../lib/license_check'; import { ReportErrorButton } from './report_error_button'; import { ReportInfoButton } from './report_info_button'; @@ -47,11 +50,11 @@ interface Job { } interface Props { - badLicenseMessage: string; - showLinks: boolean; - enableLinks: boolean; - redirect: (url: string) => void; intl: InjectedIntl; + apiClient: ReportingAPIClient; + license$: LicensingPluginSetup['license$']; + redirect: ApplicationStart['navigateToApp']; + toasts: ToastsSetup; } interface State { @@ -59,6 +62,9 @@ interface State { total: number; jobs: Job[]; isLoading: boolean; + showLinks: boolean; + enableLinks: boolean; + badLicenseMessage: string; } const jobStatusLabelsMap = new Map([ @@ -95,9 +101,10 @@ const jobStatusLabelsMap = new Map([ ]); class ReportListingUi extends Component { + private isInitialJobsFetch: boolean; + private licenseSubscription?: Subscription; private mounted?: boolean; private poller?: any; - private isInitialJobsFetch: boolean; constructor(props: Props) { super(props); @@ -107,6 +114,9 @@ class ReportListingUi extends Component { total: 0, jobs: [], isLoading: false, + showLinks: false, + enableLinks: false, + badLicenseMessage: '', }; this.isInitialJobsFetch = true; @@ -137,23 +147,41 @@ class ReportListingUi extends Component { public componentWillUnmount() { this.mounted = false; this.poller.stop(); + + if (this.licenseSubscription) { + this.licenseSubscription.unsubscribe(); + } } public componentDidMount() { this.mounted = true; - const { jobsRefresh } = chrome.getInjected('reportingPollConfig'); this.poller = new Poller({ functionToPoll: () => { return this.fetchJobs(); }, - pollFrequencyInMillis: jobsRefresh.interval, + pollFrequencyInMillis: + JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG.jobCompletionNotifier.interval, trailing: false, continuePollingOnError: true, - pollFrequencyErrorMultiplier: jobsRefresh.intervalErrorMultiplier, + pollFrequencyErrorMultiplier: + JOB_COMPLETION_NOTIFICATIONS_POLLER_CONFIG.jobCompletionNotifier.intervalErrorMultiplier, }); this.poller.start(); + this.licenseSubscription = this.props.license$.subscribe(this.licenseHandler); } + private licenseHandler = (license: ILicense) => { + const { enableLinks, showLinks, message: badLicenseMessage } = checkLicense( + license.check('reporting', 'basic') + ); + + this.setState({ + enableLinks, + showLinks, + badLicenseMessage, + }); + }; + private renderTable() { const { intl } = this.props; @@ -275,7 +303,6 @@ class ReportListingUi extends Component {
    {statusLabel} {maxSizeReached} - {warnings}
    ); }, @@ -340,7 +367,7 @@ class ReportListingUi extends Component { const { intl } = this.props; const button = ( downloadReport(record.id)} + onClick={() => this.props.apiClient.downloadReport(record.id)} iconType="importAction" aria-label={intl.formatMessage({ id: 'xpack.reporting.listing.table.downloadReportAriaLabel', @@ -386,11 +413,11 @@ class ReportListingUi extends Component { return; } - return ; + return ; }; private renderInfoButton = (record: Job) => { - return ; + return ; }; private onTableChange = ({ page }: { page: { index: number } }) => { @@ -407,19 +434,19 @@ class ReportListingUi extends Component { let jobs: JobQueueEntry[]; let total: number; try { - jobs = await jobQueueClient.list(this.state.page); - total = await jobQueueClient.total(); + jobs = await this.props.apiClient.list(this.state.page); + total = await this.props.apiClient.total(); this.isInitialJobsFetch = false; - } catch (kfetchError) { + } catch (fetchError) { if (!this.licenseAllowsToShowThisPage()) { - toastNotifications.addDanger(this.props.badLicenseMessage); - this.props.redirect('/management'); + this.props.toasts.addDanger(this.state.badLicenseMessage); + this.props.redirect('kibana#/management'); return; } - if (kfetchError.res.status !== 401 && kfetchError.res.status !== 403) { - toastNotifications.addDanger( - kfetchError.res.statusText || + if (fetchError.message === 'Failed to fetch') { + this.props.toasts.addDanger( + fetchError.message || this.props.intl.formatMessage({ id: 'xpack.reporting.listing.table.requestFailedErrorMessage', defaultMessage: 'Request failed', @@ -463,7 +490,7 @@ class ReportListingUi extends Component { }; private licenseAllowsToShowThisPage = () => { - return this.props.showLinks && this.props.enableLinks; + return this.state.showLinks && this.state.enableLinks; }; private formatDate(timestamp: string) { diff --git a/x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx similarity index 88% rename from x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx rename to x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index aaf4021302a97..cf107fd712876 100644 --- a/x-pack/legacy/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -7,12 +7,14 @@ import { EuiButton, EuiCopy, EuiForm, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import React, { Component, ReactElement } from 'react'; -import { toastNotifications } from 'ui/notify'; import url from 'url'; -import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; -import * as reportingClient from '../lib/reporting_client'; +import { ToastsSetup } from 'src/core/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; interface Props { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; reportType: string; layoutId: string | undefined; objectId?: string; @@ -31,23 +33,6 @@ interface State { } class ReportingPanelContentUi extends Component { - public static getDerivedStateFromProps(nextProps: Props, prevState: State) { - if (nextProps.layoutId !== prevState.layoutId) { - return { - ...prevState, - absoluteUrl: ReportingPanelContentUi.getAbsoluteReportGenerationUrl(nextProps), - }; - } - return prevState; - } - - private static getAbsoluteReportGenerationUrl = (props: Props) => { - const relativePath = reportingClient.getReportingJobPath( - props.reportType, - props.getJobParams() - ); - return url.resolve(window.location.href, relativePath); - }; private mounted?: boolean; constructor(props: Props) { @@ -55,11 +40,29 @@ class ReportingPanelContentUi extends Component { this.state = { isStale: false, - absoluteUrl: '', + absoluteUrl: this.getAbsoluteReportGenerationUrl(props), layoutId: '', }; } + private getAbsoluteReportGenerationUrl = (props: Props) => { + const relativePath = this.props.apiClient.getReportingJobPath( + props.reportType, + props.getJobParams() + ); + return url.resolve(window.location.href, relativePath); + }; + + public componentDidUpdate(prevProps: Props, prevState: State) { + if (this.props.layoutId && this.props.layoutId !== prevState.layoutId) { + this.setState({ + ...prevState, + absoluteUrl: this.getAbsoluteReportGenerationUrl(this.props), + layoutId: this.props.layoutId, + }); + } + } + public componentWillUnmount() { window.removeEventListener('hashchange', this.markAsStale); window.removeEventListener('resize', this.setAbsoluteReportGenerationUrl); @@ -188,17 +191,17 @@ class ReportingPanelContentUi extends Component { if (!this.mounted) { return; } - const absoluteUrl = ReportingPanelContentUi.getAbsoluteReportGenerationUrl(this.props); + const absoluteUrl = this.getAbsoluteReportGenerationUrl(this.props); this.setState({ absoluteUrl }); }; private createReportingJob = () => { const { intl } = this.props; - return reportingClient + return this.props.apiClient .createReportingJob(this.props.reportType, this.props.getJobParams()) .then(() => { - toastNotifications.addSuccess({ + this.props.toasts.addSuccess({ title: intl.formatMessage( { id: 'xpack.reporting.panelContent.successfullyQueuedReportNotificationTitle', @@ -218,7 +221,7 @@ class ReportingPanelContentUi extends Component { }) .catch((error: any) => { if (error.message === 'not exportable') { - return toastNotifications.addWarning({ + return this.props.toasts.addWarning({ title: intl.formatMessage( { id: 'xpack.reporting.panelContent.whatCanBeExportedWarningTitle', @@ -248,7 +251,7 @@ class ReportingPanelContentUi extends Component { /> ); - toastNotifications.addDanger({ + this.props.toasts.addDanger({ title: intl.formatMessage({ id: 'xpack.reporting.panelContent.notification.reportingErrorTitle', defaultMessage: 'Reporting error', diff --git a/x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx similarity index 91% rename from x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx rename to x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx index cf6bb94876361..9fb74a70ff1ac 100644 --- a/x-pack/legacy/plugins/reporting/public/components/screen_capture_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx @@ -7,9 +7,13 @@ import { EuiSpacer, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; +import { ToastsSetup } from 'src/core/public'; import { ReportingPanelContent } from './reporting_panel_content'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; interface Props { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; reportType: string; objectId?: string; objectType: string; @@ -38,6 +42,8 @@ export class ScreenCapturePanelContent extends Component { public render() { return ( + "path": => { - return http.fetch(`${API_BASE_URL}/list`, { - query: { page: 0, ids: jobIds.join(',') }, - method: 'GET', - }); - }; - - public getContent(http: HttpService, jobId: JobId): Promise { - return http - .fetch(`${API_BASE_URL}/output/${jobId}`, { - method: 'GET', - }) - .then((data: JobContent) => data.content); - } -} - -export const jobQueueClient = new JobQueue(); diff --git a/x-pack/plugins/reporting/public/lib/license_check.test.ts b/x-pack/plugins/reporting/public/lib/license_check.test.ts new file mode 100644 index 0000000000000..24e14969d2c81 --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/license_check.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { checkLicense } from './license_check'; +import { LicenseCheck } from '../../../licensing/public'; + +describe('License check', () => { + it('enables and shows links when licenses are good mkay', () => { + expect(checkLicense({ state: 'VALID' } as LicenseCheck)).toEqual({ + enableLinks: true, + showLinks: true, + message: '', + }); + }); + + it('disables and shows links when licenses are not valid', () => { + expect(checkLicense({ state: 'INVALID' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: false, + message: 'Your license does not support Reporting. Please upgrade your license.', + }); + }); + + it('shows links, but disables them, on expired licenses', () => { + expect(checkLicense({ state: 'EXPIRED' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: true, + message: 'You cannot use Reporting because your license has expired.', + }); + }); + + it('shows links, but disables them, when license checks are unavailable', () => { + expect(checkLicense({ state: 'UNAVAILABLE' } as LicenseCheck)).toEqual({ + enableLinks: false, + showLinks: true, + message: + 'You cannot use Reporting because license information is not available at this time.', + }); + }); + + it('shows and enables links if state is not known', () => { + expect(checkLicense({ state: 'PONYFOO' } as any)).toEqual({ + enableLinks: true, + showLinks: true, + message: '', + }); + }); +}); diff --git a/x-pack/plugins/reporting/public/lib/license_check.ts b/x-pack/plugins/reporting/public/lib/license_check.ts new file mode 100644 index 0000000000000..ca803fb38ef2a --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/license_check.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { LicenseCheckResults } from '../..'; +import { LICENSE_CHECK_STATE, LicenseCheck } from '../../../licensing/public'; + +export const checkLicense = (checkResults: LicenseCheck): LicenseCheckResults => { + switch (checkResults.state) { + case LICENSE_CHECK_STATE.Valid: { + return { + showLinks: true, + enableLinks: true, + message: '', + }; + } + + case LICENSE_CHECK_STATE.Invalid: { + return { + showLinks: false, + enableLinks: false, + message: 'Your license does not support Reporting. Please upgrade your license.', + }; + } + + case LICENSE_CHECK_STATE.Unavailable: { + return { + showLinks: true, + enableLinks: false, + message: + 'You cannot use Reporting because license information is not available at this time.', + }; + } + + case LICENSE_CHECK_STATE.Expired: { + return { + showLinks: true, + enableLinks: false, + message: 'You cannot use Reporting because your license has expired.', + }; + } + + default: { + return { + showLinks: true, + enableLinks: true, + message: '', + }; + } + } +}; diff --git a/x-pack/plugins/reporting/public/lib/reporting_api_client.ts b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts new file mode 100644 index 0000000000000..ddfeb144d3cd7 --- /dev/null +++ b/x-pack/plugins/reporting/public/lib/reporting_api_client.ts @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { stringify } from 'query-string'; +import rison from 'rison-node'; + +import { HttpSetup } from 'src/core/public'; +import { add } from './job_completion_notifications'; +import { + API_LIST_URL, + API_BASE_URL, + API_BASE_GENERATE, + REPORTING_MANAGEMENT_HOME, +} from '../../constants'; +import { JobId, SourceJob } from '../..'; + +export interface JobQueueEntry { + _id: string; + _source: any; +} + +export interface JobContent { + content: string; + content_type: boolean; +} + +export interface JobInfo { + kibana_name: string; + kibana_id: string; + browser_type: string; + created_at: string; + priority: number; + jobtype: string; + created_by: string; + timeout: number; + output: { + content_type: string; + size: number; + warnings: string[]; + }; + process_expiration: string; + completed_at: string; + payload: { + layout: { id: string; dimensions: { width: number; height: number } }; + objects: Array<{ relativeUrl: string }>; + type: string; + title: string; + forceNow: string; + browserTimezone: string; + }; + meta: { + layout: string; + objectType: string; + }; + max_attempts: number; + started_at: string; + attempts: number; + status: string; +} + +interface JobParams { + [paramName: string]: any; +} + +export class ReportingAPIClient { + private http: HttpSetup; + + constructor(http: HttpSetup) { + this.http = http; + } + + public getReportURL(jobId: string) { + const apiBaseUrl = this.http.basePath.prepend(API_LIST_URL); + const downloadLink = `${apiBaseUrl}/download/${jobId}`; + + return downloadLink; + } + + public downloadReport(jobId: string) { + const location = this.getReportURL(jobId); + + window.open(location); + } + + public list = (page = 0, jobIds: string[] = []): Promise => { + const query = { page } as any; + if (jobIds.length > 0) { + // Only getting the first 10, to prevent URL overflows + query.ids = jobIds.slice(0, 10).join(','); + } + + return this.http.get(`${API_LIST_URL}/list`, { + query, + asSystemRequest: true, + }); + }; + + public total(): Promise { + return this.http.get(`${API_LIST_URL}/count`, { + asSystemRequest: true, + }); + } + + public getContent(jobId: string): Promise { + return this.http.get(`${API_LIST_URL}/output/${jobId}`, { + asSystemRequest: true, + }); + } + + public getInfo(jobId: string): Promise { + return this.http.get(`${API_LIST_URL}/info/${jobId}`, { + asSystemRequest: true, + }); + } + + public findForJobIds = (jobIds: JobId[]): Promise => { + return this.http.fetch(`${API_LIST_URL}/list`, { + query: { page: 0, ids: jobIds.join(',') }, + method: 'GET', + }); + }; + + public getReportingJobPath = (exportType: string, jobParams: JobParams) => { + const params = stringify({ jobParams: rison.encode(jobParams) }); + + return `${this.http.basePath.prepend(API_BASE_URL)}/${exportType}?${params}`; + }; + + public createReportingJob = async (exportType: string, jobParams: any) => { + const jobParamsRison = rison.encode(jobParams); + const resp = await this.http.post(`${API_BASE_GENERATE}/${exportType}`, { + method: 'POST', + body: JSON.stringify({ + jobParams: jobParamsRison, + }), + }); + + add(resp.job.id); + + return resp; + }; + + public getManagementLink = () => this.http.basePath.prepend(REPORTING_MANAGEMENT_HOME); + + public getDownloadLink = (jobId: JobId) => + this.http.basePath.prepend(`${API_LIST_URL}/download/${jobId}`); + + public getBasePath = () => this.http.basePath.get(); +} diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts index aeba2ca5406b8..3a2c7de9ad0f0 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.test.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.test.ts @@ -5,9 +5,9 @@ */ import sinon, { stub } from 'sinon'; -import { HttpSetup, NotificationsStart } from '../../../../../src/core/public'; -import { SourceJob, JobSummary, HttpService } from '../../index.d'; -import { JobQueue } from './job_queue'; +import { NotificationsStart } from 'src/core/public'; +import { SourceJob, JobSummary } from '../../index.d'; +import { ReportingAPIClient } from './reporting_api_client'; import { ReportingNotifierStreamHandler } from './stream_handler'; Object.defineProperty(window, 'sessionStorage', { @@ -44,20 +44,16 @@ const mockJobsFound = [ }, ]; -const jobQueueClientMock: JobQueue = { - findForJobIds: async (http: HttpService, jobIds: string[]) => { +const jobQueueClientMock: ReportingAPIClient = { + findForJobIds: async (jobIds: string[]) => { return mockJobsFound as SourceJob[]; }, - getContent: () => { - return Promise.resolve('this is the completed report data'); + getContent: (): Promise => { + return Promise.resolve({ content: 'this is the completed report data' }); }, -}; - -const httpMock: HttpService = ({ - basePath: { - prepend: stub(), - }, -} as unknown) as HttpSetup; + getManagementLink: () => '/#management', + getDownloadLink: () => '/reporting/download/job-123', +} as any; const mockShowDanger = stub(); const mockShowSuccess = stub(); @@ -76,17 +72,13 @@ describe('stream handler', () => { }); it('constructs', () => { - const sh = new ReportingNotifierStreamHandler(httpMock, notificationsMock, jobQueueClientMock); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); expect(sh).not.toBe(null); }); describe('findChangedStatusJobs', () => { it('finds no changed status jobs from empty', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); const findJobs = sh.findChangedStatusJobs([]); findJobs.subscribe(data => { expect(data).toEqual({ completed: [], failed: [] }); @@ -95,11 +87,7 @@ describe('stream handler', () => { }); it('finds changed status jobs', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); const findJobs = sh.findChangedStatusJobs([ 'job-source-mock1', 'job-source-mock2', @@ -115,11 +103,7 @@ describe('stream handler', () => { describe('showNotifications', () => { it('show success', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -140,11 +124,7 @@ describe('stream handler', () => { }); it('show max length warning', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -166,11 +146,7 @@ describe('stream handler', () => { }); it('show csv formulas warning', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { @@ -192,11 +168,7 @@ describe('stream handler', () => { }); it('show failed job toast', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [], failed: [ @@ -217,11 +189,7 @@ describe('stream handler', () => { }); it('show multiple toast', done => { - const sh = new ReportingNotifierStreamHandler( - httpMock, - notificationsMock, - jobQueueClientMock - ); + const sh = new ReportingNotifierStreamHandler(notificationsMock, jobQueueClientMock); sh.showNotifications({ completed: [ { diff --git a/x-pack/plugins/reporting/public/lib/stream_handler.ts b/x-pack/plugins/reporting/public/lib/stream_handler.ts index e58e90d3de8ef..1aae30f6fdfb0 100644 --- a/x-pack/plugins/reporting/public/lib/stream_handler.ts +++ b/x-pack/plugins/reporting/public/lib/stream_handler.ts @@ -11,19 +11,16 @@ import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JOB_STATUS_COMPLETED, JOB_STATUS_FAILED, - API_BASE_URL, - REPORTING_MANAGEMENT_HOME, } from '../../constants'; + import { JobId, JobSummary, JobStatusBuckets, - HttpService, NotificationsService, SourceJob, - DownloadReportFn, - ManagementLinkFn, } from '../../index.d'; + import { getSuccessToast, getFailureToast, @@ -31,7 +28,7 @@ import { getWarningMaxSizeToast, getGeneralErrorToast, } from '../components'; -import { jobQueueClient as defaultJobQueueClient } from './job_queue'; +import { ReportingAPIClient } from './reporting_api_client'; function updateStored(jobIds: JobId[]): void { sessionStorage.setItem(JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY, JSON.stringify(jobIds)); @@ -49,21 +46,7 @@ function summarizeJob(src: SourceJob): JobSummary { } export class ReportingNotifierStreamHandler { - private getManagementLink: ManagementLinkFn; - private getDownloadLink: DownloadReportFn; - - constructor( - private http: HttpService, - private notifications: NotificationsService, - private jobQueueClient = defaultJobQueueClient - ) { - this.getManagementLink = () => { - return http.basePath.prepend(REPORTING_MANAGEMENT_HOME); - }; - this.getDownloadLink = (jobId: JobId) => { - return http.basePath.prepend(`${API_BASE_URL}/download/${jobId}`); - }; - } + constructor(private notifications: NotificationsService, private apiClient: ReportingAPIClient) {} /* * Use Kibana Toast API to show our messages @@ -77,23 +60,33 @@ export class ReportingNotifierStreamHandler { for (const job of completedJobs) { if (job.csvContainsFormulas) { this.notifications.toasts.addWarning( - getWarningFormulasToast(job, this.getManagementLink, this.getDownloadLink) + getWarningFormulasToast( + job, + this.apiClient.getManagementLink, + this.apiClient.getDownloadLink + ) ); } else if (job.maxSizeReached) { this.notifications.toasts.addWarning( - getWarningMaxSizeToast(job, this.getManagementLink, this.getDownloadLink) + getWarningMaxSizeToast( + job, + this.apiClient.getManagementLink, + this.apiClient.getDownloadLink + ) ); } else { this.notifications.toasts.addSuccess( - getSuccessToast(job, this.getManagementLink, this.getDownloadLink) + getSuccessToast(job, this.apiClient.getManagementLink, this.apiClient.getDownloadLink) ); } } // no download link available for (const job of failedJobs) { - const content = await this.jobQueueClient.getContent(this.http, job.id); - this.notifications.toasts.addDanger(getFailureToast(content, job, this.getManagementLink)); + const { content } = await this.apiClient.getContent(job.id); + this.notifications.toasts.addDanger( + getFailureToast(content, job, this.apiClient.getManagementLink) + ); } return { completed: completedJobs, failed: failedJobs }; }; @@ -106,7 +99,7 @@ export class ReportingNotifierStreamHandler { * session storage) but have non-processing job status on the server */ public findChangedStatusJobs(storedJobs: JobId[]): Rx.Observable { - return Rx.from(this.jobQueueClient.findForJobIds(this.http, storedJobs)).pipe( + return Rx.from(this.apiClient.findForJobIds(storedJobs)).pipe( map((jobs: SourceJob[]) => { const completedJobs: JobSummary[] = []; const failedJobs: JobSummary[] = []; diff --git a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx similarity index 72% rename from x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx rename to x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx index 4c9cd890ee75b..282ee75815fa5 100644 --- a/x-pack/legacy/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx +++ b/x-pack/plugins/reporting/public/panel_actions/get_csv_panel_action.tsx @@ -6,24 +6,21 @@ import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; - -import { npSetup, npStart } from 'ui/new_platform'; -import { - ActionByType, - IncompatibleActionError, -} from '../../../../../../src/plugins/ui_actions/public'; +import { CoreSetup } from 'src/core/public'; +import { Action, IncompatibleActionError } from '../../../../../src/plugins/ui_actions/public'; +import { LicensingPluginSetup } from '../../../licensing/public'; +import { checkLicense } from '../lib/license_check'; import { ViewMode, IEmbeddable, - CONTEXT_MENU_TRIGGER, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; -import { ISearchEmbeddable } from '../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/types'; +} from '../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../common/constants'; +// @TODO: These import paths will need to be updated once discovery moves to non-legacy dir +import { SEARCH_EMBEDDABLE_TYPE } from '../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/constants'; +import { ISearchEmbeddable } from '../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/embeddable/types'; -const { core } = npStart; +import { API_GENERATE_IMMEDIATE, CSV_REPORTING_ACTION } from '../../constants'; function isSavedSearchEmbeddable( embeddable: IEmbeddable | ISearchEmbeddable @@ -31,23 +28,26 @@ function isSavedSearchEmbeddable( return embeddable.type === SEARCH_EMBEDDABLE_TYPE; } -export interface CSVActionContext { +interface ActionContext { embeddable: ISearchEmbeddable; } -declare module '../../../../../../src/plugins/ui_actions/public' { - export interface ActionContextMapping { - [CSV_REPORTING_ACTION]: CSVActionContext; - } -} - -class GetCsvReportPanelAction implements ActionByType { +export class GetCsvReportPanelAction implements Action { private isDownloading: boolean; - public readonly type = CSV_REPORTING_ACTION; + public readonly type = ''; public readonly id = CSV_REPORTING_ACTION; + private canDownloadCSV: boolean = false; + private core: CoreSetup; - constructor() { + constructor(core: CoreSetup, license$: LicensingPluginSetup['license$']) { this.isDownloading = false; + this.core = core; + + license$.subscribe(license => { + const results = license.check('reporting', 'basic'); + const { showLinks } = checkLicense(results); + this.canDownloadCSV = showLinks; + }); } public getIconType() { @@ -73,13 +73,17 @@ class GetCsvReportPanelAction implements ActionByType { + public isCompatible = async (context: ActionContext) => { + if (!this.canDownloadCSV) { + return false; + } + const { embeddable } = context; return embeddable.getInput().viewMode !== ViewMode.EDIT && embeddable.type === 'search'; }; - public execute = async (context: CSVActionContext) => { + public execute = async (context: ActionContext) => { const { embeddable } = context; if (!isSavedSearchEmbeddable(embeddable)) { @@ -97,7 +101,7 @@ class GetCsvReportPanelAction implements ActionByType { this.isDownloading = false; @@ -160,7 +164,7 @@ class GetCsvReportPanelAction implements ActionByType { private readonly stop$ = new Rx.ReplaySubject(1); - // FIXME: License checking: only active, non-expired licenses allowed - // Depends on https://github.com/elastic/kibana/pull/44922 + private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', { + defaultMessage: 'Reporting', + }); + + private readonly breadcrumbText = i18n.translate('xpack.reporting.breadcrumb', { + defaultMessage: 'Reporting', + }); + constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup) {} + public setup( + core: CoreSetup, + { + home, + management, + licensing, + uiActions, + share, + }: { + home: HomePublicPluginSetup; + management: ManagementSetup; + licensing: LicensingPluginSetup; + uiActions: UiActionsSetup; + share: SharePluginSetup; + } + ) { + const { + http, + notifications: { toasts }, + getStartServices, + uiSettings, + } = core; + const { license$ } = licensing; + + const apiClient = new ReportingAPIClient(http); + const action = new GetCsvReportPanelAction(core, license$); + + home.featureCatalogue.register({ + id: 'reporting', + title: i18n.translate('xpack.reporting.registerFeature.reportingTitle', { + defaultMessage: 'Reporting', + }), + description: i18n.translate('xpack.reporting.registerFeature.reportingDescription', { + defaultMessage: 'Manage your reports generated from Discover, Visualize, and Dashboard.', + }), + icon: 'reportingApp', + path: '/app/kibana#/management/kibana/reporting', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }); + + management.sections.getSection('kibana')!.registerApp({ + id: 'reporting', + title: this.title, + order: 15, + mount: async params => { + const [start] = await getStartServices(); + params.setBreadcrumbs([{ text: this.breadcrumbText }]); + ReactDOM.render( + + + , + params.element + ); + + return () => { + ReactDOM.unmountComponentAtNode(params.element); + }; + }, + }); + + uiActions.registerAction(action); + uiActions.attachAction(CONTEXT_MENU_TRIGGER, action); + + share.register(csvReportingProvider({ apiClient, toasts, license$ })); + share.register( + reportingPDFPNGProvider({ + apiClient, + toasts, + license$, + uiSettings, + }) + ); + } // FIXME: only perform these actions for authenticated routes // Depends on https://github.com/elastic/kibana/pull/39477 public start(core: CoreStart) { const { http, notifications } = core; - const streamHandler = new StreamHandler(http, notifications); + const apiClient = new ReportingAPIClient(http); + const streamHandler = new StreamHandler(notifications, apiClient); Rx.timer(0, JOBS_REFRESH_INTERVAL) .pipe( diff --git a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx similarity index 61% rename from x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx rename to x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 3c9d1d7262587..9d4f475cde79a 100644 --- a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -5,14 +5,34 @@ */ import { i18n } from '@kbn/i18n'; -// @ts-ignore: implicit any for JS file -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; import React from 'react'; -import { npSetup } from 'ui/new_platform'; + +import { ToastsSetup } from 'src/core/public'; import { ReportingPanelContent } from '../components/reporting_panel_content'; -import { ShareContext } from '../../../../../../src/plugins/share/public'; +import { ReportingAPIClient } from '../lib/reporting_api_client'; +import { checkLicense } from '../lib/license_check'; +import { LicensingPluginSetup } from '../../../licensing/public'; +import { ShareContext } from '../../../../../src/plugins/share/public'; + +interface ReportingProvider { + apiClient: ReportingAPIClient; + toasts: ToastsSetup; + license$: LicensingPluginSetup['license$']; +} + +export const csvReportingProvider = ({ apiClient, toasts, license$ }: ReportingProvider) => { + let toolTipContent = ''; + let disabled = true; + let hasCSVReporting = false; + + license$.subscribe(license => { + const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'basic')); + + toolTipContent = message; + hasCSVReporting = showLinks; + disabled = !enableLinks; + }); -function reportingProvider() { const getShareMenuItems = ({ objectType, objectId, @@ -32,7 +52,8 @@ function reportingProvider() { }; const shareActions = []; - if (xpackInfo.get('features.reporting.csv.showLinks', false)) { + + if (hasCSVReporting) { const panelTitle = i18n.translate('xpack.reporting.shareContextMenu.csvReportsButtonLabel', { defaultMessage: 'CSV Reports', }); @@ -41,8 +62,8 @@ function reportingProvider() { shareMenuItem: { name: panelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.csv.message'), - disabled: !xpackInfo.get('features.reporting.csv.enableLinks', false) ? true : false, + toolTipContent, + disabled, ['data-test-subj']: 'csvReportMenuItem', sortOrder: 1, }, @@ -51,6 +72,8 @@ function reportingProvider() { title: panelTitle, content: ( { + let toolTipContent = ''; + let disabled = true; + let hasPDFPNGReporting = false; -const { core } = npSetup; + license$.subscribe(license => { + const { enableLinks, showLinks, message } = checkLicense(license.check('reporting', 'gold')); + + toolTipContent = message; + hasPDFPNGReporting = showLinks; + disabled = !enableLinks; + }); -async function reportingProvider() { const getShareMenuItems = ({ objectType, objectId, @@ -29,24 +52,22 @@ async function reportingProvider() { } // Dashboard only mode does not currently support reporting // https://github.com/elastic/kibana/issues/18286 - if ( - objectType === 'dashboard' && - npStart.plugins.kibanaLegacy.dashboardConfig.getHideWriteControls() - ) { + // @TODO For NP + if (objectType === 'dashboard' && false) { return []; } const getReportingJobParams = () => { // Replace hashes with original RISON values. const relativeUrl = shareableUrl.replace( - window.location.origin + core.http.basePath.get(), + window.location.origin + apiClient.getBasePath(), '' ); const browserTimezone = - core.uiSettings.get('dateFormat:tz') === 'Browser' + uiSettings.get('dateFormat:tz') === 'Browser' ? moment.tz.guess() - : core.uiSettings.get('dateFormat:tz'); + : uiSettings.get('dateFormat:tz'); return { ...sharingData, @@ -59,14 +80,14 @@ async function reportingProvider() { const getPngJobParams = () => { // Replace hashes with original RISON values. const relativeUrl = shareableUrl.replace( - window.location.origin + core.http.basePath.get(), + window.location.origin + apiClient.getBasePath(), '' ); const browserTimezone = - core.uiSettings.get('dateFormat:tz') === 'Browser' + uiSettings.get('dateFormat:tz') === 'Browser' ? moment.tz.guess() - : core.uiSettings.get('dateFormat:tz'); + : uiSettings.get('dateFormat:tz'); return { ...sharingData, @@ -77,60 +98,69 @@ async function reportingProvider() { }; const shareActions = []; - if (xpackInfo.get('features.reporting.printablePdf.showLinks', false)) { - const panelTitle = i18n.translate('xpack.reporting.shareContextMenu.pdfReportsButtonLabel', { - defaultMessage: 'PDF Reports', - }); + + if (hasPDFPNGReporting) { + const pngPanelTitle = i18n.translate( + 'xpack.reporting.shareContextMenu.pngReportsButtonLabel', + { + defaultMessage: 'PNG Reports', + } + ); + + const pdfPanelTitle = i18n.translate( + 'xpack.reporting.shareContextMenu.pdfReportsButtonLabel', + { + defaultMessage: 'PDF Reports', + } + ); shareActions.push({ shareMenuItem: { - name: panelTitle, + name: pngPanelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.printablePdf.message'), - disabled: !xpackInfo.get('features.reporting.printablePdf.enableLinks', false) - ? true - : false, - ['data-test-subj']: 'pdfReportMenuItem', + toolTipContent, + disabled, + ['data-test-subj']: 'pngReportMenuItem', sortOrder: 10, }, panel: { - id: 'reportingPdfPanel', - title: panelTitle, + id: 'reportingPngPanel', + title: pngPanelTitle, content: ( ), }, }); - } - - if (xpackInfo.get('features.reporting.png.showLinks', false)) { - const panelTitle = 'PNG Reports'; shareActions.push({ shareMenuItem: { - name: panelTitle, + name: pdfPanelTitle, icon: 'document', - toolTipContent: xpackInfo.get('features.reporting.png.message'), - disabled: !xpackInfo.get('features.reporting.png.enableLinks', false) ? true : false, - ['data-test-subj']: 'pngReportMenuItem', + toolTipContent, + disabled, + ['data-test-subj']: 'pdfReportMenuItem', sortOrder: 10, }, panel: { - id: 'reportingPngPanel', - title: panelTitle, + id: 'reportingPdfPanel', + title: pdfPanelTitle, content: ( @@ -146,8 +176,4 @@ async function reportingProvider() { id: 'screenCaptureReports', getShareMenuItems, }; -} - -(async () => { - npSetup.plugins.share.register(await reportingProvider()); -})(); +}; From 89f9260da2eb0d54404a93380ea4eef6c4b16597 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 17 Mar 2020 10:41:23 -0700 Subject: [PATCH 07/35] FTR configurable test users (#52431) * initial implementation of configurable test users * user superuser by default to match master * referenced the configs in reporting and api integration * setting the minimum number of default roles * looking for x-pack tests with users and roles * add testUserService in dashboard mode tests * running only ciGroup7 * uncommenting - addign visualization * re-enabling all CI groups to run on CI * reinstating Jenkinsfile * disable Test user for OIDC config * improved logging and added Roles for OSS tests to get better info on the runs. * disable test_user for auth tests * don't fetch enabledPlugins when testuser disabled * fix es-lint * running oss tests with x-pack enabled * [revertme] build default dist for oss tests * updating NOTICE.txt file as it complained in the kibana intake tests * changed to pick OSS builds * trying a license change to trial * switch back to xpack builds * created a new sample data role and used it in homepage tests * revert test/scripts/jenkins_ci_group.sh * only refresh browser and wait for chrome if we are already on Kibana page * fix large_string test to use minimum set of roles and privileges * fix for date nanos custom timestamp with a configured role * changes to the files with addition of new roles for the test_user * reverting to OSS changes and few additions to the time_zone test to run as a test_user * changes to security * changes to the x-pack test to use elastic superuser * fix for chart_types test * fixes to area chart , input control test * fix for dashboard filtering test and a new config role * changes to handle the x-pack tests * additional role for date nanos mixed * added the logstash role to the accessibility tests * removed telemetry setting * docs+few changes to the tests * removed Page navigation * removed pageNavigation which was unused * test/accessibility/apps/management.ts * update management.ts * aria label, and other changes * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * accidentally checked in a piped file with results. * reverted * unloading of logstash data, fixing aria label * aria-label * added the required role * fix for tsvb chart * fix for sample data test reverted home_page pageobject file * changes to sample data test and visualize index file to incorporate OSS changes * changes to describe() and some more changes to incorporate in settings_page * re-adding the after() * removed unwanted roles * replaced kibana_user with kibana_admin * added the check of deprecated kibana_user * testing with kibana_admin role * fix for discover test * incorporated the review comments * incorporated the review comments * incorporate review comments and added restoreDefaults() * removed describe.only * reverted the OSS logic change I had here- pulled into seperate PR * incorporated the review comments * incorporated review changes * adding hidden=true to find hidden kibanaChrome * change field.test.tsx to be same as that of master branch Co-authored-by: spalger Co-authored-by: Elastic Machine --- .../development-functional-tests.asciidoc | 19 ++ .../lib/config/schema.ts | 15 ++ test/common/services/security/role.ts | 2 - test/common/services/security/security.ts | 12 +- test/common/services/security/test_user.ts | 92 ++++++++++ test/functional/apps/context/_date_nanos.js | 7 +- .../context/_date_nanos_custom_timestamp.js | 11 +- .../apps/dashboard/dashboard_filtering.js | 6 + test/functional/apps/dashboard/index.js | 1 + test/functional/apps/dashboard/time_zones.js | 2 - test/functional/apps/discover/_date_nanos.js | 7 +- .../apps/discover/_date_nanos_mixed.js | 7 +- .../apps/discover/_discover_histogram.js | 7 + .../functional/apps/discover/_large_string.js | 3 + .../apps/getting_started/_shakespeare.js | 6 + test/functional/apps/home/_sample_data.ts | 6 + .../apps/management/_handle_alias.js | 3 + .../apps/management/_test_huge_fields.js | 3 + test/functional/apps/visualize/_area_chart.js | 11 +- .../apps/visualize/_experimental_vis.js | 2 +- .../apps/visualize/_linked_saved_searches.ts | 2 +- .../apps/visualize/_markdown_vis.js | 2 +- test/functional/apps/visualize/_tsvb_chart.ts | 4 + test/functional/apps/visualize/_vega_chart.js | 2 +- .../input_control_vis/input_control_range.ts | 3 + test/functional/config.js | 167 ++++++++++++++++++ test/functional/page_objects/common_page.ts | 17 +- test/functional/services/browser.ts | 2 + test/functional/services/test_subjects.ts | 2 + x-pack/test/api_integration/config.js | 1 + .../dashboard_mode/dashboard_view_mode.js | 21 ++- x-pack/test/functional/config.js | 19 ++ x-pack/test/oidc_api_integration/config.ts | 1 + x-pack/test/pki_api_integration/config.ts | 1 + .../reporting/configs/chromium_functional.js | 1 + x-pack/test/saml_api_integration/config.ts | 1 + x-pack/test/token_api_integration/config.js | 1 + 37 files changed, 433 insertions(+), 36 deletions(-) create mode 100644 test/common/services/security/test_user.ts diff --git a/docs/developer/core/development-functional-tests.asciidoc b/docs/developer/core/development-functional-tests.asciidoc index dcb3d65b8b83f..51b5273851ce7 100644 --- a/docs/developer/core/development-functional-tests.asciidoc +++ b/docs/developer/core/development-functional-tests.asciidoc @@ -178,6 +178,25 @@ To run tests on Firefox locally, use `config.firefox.js`: node scripts/functional_test_runner --config test/functional/config.firefox.js ----------- +[float] +===== Using the test_user service + +Tests should run at the positive security boundry condition, meaning that they should be run with the mimimum privileges required (and documented) and not as the superuser. + This prevents the type of regression where additional privleges accidentally become required to perform the same action. + +The functional UI tests now default to logging in with a user named `test_user` and the roles of this user can be changed dynamically without logging in and out. + +In order to achieve this a new service was introduced called `createTestUserService` (see `test/common/services/security/test_user.ts`). The purpose of this test user service is to create roles defined in the test config files and setRoles() or restoreDefaults(). + +An example of how to set the role like how its defined below: + +`await security.testUser.setRoles(['kibana_user', 'kibana_date_nanos']);` + +Here we are setting the `test_user` to have the `kibana_user` role and also role access to a specific data index (`kibana_date_nanos`). + +Tests should normally setRoles() in the before() and restoreDefaults() in the after(). + + [float] ===== Anatomy of a test file diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 28e8396d0beba..66f17ab579ec3 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -255,5 +255,20 @@ export const schema = Joi.object() fixedHeaderHeight: Joi.number().default(50), }) .default(), + + // settings for the security service if there is no defaultRole defined, then default to superuser role. + security: Joi.object() + .keys({ + roles: Joi.object().default(), + defaultRoles: Joi.array() + .items(Joi.string()) + .when('$primary', { + is: true, + then: Joi.array().min(1), + }) + .default(['superuser']), + disableTestUser: Joi.boolean(), + }) + .default(), }) .default(); diff --git a/test/common/services/security/role.ts b/test/common/services/security/role.ts index 0e7572882f80d..dfc6ff9b164e5 100644 --- a/test/common/services/security/role.ts +++ b/test/common/services/security/role.ts @@ -43,7 +43,6 @@ export class Role { `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(data)}` ); } - this.log.debug(`created role ${name}`); } public async delete(name: string) { @@ -56,6 +55,5 @@ export class Role { )}` ); } - this.log.debug(`deleted role ${name}`); } } diff --git a/test/common/services/security/security.ts b/test/common/services/security/security.ts index 4eebb7b6697e0..6ad0933a2a5a2 100644 --- a/test/common/services/security/security.ts +++ b/test/common/services/security/security.ts @@ -23,15 +23,21 @@ import { Role } from './role'; import { User } from './user'; import { RoleMappings } from './role_mappings'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { createTestUserService } from './test_user'; -export function SecurityServiceProvider({ getService }: FtrProviderContext) { +export async function SecurityServiceProvider(context: FtrProviderContext) { + const { getService } = context; const log = getService('log'); const config = getService('config'); const url = formatUrl(config.get('servers.kibana')); + const role = new Role(url, log); + const user = new User(url, log); + const testUser = await createTestUserService(role, user, context); return new (class SecurityService { - role = new Role(url, log); roleMappings = new RoleMappings(url, log); - user = new User(url, log); + testUser = testUser; + role = role; + user = user; })(); } diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts new file mode 100644 index 0000000000000..7f01c64d291a5 --- /dev/null +++ b/test/common/services/security/test_user.ts @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { Role } from './role'; +import { User } from './user'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { Browser } from '../../../functional/services/browser'; +import { TestSubjects } from '../../../functional/services/test_subjects'; + +export async function createTestUserService( + role: Role, + user: User, + { getService, hasService }: FtrProviderContext +) { + const log = getService('log'); + const config = getService('config'); + // @ts-ignore browser service is not normally available in common. + const browser: Browser | void = hasService('browser') && getService('browser'); + const testSubjects: TestSubjects | void = + // @ts-ignore testSubject service is not normally available in common. + hasService('testSubjects') && getService('testSubjects'); + const kibanaServer = getService('kibanaServer'); + + const enabledPlugins = config.get('security.disableTestUser') + ? [] + : await kibanaServer.plugins.getEnabledIds(); + const isEnabled = () => { + return enabledPlugins.includes('security') && !config.get('security.disableTestUser'); + }; + if (isEnabled()) { + log.debug('===============creating roles and users==============='); + for (const [name, definition] of Object.entries(config.get('security.roles'))) { + // create the defined roles (need to map array to create roles) + await role.create(name, definition); + } + try { + // delete the test_user if present (will it error if the user doesn't exist?) + await user.delete('test_user'); + } catch (exception) { + log.debug('no test user to delete'); + } + + // create test_user with username and pwd + log.debug(`default roles = ${config.get('security.defaultRoles')}`); + await user.create('test_user', { + password: 'changeme', + roles: config.get('security.defaultRoles'), + full_name: 'test user', + }); + } + + return new (class TestUser { + async restoreDefaults() { + if (isEnabled()) { + await this.setRoles(config.get('security.defaultRoles')); + } + } + + async setRoles(roles: string[]) { + if (isEnabled()) { + log.debug(`set roles = ${roles}`); + await user.create('test_user', { + password: 'changeme', + roles, + full_name: 'test user', + }); + + if (browser && testSubjects) { + if (await testSubjects.exists('kibanaChrome', { allowHidden: true })) { + await browser.refresh(); + await testSubjects.find('kibanaChrome', config.get('timeouts.find') * 10); + } + } + } + } + })(); +} diff --git a/test/functional/apps/context/_date_nanos.js b/test/functional/apps/context/_date_nanos.js index d4acdb0b4d5c0..bd132e3745caa 100644 --- a/test/functional/apps/context/_date_nanos.js +++ b/test/functional/apps/context/_date_nanos.js @@ -26,11 +26,13 @@ const TEST_STEP_SIZE = 3; export default function({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const docTable = getService('docTable'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'context', 'timePicker', 'discover']); const esArchiver = getService('esArchiver'); describe('context view for date_nanos', () => { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos']); await esArchiver.loadIfNeeded('date_nanos'); await kibanaServer.uiSettings.replace({ defaultIndex: TEST_INDEX_PATTERN }); await kibanaServer.uiSettings.update({ @@ -39,8 +41,9 @@ export default function({ getService, getPageObjects }) { }); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos'); + after(async function unloadMakelogs() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos'); }); it('displays predessors - anchor - successors in right order ', async function() { diff --git a/test/functional/apps/context/_date_nanos_custom_timestamp.js b/test/functional/apps/context/_date_nanos_custom_timestamp.js index 046cca0aba8c6..7834b29931a65 100644 --- a/test/functional/apps/context/_date_nanos_custom_timestamp.js +++ b/test/functional/apps/context/_date_nanos_custom_timestamp.js @@ -26,12 +26,14 @@ const TEST_STEP_SIZE = 3; export default function({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const docTable = getService('docTable'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'context', 'timePicker', 'discover']); const esArchiver = getService('esArchiver'); // skipped due to a recent change in ES that caused search_after queries with data containing // custom timestamp formats like in the testdata to fail describe.skip('context view for date_nanos with custom timestamp', () => { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_custom']); await esArchiver.loadIfNeeded('date_nanos_custom'); await kibanaServer.uiSettings.replace({ defaultIndex: TEST_INDEX_PATTERN }); await kibanaServer.uiSettings.update({ @@ -40,10 +42,6 @@ export default function({ getService, getPageObjects }) { }); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos_custom'); - }); - it('displays predessors - anchor - successors in right order ', async function() { await PageObjects.context.navigateTo(TEST_INDEX_PATTERN, '1'); const actualRowsText = await docTable.getRowsText(); @@ -54,5 +52,10 @@ export default function({ getService, getPageObjects }) { ]; expect(actualRowsText).to.eql(expectedRowsText); }); + + after(async function() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos_custom'); + }); }); } diff --git a/test/functional/apps/dashboard/dashboard_filtering.js b/test/functional/apps/dashboard/dashboard_filtering.js index ec8a48ca74911..f388993dcaf7d 100644 --- a/test/functional/apps/dashboard/dashboard_filtering.js +++ b/test/functional/apps/dashboard/dashboard_filtering.js @@ -33,6 +33,7 @@ export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const dashboardPanelActions = getService('dashboardPanelActions'); const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'visualize', 'timePicker']); @@ -41,6 +42,7 @@ export default function({ getService, getPageObjects }) { before(async () => { await esArchiver.load('dashboard/current/kibana'); + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader', 'animals']); await kibanaServer.uiSettings.replace({ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', }); @@ -49,6 +51,10 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.gotoDashboardLandingPage(); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + describe('adding a filter that excludes all data', () => { before(async () => { await PageObjects.dashboard.clickNewDashboard(); diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js index 13e8631445393..5e96a55b19014 100644 --- a/test/functional/apps/dashboard/index.js +++ b/test/functional/apps/dashboard/index.js @@ -23,6 +23,7 @@ export default function({ getService, loadTestFile }) { async function loadCurrentData() { await browser.setWindowSize(1300, 900); + await esArchiver.unload('logstash_functional'); await esArchiver.loadIfNeeded('dashboard/current/data'); } diff --git a/test/functional/apps/dashboard/time_zones.js b/test/functional/apps/dashboard/time_zones.js index f374d6526fcf1..b7698a7d6ac4b 100644 --- a/test/functional/apps/dashboard/time_zones.js +++ b/test/functional/apps/dashboard/time_zones.js @@ -22,7 +22,6 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const pieChart = getService('pieChart'); - const browser = getService('browser'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['dashboard', 'timePicker', 'settings', 'common']); @@ -48,7 +47,6 @@ export default function({ getService, getPageObjects }) { after(async () => { await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC' }); - await browser.refresh(); }); it('Exported dashboard adjusts EST time to UTC', async () => { diff --git a/test/functional/apps/discover/_date_nanos.js b/test/functional/apps/discover/_date_nanos.js index 9b06b9ac84cfd..99a37cc18feaa 100644 --- a/test/functional/apps/discover/_date_nanos.js +++ b/test/functional/apps/discover/_date_nanos.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const fromTime = 'Sep 22, 2019 @ 20:31:44.000'; const toTime = 'Sep 23, 2019 @ 03:31:44.000'; @@ -30,12 +31,14 @@ export default function({ getService, getPageObjects }) { before(async function() { await esArchiver.loadIfNeeded('date_nanos'); await kibanaServer.uiSettings.replace({ defaultIndex: 'date-nanos' }); + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos']); await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos'); + after(async function unloadMakelogs() { + await security.testUser.restoreDefaults(); + await esArchiver.unload('date_nanos'); }); it('should show a timestamp with nanoseconds in the first result row', async function() { diff --git a/test/functional/apps/discover/_date_nanos_mixed.js b/test/functional/apps/discover/_date_nanos_mixed.js index 0bb6848db4d10..b88ae87601cc5 100644 --- a/test/functional/apps/discover/_date_nanos_mixed.js +++ b/test/functional/apps/discover/_date_nanos_mixed.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const fromTime = 'Jan 1, 2019 @ 00:00:00.000'; const toTime = 'Jan 1, 2019 @ 23:59:59.999'; @@ -30,12 +31,14 @@ export default function({ getService, getPageObjects }) { before(async function() { await esArchiver.loadIfNeeded('date_nanos_mixed'); await kibanaServer.uiSettings.replace({ defaultIndex: 'timestamp-*' }); + await security.testUser.setRoles(['kibana_admin', 'kibana_date_nanos_mixed']); await PageObjects.common.navigateToApp('discover'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); }); - after(function unloadMakelogs() { - return esArchiver.unload('date_nanos_mixed'); + after(async () => { + await security.testUser.restoreDefaults(); + esArchiver.unload('date_nanos_mixed'); }); it('shows a list of records of indices with date & date_nanos fields in the right order', async function() { diff --git a/test/functional/apps/discover/_discover_histogram.js b/test/functional/apps/discover/_discover_histogram.js index 9310838666256..f815c505a8c27 100644 --- a/test/functional/apps/discover/_discover_histogram.js +++ b/test/functional/apps/discover/_discover_histogram.js @@ -25,6 +25,7 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const elasticChart = getService('elasticChart'); const kibanaServer = getService('kibanaServer'); + const security = getService('security'); const PageObjects = getPageObjects(['settings', 'common', 'discover', 'header', 'timePicker']); const defaultSettings = { defaultIndex: 'long-window-logstash-*', @@ -35,6 +36,11 @@ export default function({ getService, getPageObjects }) { before(async function() { log.debug('load kibana index with default index pattern'); await PageObjects.common.navigateToApp('home'); + await security.testUser.setRoles([ + 'kibana_admin', + 'test_logstash_reader', + 'long_window_logstash', + ]); await esArchiver.loadIfNeeded('logstash_functional'); await esArchiver.load('long_window_logstash'); await esArchiver.load('visualize'); @@ -56,6 +62,7 @@ export default function({ getService, getPageObjects }) { await esArchiver.unload('long_window_logstash'); await esArchiver.unload('visualize'); await esArchiver.unload('discover'); + await security.testUser.restoreDefaults(); }); it('should visualize monthly data with different day intervals', async () => { diff --git a/test/functional/apps/discover/_large_string.js b/test/functional/apps/discover/_large_string.js index a5052b2403074..5e9048e2bc481 100644 --- a/test/functional/apps/discover/_large_string.js +++ b/test/functional/apps/discover/_large_string.js @@ -25,10 +25,12 @@ export default function({ getService, getPageObjects }) { const retry = getService('retry'); const kibanaServer = getService('kibanaServer'); const queryBar = getService('queryBar'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover']); describe('test large strings', function() { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'kibana_large_strings']); await esArchiver.load('empty_kibana'); await esArchiver.loadIfNeeded('hamlet'); await kibanaServer.uiSettings.replace({ defaultIndex: 'testlargestring' }); @@ -77,6 +79,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('hamlet'); }); }); diff --git a/test/functional/apps/getting_started/_shakespeare.js b/test/functional/apps/getting_started/_shakespeare.js index 5af1676cf423f..ded4eca908410 100644 --- a/test/functional/apps/getting_started/_shakespeare.js +++ b/test/functional/apps/getting_started/_shakespeare.js @@ -23,6 +23,7 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const esArchiver = getService('esArchiver'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects([ 'console', 'common', @@ -46,11 +47,16 @@ export default function({ getService, getPageObjects }) { 'Load empty_kibana and Shakespeare Getting Started data\n' + 'https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html' ); + await security.testUser.setRoles(['kibana_admin', 'test_shakespeare_reader']); await esArchiver.load('empty_kibana', { skipExisting: true }); log.debug('Load shakespeare data'); await esArchiver.loadIfNeeded('getting_started/shakespeare'); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should create shakespeare index pattern', async function() { log.debug('Create shakespeare index pattern'); await PageObjects.settings.createIndexPattern('shakes', null); diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index 8bc528e045566..5812b9b96e42a 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -25,6 +25,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const find = getService('find'); const log = getService('log'); + const security = getService('security'); const pieChart = getService('pieChart'); const renderable = getService('renderable'); const dashboardExpect = getService('dashboardExpect'); @@ -34,10 +35,15 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { this.tags('smoke'); before(async () => { + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); await PageObjects.common.navigateToUrl('home', 'tutorial_directory/sampleData'); await PageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should display registered flights sample data sets', async () => { await retry.try(async () => { const exists = await PageObjects.home.doesSampleDataSetExist('flights'); diff --git a/test/functional/apps/management/_handle_alias.js b/test/functional/apps/management/_handle_alias.js index 55f6b56d9f0d1..4ef02f6c9e873 100644 --- a/test/functional/apps/management/_handle_alias.js +++ b/test/functional/apps/management/_handle_alias.js @@ -23,11 +23,13 @@ export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const es = getService('legacyEs'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'timePicker']); // FLAKY: https://github.com/elastic/kibana/issues/59717 describe.skip('Index patterns on aliases', function() { before(async function() { + await security.testUser.setRoles(['kibana_admin', 'test_alias_reader']); await esArchiver.loadIfNeeded('alias'); await esArchiver.load('empty_kibana'); await es.indices.updateAliases({ @@ -84,6 +86,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('alias'); }); }); diff --git a/test/functional/apps/management/_test_huge_fields.js b/test/functional/apps/management/_test_huge_fields.js index 643cbcbe89482..bc280e51ae048 100644 --- a/test/functional/apps/management/_test_huge_fields.js +++ b/test/functional/apps/management/_test_huge_fields.js @@ -21,6 +21,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); + const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings']); describe('test large number of fields', function() { @@ -28,6 +29,7 @@ export default function({ getService, getPageObjects }) { const EXPECTED_FIELD_COUNT = '10006'; before(async function() { + await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader']); await esArchiver.loadIfNeeded('large_fields'); await PageObjects.settings.createIndexPattern('testhuge', 'date'); }); @@ -38,6 +40,7 @@ export default function({ getService, getPageObjects }) { }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('large_fields'); }); }); diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index 101b2d4f547dd..bf836cfe778b4 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -24,6 +24,7 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const browser = getService('browser'); const retry = getService('retry'); + const security = getService('security'); const PageObjects = getPageObjects([ 'common', 'visualize', @@ -58,7 +59,14 @@ export default function({ getService, getPageObjects }) { return PageObjects.visEditor.clickGo(); }; - before(initAreaChart); + before(async function() { + await security.testUser.setRoles([ + 'kibana_admin', + 'long_window_logstash', + 'test_logstash_reader', + ]); + await initAreaChart(); + }); it('should save and load with special characters', async function() { const vizNamewithSpecialChars = vizName1 + '/?&=%'; @@ -284,6 +292,7 @@ export default function({ getService, getPageObjects }) { .pop() .replace('embed=true', ''); await PageObjects.common.navigateToUrl('visualize', embedUrl); + await security.testUser.restoreDefaults(); }); }); diff --git a/test/functional/apps/visualize/_experimental_vis.js b/test/functional/apps/visualize/_experimental_vis.js index 2ce15cf913eff..c45a95abab86e 100644 --- a/test/functional/apps/visualize/_experimental_vis.js +++ b/test/functional/apps/visualize/_experimental_vis.js @@ -23,7 +23,7 @@ export default ({ getService, getPageObjects }) => { const log = getService('log'); const PageObjects = getPageObjects(['visualize']); - describe('visualize app', function() { + describe('experimental visualizations in visualize app ', function() { this.tags('smoke'); describe('experimental visualizations', () => { diff --git a/test/functional/apps/visualize/_linked_saved_searches.ts b/test/functional/apps/visualize/_linked_saved_searches.ts index 345987a803394..ea42f7c671985 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.ts +++ b/test/functional/apps/visualize/_linked_saved_searches.ts @@ -32,7 +32,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { 'visChart', ]); - describe('visualize app', function describeIndexTests() { + describe('saved search visualizations from visualize app', function describeIndexTests() { describe('linked saved searched', () => { const savedSearchName = 'vis_saved_search'; diff --git a/test/functional/apps/visualize/_markdown_vis.js b/test/functional/apps/visualize/_markdown_vis.js index fee6c074af5d2..649fe0a8e4c2e 100644 --- a/test/functional/apps/visualize/_markdown_vis.js +++ b/test/functional/apps/visualize/_markdown_vis.js @@ -29,7 +29,7 @@ export default function({ getPageObjects, getService }) {

    Inline HTML that should not be rendered as html

    `; - describe('visualize app', () => { + describe('markdown app in visualize app', () => { before(async function() { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickMarkdownWidget(); diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index 6a4bed3ba5892..867db66ac81dc 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -25,11 +25,13 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const log = getService('log'); const inspector = getService('inspector'); + const security = getService('security'); const PageObjects = getPageObjects(['visualize', 'visualBuilder', 'timePicker', 'visChart']); describe('visual builder', function describeIndexTests() { this.tags('smoke'); beforeEach(async () => { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisualBuilder(); await PageObjects.visualBuilder.checkVisualBuilderIsPresent(); @@ -111,8 +113,10 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.resetPage(); await PageObjects.visualBuilder.clickMetric(); await PageObjects.visualBuilder.checkMetricTabIsPresent(); + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); }); after(async () => { + await security.testUser.restoreDefaults(); await esArchiver.unload('kibana_sample_data_flights'); }); diff --git a/test/functional/apps/visualize/_vega_chart.js b/test/functional/apps/visualize/_vega_chart.js index df0603c7f95f5..7a19bde341cdd 100644 --- a/test/functional/apps/visualize/_vega_chart.js +++ b/test/functional/apps/visualize/_vega_chart.js @@ -25,7 +25,7 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const log = getService('log'); - describe('visualize app', () => { + describe('vega chart in visualize app', () => { before(async () => { log.debug('navigateToApp visualize'); await PageObjects.visualize.navigateToNewVisualization(); diff --git a/test/functional/apps/visualize/input_control_vis/input_control_range.ts b/test/functional/apps/visualize/input_control_vis/input_control_range.ts index f48ba7b54daf1..8f079f5cc430d 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_range.ts +++ b/test/functional/apps/visualize/input_control_vis/input_control_range.ts @@ -25,10 +25,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const find = getService('find'); + const security = getService('security'); const { visualize, visEditor } = getPageObjects(['visualize', 'visEditor']); describe('input control range', () => { before(async () => { + await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); await esArchiver.load('kibana_sample_data_flights_index_pattern'); await visualize.navigateToNewVisualization(); await visualize.clickInputControlVis(); @@ -63,6 +65,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await esArchiver.loadIfNeeded('long_window_logstash'); await esArchiver.load('visualize'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + await security.testUser.restoreDefaults(); }); }); } diff --git a/test/functional/config.js b/test/functional/config.js index e84b7e0a98a68..11399bd6187c8 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -103,5 +103,172 @@ export default async function({ readConfigFile }) { browser: { type: 'chrome', }, + + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_shakespeare_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['shakes*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_testhuge_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testhuge*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + test_alias_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['alias*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + //for sample data - can remove but not add sample data.( not ml)- for ml use built in role. + kibana_sample_admin: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['kibana_sample*'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date-nanos'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_custom: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_custom_timestamp'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_date_nanos_mixed: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['date_nanos_mixed', 'timestamp-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + kibana_large_strings: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['testlargestring'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + long_window_logstash: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['long-window-logstash-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + + animals: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['animals-*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + }, + defaultRoles: ['test_logstash_reader', 'kibana_admin'], + }, }; } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 60966511c1f99..5ee3726ddb44f 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -105,13 +105,16 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo const wantedLoginPage = appUrl.includes('/login') || appUrl.includes('/logout'); if (loginPage && !wantedLoginPage) { - log.debug( - `Found login page. Logging in with username = ${config.get('servers.kibana.username')}` - ); - await PageObjects.shield.login( - config.get('servers.kibana.username'), - config.get('servers.kibana.password') - ); + log.debug('Found login page'); + if (config.get('security.disableTestUser')) { + await PageObjects.shield.login( + config.get('servers.kibana.username'), + config.get('servers.kibana.password') + ); + } else { + await PageObjects.shield.login('test_user', 'changeme'); + } + await find.byCssSelector( '[data-test-subj="kibanaChrome"] nav:not(.ng-hide)', 6 * defaultFindTimeout diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 02349b4e6cca2..5017947e95d03 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -21,6 +21,7 @@ import { cloneDeep } from 'lodash'; import { Key, Origin } from 'selenium-webdriver'; // @ts-ignore internal modules are not typed import { LegacyActionSequence } from 'selenium-webdriver/lib/actions'; +import { ProvidedType } from '@kbn/test/types/ftr'; import Jimp from 'jimp'; import { modifyUrl } from '../../../src/core/utils'; @@ -28,6 +29,7 @@ import { WebElementWrapper } from './lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; import { Browsers } from './remote/browsers'; +export type Browser = ProvidedType; export async function BrowserProvider({ getService }: FtrProviderContext) { const log = getService('log'); const { driver, browserType } = await getService('__webdriver__').init(); diff --git a/test/functional/services/test_subjects.ts b/test/functional/services/test_subjects.ts index d47b838c8d72a..e5c2e61c48a0b 100644 --- a/test/functional/services/test_subjects.ts +++ b/test/functional/services/test_subjects.ts @@ -19,6 +19,7 @@ import testSubjSelector from '@kbn/test-subj-selector'; import { map as mapAsync } from 'bluebird'; +import { ProvidedType } from '@kbn/test/types/ftr'; import { WebElementWrapper } from './lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -32,6 +33,7 @@ interface SetValueOptions { typeCharByChar?: boolean; } +export type TestSubjects = ProvidedType; export function TestSubjectsProvider({ getService }: FtrProviderContext) { const log = getService('log'); const retry = getService('retry'); diff --git a/x-pack/test/api_integration/config.js b/x-pack/test/api_integration/config.js index 182a9105a7df8..b62368bf2d608 100644 --- a/x-pack/test/api_integration/config.js +++ b/x-pack/test/api_integration/config.js @@ -15,6 +15,7 @@ export async function getApiIntegrationConfig({ readConfigFile }) { testFiles: [require.resolve('./apis')], services, servers: xPackFunctionalTestsConfig.get('servers'), + security: xPackFunctionalTestsConfig.get('security'), esArchiver: xPackFunctionalTestsConfig.get('esArchiver'), junit: { reportName: 'X-Pack API Integration Tests', diff --git a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js index b9c0b0095b96b..78cef80c7ca87 100644 --- a/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js +++ b/x-pack/test/functional/apps/dashboard_mode/dashboard_view_mode.js @@ -12,6 +12,7 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const log = getService('log'); const pieChart = getService('pieChart'); + const security = getService('security'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); @@ -109,13 +110,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.security.clickSaveEditUser(); }); - after('logout', async () => { - await PageObjects.security.forceLogout(); + after(async () => { + await security.testUser.restoreDefaults(); }); it('shows only the dashboard app link', async () => { + await security.testUser.setRoles(['test_logstash_reader', 'kibana_dashboard_only_user']); + await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.security.forceLogout(); - await PageObjects.security.login('dashuser', '123456'); + await PageObjects.security.login('test_user', 'changeme'); const appLinks = await appsMenu.readLinks(); expect(appLinks).to.have.length(1); @@ -194,8 +197,12 @@ export default function({ getService, getPageObjects }) { }); it('is loaded for a user who is assigned a non-dashboard mode role', async () => { - await PageObjects.security.forceLogout(); - await PageObjects.security.login('mixeduser', '123456'); + await security.testUser.setRoles([ + 'test_logstash_reader', + 'kibana_dashboard_only_user', + 'kibana_admin', + ]); + await PageObjects.header.waitUntilLoadingHasFinished(); if (await appsMenu.linkExists('Management')) { throw new Error('Expected management nav link to not be shown'); @@ -203,8 +210,8 @@ export default function({ getService, getPageObjects }) { }); it('is not loaded for a user who is assigned a superuser role', async () => { - await PageObjects.security.forceLogout(); - await PageObjects.security.login('mysuperuser', '123456'); + await security.testUser.setRoles(['kibana_dashboard_only_user', 'superuser']); + await PageObjects.header.waitUntilLoadingHasFinished(); if (!(await appsMenu.linkExists('Management'))) { throw new Error('Expected management nav link to be shown'); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 09ec403af7424..1586908d8b5ef 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -217,5 +217,24 @@ export default async function({ readConfigFile }) { junit: { reportName: 'Chrome X-Pack UI Functional Tests', }, + security: { + roles: { + test_logstash_reader: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['logstash*'], + privileges: ['read', 'view_index_metadata'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + }, + defaultRoles: ['superuser'], + }, }; } diff --git a/x-pack/test/oidc_api_integration/config.ts b/x-pack/test/oidc_api_integration/config.ts index 724ffd35cc9e3..557dea4d51b0e 100644 --- a/x-pack/test/oidc_api_integration/config.ts +++ b/x-pack/test/oidc_api_integration/config.ts @@ -17,6 +17,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis/authorization_code_flow')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services, junit: { reportName: 'X-Pack OpenID Connect API Integration Tests', diff --git a/x-pack/test/pki_api_integration/config.ts b/x-pack/test/pki_api_integration/config.ts index a445b3d4943b0..21ae1b40efa16 100644 --- a/x-pack/test/pki_api_integration/config.ts +++ b/x-pack/test/pki_api_integration/config.ts @@ -28,6 +28,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis')], servers, + security: { disableTestUser: true }, services, junit: { reportName: 'X-Pack PKI API Integration Tests', diff --git a/x-pack/test/reporting/configs/chromium_functional.js b/x-pack/test/reporting/configs/chromium_functional.js index 81a51f44c1c5f..05c3b6c142946 100644 --- a/x-pack/test/reporting/configs/chromium_functional.js +++ b/x-pack/test/reporting/configs/chromium_functional.js @@ -33,5 +33,6 @@ export default async function({ readConfigFile }) { }, esArchiver: functionalConfig.get('esArchiver'), esTestCluster: functionalConfig.get('esTestCluster'), + security: { disableTestUser: true }, }; } diff --git a/x-pack/test/saml_api_integration/config.ts b/x-pack/test/saml_api_integration/config.ts index 6ea29b0d9e56e..502d34d4c9e5d 100644 --- a/x-pack/test/saml_api_integration/config.ts +++ b/x-pack/test/saml_api_integration/config.ts @@ -19,6 +19,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { return { testFiles: [require.resolve('./apis')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services: { randomness: kibanaAPITestsConfig.get('services.randomness'), legacyEs: kibanaAPITestsConfig.get('services.legacyEs'), diff --git a/x-pack/test/token_api_integration/config.js b/x-pack/test/token_api_integration/config.js index db5ee6a9a1cbd..84322ff9473f3 100644 --- a/x-pack/test/token_api_integration/config.js +++ b/x-pack/test/token_api_integration/config.js @@ -10,6 +10,7 @@ export default async function({ readConfigFile }) { return { testFiles: [require.resolve('./auth')], servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, services: { legacyEs: xPackAPITestsConfig.get('services.legacyEs'), supertestWithoutAuth: xPackAPITestsConfig.get('services.supertestWithoutAuth'), From f875b7165e3ea0ba925f9a4dd89c39703a5e6bab Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 17 Mar 2020 13:41:46 -0400 Subject: [PATCH 08/35] do not update cell background if is label cell (#60308) --- .../classification_exploration/column_data.tsx | 4 +++- .../classification_exploration/evaluate_panel.tsx | 14 ++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx index 5a08dd159affb..baf7fd32b0f60 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/column_data.tsx @@ -15,6 +15,8 @@ interface ColumnData { error_count?: number; } +export const ACTUAL_CLASS_ID = 'actual_class'; + export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const colData: Partial = []; @@ -67,7 +69,7 @@ export function getColumnData(confusionMatrixData: ConfusionMatrix[]) { const columns: any = [ { - id: 'actual_class', + id: ACTUAL_CLASS_ID, display: , }, ]; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index 23dd1ae288d8e..7bf55f4ecf392 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -39,7 +39,7 @@ import { ANALYSIS_CONFIG_TYPE, } from '../../../../common/analytics'; import { LoadingPanel } from '../loading_panel'; -import { getColumnData } from './column_data'; +import { getColumnData, ACTUAL_CLASS_ID } from './column_data'; const defaultPanelWidth = 500; @@ -205,11 +205,13 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const cellValue = columnsData[rowIndex][columnId]; // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { - setCellProps({ - style: { - backgroundColor: `rgba(0, 179, 164, ${cellValue})`, - }, - }); + if (columnId !== ACTUAL_CLASS_ID) { + setCellProps({ + style: { + backgroundColor: `rgba(0, 179, 164, ${cellValue})`, + }, + }); + } }, [rowIndex, columnId, setCellProps]); return ( {typeof cellValue === 'number' ? `${Math.round(cellValue * 100)}%` : cellValue} From 928454afa4ce0f7136c90b2a8756e0600b55e360 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Tue, 17 Mar 2020 11:51:17 -0700 Subject: [PATCH 09/35] Update the ems-client dependency to 7.7.0 (#59936) * Update the ems-client dependency This PR adds the `appName` and `appVersion` parameters used by ems-client. The `appVersion` parameter replaces the now deprecated `kbnVersion` parameter in ems-client. * Review feedback * Fix borked merge Co-authored-by: Elastic Machine --- package.json | 2 +- src/legacy/ui/public/vis/map/service_settings.js | 3 ++- x-pack/legacy/plugins/maps/public/meta.js | 4 +++- x-pack/legacy/plugins/maps/server/routes.js | 4 +++- x-pack/package.json | 2 +- x-pack/plugins/maps/common/constants.ts | 1 + yarn.lock | 8 ++++---- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 261b3ad74d9b7..13796dcbf2b76 100644 --- a/package.json +++ b/package.json @@ -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", 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/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/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/package.json b/x-pack/package.json index 3c8aa435c3e43..20b9dcc6cd0bc 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -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", diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index b1483cefa43bc..b608151d26ae8 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'; diff --git a/yarn.lock b/yarn.lock index 1e5c160a7eb19..ea153e2a9c63f 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" From 9f31565b885f149c4473ba7408d5625c9b538690 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 17 Mar 2020 19:25:01 +0000 Subject: [PATCH 10/35] [ML] Fixing custom urls to dashboards (#60355) * [ML] Fixing custom urls to dashboards * missing file Co-authored-by: Elastic Machine --- x-pack/plugins/ml/kibana.json | 3 +- x-pack/plugins/ml/public/application/app.tsx | 1 + .../components/custom_url_editor/utils.js | 139 +++++++++--------- .../application/util/dependency_cache.ts | 11 ++ x-pack/plugins/ml/public/plugin.ts | 3 + 5 files changed, 85 insertions(+), 72 deletions(-) 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/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/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/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..624e877bda49f 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -7,6 +7,7 @@ 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 { DataPublicPluginStart } from 'src/plugins/data/public'; import { SecurityPluginSetup } from '../../security/public'; @@ -17,6 +18,7 @@ import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; export interface MlStartDependencies { data: DataPublicPluginStart; + share: SharePluginStart; } export interface MlSetupDependencies { security: SecurityPluginSetup; @@ -41,6 +43,7 @@ export class MlPlugin implements Plugin { coreStart, { data: pluginsStart.data, + share: pluginsStart.share, security: pluginsSetup.security, licensing: pluginsSetup.licensing, management: pluginsSetup.management, From a8e33a87b57b141a925ddc5c8a7bea72c51832a9 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Tue, 17 Mar 2020 13:27:10 -0600 Subject: [PATCH 11/35] Update app arch CODEOWNERS items. (#60396) --- .github/CODEOWNERS | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index accc99170bb70..132a99fb0a151 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 From 631d93da50335a1db1519bb7fc5dfb35210da758 Mon Sep 17 00:00:00 2001 From: Karen Metts <35154725+karenzone@users.noreply.github.com> Date: Tue, 17 Mar 2020 16:11:40 -0400 Subject: [PATCH 12/35] Remove link to old settings (#60326) --- docs/settings/monitoring-settings.asciidoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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]. From 2367d749c102faf1f039a5c03d2d16eff43f06f9 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 17 Mar 2020 15:24:59 -0700 Subject: [PATCH 13/35] upgrade react-use (#60427) Co-authored-by: spalger Co-authored-by: Elastic Machine --- package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 42 +++++++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 13796dcbf2b76..583e99158da72 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/x-pack/package.json b/x-pack/package.json index 20b9dcc6cd0bc..6f15b46e28f53 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -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/yarn.lock b/yarn.lock index ea153e2a9c63f..a7e29935c7ab5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -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" @@ -13667,10 +13672,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" @@ -18498,6 +18503,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" @@ -24919,16 +24929,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" From 32b3861580720503a7864cb92b30ff84e4e7f8de Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 17 Mar 2020 15:25:44 -0700 Subject: [PATCH 14/35] [kbn/pm] don't fail when plugins are outside repo (#60164) * [kbn/pm] don't fail when plugins are outside repo * remove unused import Co-authored-by: spalger Co-authored-by: Elastic Machine --- packages/kbn-pm/dist/index.js | 1244 +++++++++-------- packages/kbn-pm/package.json | 1 + packages/kbn-pm/src/utils/kibana.ts | 12 + .../kbn-pm/src/utils/project_checksums.ts | 26 +- yarn.lock | 2 +- 5 files changed, 681 insertions(+), 604 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 338d489300526..16fc0d891185f 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"]; }); @@ -57074,7 +57074,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 +57117,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 +57146,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 +57209,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 +79171,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 +79203,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 +79218,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 +79277,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 +79286,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 +79304,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 +79405,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 +79465,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 +79613,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 706 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79573,13 +79621,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 +79746,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 +79759,7 @@ module.exports.default = module.exports; /***/ }), -/* 708 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79790,7 +79838,7 @@ module.exports.default = pMap; /***/ }), -/* 709 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79820,17 +79868,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 +80023,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 +80036,7 @@ module.exports = function () { /***/ }), -/* 712 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80057,7 +80105,7 @@ if ('Set' in global) { /***/ }), -/* 713 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -80106,13 +80154,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 +80901,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 714 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -80863,12 +80911,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 +80949,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 716 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -80911,12 +80959,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 +81441,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 717 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -81639,10 +81687,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 +81703,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 +81781,7 @@ function isString(source) { /***/ }), -/* 720 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81771,13 +81819,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 +81916,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 +82071,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 +82102,7 @@ module.exports = function globParent(str) { /***/ }), -/* 724 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82085,7 +82133,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 725 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82235,7 +82283,7 @@ module.exports.win32 = win32; /***/ }), -/* 726 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -82287,7 +82335,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 727 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82298,18 +82346,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 +83219,7 @@ module.exports = micromatch; /***/ }), -/* 728 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83181,18 +83229,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 +83544,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 +83699,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 730 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83664,7 +83712,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 +83737,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 731 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83702,9 +83750,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 +83766,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 732 */ +/* 733 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -83871,7 +83919,7 @@ function isBuffer(val) { /***/ }), -/* 733 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83884,7 +83932,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(734); +var typeOf = __webpack_require__(735); // accessor descriptor properties var accessor = { @@ -83947,10 +83995,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 +84117,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 735 */ +/* 736 */ /***/ (function(module, exports) { /*! @@ -84096,7 +84144,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 736 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84109,7 +84157,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(737); +var typeOf = __webpack_require__(738); // data descriptor properties var data = { @@ -84158,10 +84206,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 +84328,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 +84368,7 @@ function hasOwn(obj, key) { /***/ }), -/* 739 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84340,13 +84388,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 +84461,7 @@ module.exports = toRegex; /***/ }), -/* 741 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84463,13 +84511,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 +84800,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 +85150,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 744 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85115,7 +85163,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 +85328,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 +85395,7 @@ function isEnum(obj, key) { /***/ }), -/* 746 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85360,7 +85408,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 +85416,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 747 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85381,7 +85429,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 +85460,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 748 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85431,7 +85479,7 @@ module.exports = function isObject(val) { /***/ }), -/* 749 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85478,7 +85526,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 750 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85507,7 +85555,7 @@ function flat(arr, res) { /***/ }), -/* 751 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85521,10 +85569,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 +85770,7 @@ module.exports = fillRange; /***/ }), -/* 752 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85735,7 +85783,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 +85799,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 +85921,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 754 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85950,7 +85998,7 @@ function repeat(str, num) { /***/ }), -/* 755 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85963,8 +86011,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 +86299,7 @@ module.exports = toRegexRange; /***/ }), -/* 756 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86276,14 +86324,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 +86691,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 +87190,7 @@ exports = module.exports = Node; /***/ }), -/* 759 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87155,7 +87203,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 +87228,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 760 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87193,9 +87241,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 +87257,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 761 */ +/* 762 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87344,7 +87392,7 @@ function isBuffer(val) { /***/ }), -/* 762 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87357,7 +87405,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(763); +var typeOf = __webpack_require__(764); // accessor descriptor properties var accessor = { @@ -87420,7 +87468,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 763 */ +/* 764 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87555,7 +87603,7 @@ function isBuffer(val) { /***/ }), -/* 764 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87568,7 +87616,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 +87659,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 765 */ +/* 766 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -87746,13 +87794,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 +88820,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 +88931,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 +89112,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 +89554,7 @@ module.exports.namespace = namespace; /***/ }), -/* 770 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89519,7 +89567,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 +89592,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 +89860,7 @@ module.exports.namespace = namespace; /***/ }), -/* 772 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { @@ -89981,7 +90029,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 773 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89994,8 +90042,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 +90066,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 774 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90031,7 +90079,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 +90106,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 +90150,7 @@ function isObject(val) { /***/ }), -/* 776 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90115,7 +90163,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 +90190,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 +90227,7 @@ function arrayify(val) { /***/ }), -/* 778 */ +/* 779 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90215,7 +90263,7 @@ module.exports = function union(init) { /***/ }), -/* 779 */ +/* 780 */ /***/ (function(module, exports) { /*! @@ -90271,7 +90319,7 @@ function toString(val) { /***/ }), -/* 780 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90284,10 +90332,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 +90381,7 @@ function isValidKey(key) { /***/ }), -/* 781 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90346,8 +90394,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 +90420,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 782 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90385,9 +90433,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 +90446,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 783 */ +/* 784 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90411,7 +90459,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 +90467,7 @@ module.exports = function isObject(val) { /***/ }), -/* 784 */ +/* 785 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -90430,7 +90478,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 785 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90473,7 +90521,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 786 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90486,9 +90534,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 +90544,7 @@ module.exports = function(val, prop) { /***/ }), -/* 787 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90509,8 +90557,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 +90611,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 +90736,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 +90807,7 @@ module.exports = mixinDeep; /***/ }), -/* 790 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90772,7 +90820,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 +90828,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 791 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90803,7 +90851,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 792 */ +/* 793 */ /***/ (function(module, exports) { /*! @@ -90830,14 +90878,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 +91250,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 794 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91216,10 +91264,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 +91278,7 @@ module.exports = utils; /***/ }), -/* 795 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91243,8 +91291,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 +91375,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 +91556,7 @@ module.exports.has = has; /***/ }), -/* 797 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91596,16 +91644,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 +91807,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 +91828,7 @@ module.exports = Compiler; /***/ }), -/* 799 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91793,7 +91841,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 +91956,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 800 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91922,8 +91970,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 +91986,7 @@ module.exports = utils; /***/ }), -/* 801 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -91947,14 +91995,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 +92011,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 +92193,7 @@ function localstorage() { /***/ }), -/* 803 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { @@ -92161,7 +92209,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 +92401,7 @@ function coerce(val) { /***/ }), -/* 804 */ +/* 805 */ /***/ (function(module, exports) { /** @@ -92511,7 +92559,7 @@ function plural(ms, n, name) { /***/ }), -/* 805 */ +/* 806 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -92527,7 +92575,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 +92754,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 +92813,13 @@ exports.enable(load()); /***/ }), -/* 806 */ +/* 807 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 807 */ +/* 808 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92781,9 +92829,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 +92874,7 @@ exports.last = function(arr, n) { /***/ }), -/* 808 */ +/* 809 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -92834,13 +92882,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 +92898,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 +93310,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 810 */ +/* 811 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93302,7 +93350,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 +93456,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 811 */ +/* 812 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93481,7 +93529,7 @@ exports.decode = function (charCode) { /***/ }), -/* 812 */ +/* 813 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93904,7 +93952,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 813 */ +/* 814 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -93914,7 +93962,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 +94079,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 814 */ +/* 815 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94041,7 +94089,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 +94164,7 @@ exports.MappingList = MappingList; /***/ }), -/* 815 */ +/* 816 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94126,11 +94174,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 +95252,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 816 */ +/* 817 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95321,7 +95369,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 817 */ +/* 818 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95441,7 +95489,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 818 */ +/* 819 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95451,8 +95499,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 +95908,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 +96216,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 +96279,7 @@ void (function(root, factory) { /***/ }), -/* 821 */ +/* 822 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96249,13 +96297,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 +96314,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 823 */ +/* 824 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96367,7 +96415,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 824 */ +/* 825 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -96390,7 +96438,7 @@ module.exports = urix /***/ }), -/* 825 */ +/* 826 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96404,7 +96452,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 826 */ +/* 827 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -96412,8 +96460,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 +96604,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 +97144,7 @@ module.exports = Parser; /***/ }), -/* 828 */ +/* 829 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97203,13 +97251,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 +97272,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 +97434,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 +97483,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 +97771,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 +97888,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 834 */ +/* 835 */ /***/ (function(module, exports) { module.exports = { @@ -97856,10 +97904,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 +97992,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 +98015,7 @@ exports.end = function() { /***/ }), -/* 837 */ +/* 838 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97980,8 +98028,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 +98060,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 +98127,7 @@ function isEnum(obj, key) { /***/ }), -/* 839 */ +/* 840 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98092,7 +98140,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 +98148,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 +98227,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 +98311,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 842 */ +/* 843 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98274,17 +98322,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 +99156,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 +99223,7 @@ function isEnum(obj, key) { /***/ }), -/* 844 */ +/* 845 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99188,7 +99236,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 +99244,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 845 */ +/* 846 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99542,15 +99590,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 +99984,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 847 */ +/* 848 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99949,7 +99997,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 +100011,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 848 */ +/* 849 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99991,14 +100039,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 +100059,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 +100181,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 851 */ +/* 852 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100146,14 +100194,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 +100567,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 +100595,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 853 */ +/* 854 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100560,8 +100608,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 +100640,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 854 */ +/* 855 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100646,7 +100694,7 @@ function diffArray(one, two) { /***/ }), -/* 855 */ +/* 856 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100659,7 +100707,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 +100736,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 856 */ +/* 857 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -100823,7 +100871,7 @@ function isBuffer(val) { /***/ }), -/* 857 */ +/* 858 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100833,18 +100881,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 +101209,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 +101385,7 @@ module.exports = function(extglob) { /***/ }), -/* 859 */ +/* 860 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101347,17 +101395,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 +101603,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 +101697,7 @@ module.exports = function(brackets) { /***/ }), -/* 861 */ +/* 862 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101678,14 +101726,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 +101952,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 +101993,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 864 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -101954,14 +102002,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 +102018,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 +102200,7 @@ function localstorage() { /***/ }), -/* 866 */ +/* 867 */ /***/ (function(module, exports, __webpack_require__) { @@ -102168,7 +102216,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 +102408,7 @@ function coerce(val) { /***/ }), -/* 867 */ +/* 868 */ /***/ (function(module, exports) { /** @@ -102518,7 +102566,7 @@ function plural(ms, n, name) { /***/ }), -/* 868 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -102534,7 +102582,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 +102761,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 +102820,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 +102983,7 @@ module.exports = parsers; /***/ }), -/* 870 */ +/* 871 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102948,7 +102996,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 +103021,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 +103097,7 @@ utils.createRegex = function(str) { /***/ }), -/* 872 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103059,16 +103107,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 +103182,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 +103272,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 +103292,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 +103595,7 @@ utils.unixify = function(options) { /***/ }), -/* 876 */ +/* 877 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -103682,7 +103730,7 @@ function isBuffer(val) { /***/ }), -/* 877 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103701,9 +103749,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 +103812,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 +103904,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 879 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103864,11 +103912,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 +103945,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 880 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103906,9 +103954,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 +104332,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 +104516,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 882 */ +/* 883 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -104605,13 +104653,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 +104734,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 884 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104747,14 +104795,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 +104866,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 886 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104847,7 +104895,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 887 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104855,12 +104903,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 +104950,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 888 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104929,7 +104977,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 889 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104965,7 +105013,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 890 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104973,11 +105021,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 +105045,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 +105120,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 +105210,7 @@ exports.default = DeepFilter; /***/ }), -/* 893 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105193,14 +105241,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 +105333,7 @@ exports.default = EntryFilter; /***/ }), -/* 895 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105305,8 +105353,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 +105404,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 +105442,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 +105461,7 @@ exports.prepare = prepare; /***/ }), -/* 898 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105436,7 +105484,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 899 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105488,7 +105536,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 900 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105519,7 +105567,7 @@ exports.default = FileSystem; /***/ }), -/* 901 */ +/* 902 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105539,9 +105587,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 +105657,7 @@ exports.default = ReaderStream; /***/ }), -/* 902 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105628,9 +105676,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 +105738,7 @@ exports.default = ReaderSync; /***/ }), -/* 903 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105709,8 +105757,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 +105804,7 @@ exports.default = FileSystemSync; /***/ }), -/* 904 */ +/* 905 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105772,7 +105820,7 @@ exports.flatten = flatten; /***/ }), -/* 905 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105793,13 +105841,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 +105913,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 +105962,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 908 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106005,17 +106053,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 +106161,7 @@ module.exports.sync = options => { /***/ }), -/* 910 */ +/* 911 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -106582,7 +106630,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 911 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106657,7 +106705,7 @@ module.exports = (input, options) => { /***/ }), -/* 912 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106675,17 +106723,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 +106847,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 +107143,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 +107194,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 916 */ +/* 917 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107168,12 +107216,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 +107235,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 +107289,7 @@ module.exports = NestedError; /***/ }), -/* 919 */ +/* 920 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -107249,12 +107297,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 +107331,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 +107437,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 922 */ +/* 923 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107397,7 +107445,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 +107594,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 923 */ +/* 924 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -109148,7 +109196,7 @@ function coerce (version, options) { /***/ }), -/* 924 */ +/* 925 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109189,7 +109237,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 925 */ +/* 926 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109235,12 +109283,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 +109302,7 @@ module.exports = CpyError; /***/ }), -/* 927 */ +/* 928 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -109310,7 +109358,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..278fdbd2bc9a4 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -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/yarn.lock b/yarn.lock index a7e29935c7ab5..eaee706101a7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17576,7 +17576,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== From f168b6abb844c6f4c76db0d7bb3018f60ccba89d Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 15:26:29 -0700 Subject: [PATCH 15/35] Add additional safeguards for data source wizard step 2 (#60426) Co-authored-by: Elastic Machine --- .../create_datasource_page/step_configure_datasource.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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); From 3e0b6fb65d916104c3d80874bab6dd2f5844f193 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Tue, 17 Mar 2020 18:55:56 -0400 Subject: [PATCH 16/35] [IM] Use EuiCodeBlock to render index mapping (#60420) --- .../detail_panel/show_json/show_json.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) 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}; } } From bc16fcd984d772edcb381f28ce654d73d0ae7a08 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Tue, 17 Mar 2020 16:15:27 -0700 Subject: [PATCH 17/35] Update ingest management team handle (#60457) --- .github/CODEOWNERS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 132a99fb0a151..df3a56dd35130 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -74,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 From 0d23c516ce4da540bacef2ab5536f8fd35bd1198 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Mar 2020 00:23:50 +0100 Subject: [PATCH 18/35] [Console] Fix bool filter autocompletions and refactor (#60361) The autocomplete lib component was controlling state that belongs to the legacy editor model and so it was moved there. The fix for filter autocompletion inside of "bool" was just adding "[" around the filter scoped entry. * Remove reference to model code from autocomplete lib * Also renamed the __tests__ dir to __jest__ to avoid re-running in mocha. --- .../legacy_core_editor/legacy_core_editor.ts | 56 ++++++- .../__tests__/integration.test.js | 145 +++++++++--------- .../models/sense_editor/sense_editor.ts | 1 + .../url_autocomplete.test.js | 1 - .../url_params.test.js | 4 - .../public/lib/autocomplete/autocomplete.ts | 71 ++------- .../public/lib/autocomplete/body_completer.js | 1 - .../console/public/lib/autocomplete/engine.js | 2 +- .../console/public/types/core_editor.ts | 12 ++ .../lib/spec_definitions/es_6_0/query/dsl.js | 8 +- 10 files changed, 161 insertions(+), 140 deletions(-) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_autocomplete.test.js (99%) rename src/plugins/console/public/lib/autocomplete/{__tests__ => __jest__}/url_params.test.js (95%) diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 47947e985092b..49093dd3527b5 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -18,9 +18,17 @@ */ import ace from 'brace'; -import { Editor as IAceEditor } from 'brace'; +import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; import $ from 'jquery'; -import { CoreEditor, Position, Range, Token, TokensProvider, EditorEvent } from '../../../types'; +import { + CoreEditor, + Position, + Range, + Token, + TokensProvider, + EditorEvent, + AutoCompleterFunction, +} from '../../../types'; import { AceTokensProvider } from '../../../lib/ace_token_provider'; import * as curl from '../sense_editor/curl'; import smartResize from './smart_resize'; @@ -354,4 +362,48 @@ export class LegacyCoreEditor implements CoreEditor { } } } + + registerAutocompleter(getCompletions: AutoCompleterFunction): void { + // Hook into Ace + + // disable standard context based autocompletion. + // @ts-ignore + ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( + require: any, + exports: any + ) { + exports.getCompletions = function( + innerEditor: any, + session: any, + pos: any, + prefix: any, + callback: any + ) { + callback(null, []); + }; + }); + + const langTools = ace.acequire('ace/ext/language_tools'); + + langTools.setCompleters([ + { + identifierRegexps: [ + /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character + ], + getCompletions: ( + DO_NOT_USE_1: IAceEditor, + DO_NOT_USE_2: IAceEditSession, + pos: { row: number; column: number }, + prefix: string, + callback: (...args: any[]) => void + ) => { + const position: Position = { + lineNumber: pos.row + 1, + column: pos.column + 1, + }; + getCompletions(position, prefix, callback); + }, + }, + ]); + } } diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index 1a09b6b00da9c..c5a0c2ebddf71 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -84,93 +84,90 @@ describe('Integration', () => { changeListener: function() {}, }; // mimic auto complete - senseEditor.autocomplete._test.getCompletions( - senseEditor, - null, - { row: cursor.lineNumber - 1, column: cursor.column - 1 }, - '', - function(err, terms) { - if (testToRun.assertThrows) { - done(); - return; - } + senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function( + err, + terms + ) { + if (testToRun.assertThrows) { + done(); + return; + } - if (err) { - throw err; - } + if (err) { + throw err; + } - if (testToRun.no_context) { - expect(!terms || terms.length === 0).toBeTruthy(); - } else { - expect(terms).not.toBeNull(); - expect(terms.length).toBeGreaterThan(0); - } + if (testToRun.no_context) { + expect(!terms || terms.length === 0).toBeTruthy(); + } else { + expect(terms).not.toBeNull(); + expect(terms.length).toBeGreaterThan(0); + } - if (!terms || terms.length === 0) { - done(); - return; - } + if (!terms || terms.length === 0) { + done(); + return; + } - if (testToRun.autoCompleteSet) { - const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return t; - }); - if (terms.length !== expectedTerms.length) { - expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); - } else { - const filteredActualTerms = _.map(terms, function(actualTerm, i) { - const expectedTerm = expectedTerms[i]; - const filteredTerm = {}; - _.each(expectedTerm, function(v, p) { - filteredTerm[p] = actualTerm[p]; - }); - return filteredTerm; - }); - expect(filteredActualTerms).toEqual(expectedTerms); + if (testToRun.autoCompleteSet) { + const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { + if (typeof t !== 'object') { + t = { name: t }; } + return t; + }); + if (terms.length !== expectedTerms.length) { + expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); + } else { + const filteredActualTerms = _.map(terms, function(actualTerm, i) { + const expectedTerm = expectedTerms[i]; + const filteredTerm = {}; + _.each(expectedTerm, function(v, p) { + filteredTerm[p] = actualTerm[p]; + }); + return filteredTerm; + }); + expect(filteredActualTerms).toEqual(expectedTerms); } + } - const context = terms[0].context; - const { - cursor: { lineNumber, column }, - } = testToRun; - senseEditor.autocomplete._test.addReplacementInfoToContext( - context, - { lineNumber, column }, - terms[0].value - ); + const context = terms[0].context; + const { + cursor: { lineNumber, column }, + } = testToRun; + senseEditor.autocomplete._test.addReplacementInfoToContext( + context, + { lineNumber, column }, + terms[0].value + ); - function ac(prop, propTest) { - if (typeof testToRun[prop] !== 'undefined') { - if (propTest) { - propTest(context[prop], testToRun[prop], prop); - } else { - expect(context[prop]).toEqual(testToRun[prop]); - } + function ac(prop, propTest) { + if (typeof testToRun[prop] !== 'undefined') { + if (propTest) { + propTest(context[prop], testToRun[prop], prop); + } else { + expect(context[prop]).toEqual(testToRun[prop]); } } + } - function posCompare(actual, expected) { - expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); - expect(actual.column).toEqual(expected.column); - } - - function rangeCompare(actual, expected, name) { - posCompare(actual.start, expected.start, name + '.start'); - posCompare(actual.end, expected.end, name + '.end'); - } + function posCompare(actual, expected) { + expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); + expect(actual.column).toEqual(expected.column); + } - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); + function rangeCompare(actual, expected, name) { + posCompare(actual.start, expected.start, name + '.start'); + posCompare(actual.end, expected.end, name + '.end'); } - ); + + ac('prefixToAdd'); + ac('suffixToAdd'); + ac('addTemplate'); + ac('textBoxPosition', posCompare); + ac('rangeToReplace', rangeCompare); + done(); + }); }); } diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index f559f5dfcd707..b1444bdf2bbab 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -44,6 +44,7 @@ export class SenseEditor { coreEditor, parser: this.parser, }); + this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions); this.coreEditor.on( 'tokenizerUpdate', this.highlightCurrentRequestsAndUpdateActionBar.bind(this) diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js similarity index 99% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js index 40fcd551fb6f7..0758a75695566 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import '../../../application/models/sense_editor/sense_editor.test.mocks'; const _ = require('lodash'); import { diff --git a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js similarity index 95% rename from src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js rename to src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js index ce2a2553b19ee..72fce53c4f1fe 100644 --- a/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js @@ -16,10 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import '../../../application/models/sense_editor/sense_editor.test.mocks'; -import 'brace'; -import 'brace/mode/javascript'; -import 'brace/mode/json'; const _ = require('lodash'); import { UrlParams } from '../../autocomplete/url_params'; import { populateContext } from '../../autocomplete/engine'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index e09024ccfc859..d4f10ff4e4277 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -18,9 +18,9 @@ */ import _ from 'lodash'; -import ace, { Editor as AceEditor, IEditSession } from 'brace'; import { i18n } from '@kbn/i18n'; +// TODO: All of these imports need to be moved to the core editor so that it can inject components from there. import { getTopLevelUrlCompleteComponents, getEndpointBodyCompleteComponents, @@ -39,7 +39,7 @@ import { createTokenIterator } from '../../application/factories'; import { Position, Token, Range, CoreEditor } from '../../types'; -let LAST_EVALUATED_TOKEN: any = null; +let lastEvaluatedToken: any = null; function isUrlParamsToken(token: any) { switch ((token || {}).type) { @@ -889,7 +889,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!currentToken) { if (pos.lineNumber === 1) { - LAST_EVALUATED_TOKEN = null; + lastEvaluatedToken = null; return; } currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row @@ -902,26 +902,26 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (parser.isEmptyToken(nextToken)) { // Empty line, or we're not on the edge of current token. Save the current position as base currentToken.position.column = pos.column; - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; } else { nextToken.position.lineNumber = pos.lineNumber; - LAST_EVALUATED_TOKEN = nextToken; + lastEvaluatedToken = nextToken; } return; } - if (!LAST_EVALUATED_TOKEN) { - LAST_EVALUATED_TOKEN = currentToken; + if (!lastEvaluatedToken) { + lastEvaluatedToken = currentToken; return; // wait for the next typing. } if ( - LAST_EVALUATED_TOKEN.position.column !== currentToken.position.column || - LAST_EVALUATED_TOKEN.position.lineNumber !== currentToken.position.lineNumber || - LAST_EVALUATED_TOKEN.value === currentToken.value + lastEvaluatedToken.position.column !== currentToken.position.column || + lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber || + lastEvaluatedToken.value === currentToken.value ) { // not on the same place or nothing changed, cache and wait for the next time - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; return; } @@ -935,7 +935,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor return; } - LAST_EVALUATED_TOKEN = currentToken; + lastEvaluatedToken = currentToken; editor.execCommand('startAutocomplete'); }, 100); @@ -947,17 +947,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor } } - function getCompletions( - DO_NOT_USE: AceEditor, - DO_NOT_USE_SESSION: IEditSession, - pos: { row: number; column: number }, - prefix: string, - callback: (...args: any[]) => void - ) { - const position: Position = { - lineNumber: pos.row + 1, - column: pos.column + 1, - }; + function getCompletions(position: Position, prefix: string, callback: (...args: any[]) => void) { try { const context = getAutoCompleteContext(editor, position); if (!context) { @@ -1028,39 +1018,12 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor editor.on('changeSelection', editorChangeListener); - // Hook into Ace - - // disable standard context based autocompletion. - // @ts-ignore - ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( - require: any, - exports: any - ) { - exports.getCompletions = function( - innerEditor: any, - session: any, - pos: any, - prefix: any, - callback: any - ) { - callback(null, []); - }; - }); - - const langTools = ace.acequire('ace/ext/language_tools'); - - langTools.setCompleters([ - { - identifierRegexps: [ - /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character - ], - getCompletions, - }, - ]); - return { + getCompletions, + // TODO: This needs to be cleaned up _test: { - getCompletions, + getCompletions: (_editor: any, _editSession: any, pos: any, prefix: any, callback: any) => + getCompletions(pos, prefix, callback), addReplacementInfoToContext, addChangeListener: () => editor.on('changeSelection', editorChangeListener), removeChangeListener: () => editor.off('changeSelection', editorChangeListener), diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index e23a58780a362..1aa315c50b9bf 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -115,7 +115,6 @@ class ScopeResolver extends SharedComponent { next: [], }; const components = this.resolveLinkToComponents(context, editor); - _.each(components, function(component) { const componentResult = component.match(token, context, editor); if (componentResult && componentResult.next) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index f4df8af871eba..7b64d91c95374 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -43,7 +43,7 @@ export function wrapComponentWithDefaults(component, defaults) { const tracer = function() { if (window.engine_trace) { - console.log.call(console, arguments); + console.log.call(console, ...arguments); } }; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 79dc3ca74200b..84a2c64a80888 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -29,6 +29,12 @@ export type EditorEvent = | 'change' | 'changeSelection'; +export type AutoCompleterFunction = ( + pos: Position, + prefix: string, + callback: (...args: any[]) => void +) => void; + export interface Position { /** * The line number, not zero-indexed. @@ -256,4 +262,10 @@ export interface CoreEditor { * Register a keyboard shortcut and provide a function to be called. */ registerKeyboardShortcut(opts: { keys: any; fn: () => void; name: string }): void; + + /** + * Register a completions function that will be called when the editor + * detects a change + */ + registerAutocompleter(getCompletions: AutoCompleterFunction): void; } diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/es_6_0/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/es_6_0/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, }, From 8412ab61b4f7ecbdca6bb05225c80023d9bd4745 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 17 Mar 2020 16:27:45 -0700 Subject: [PATCH 19/35] Revert "[Console] Fix bool filter autocompletions and refactor (#60361)" This reverts commit 0d23c516ce4da540bacef2ab5536f8fd35bd1198. --- .../legacy_core_editor/legacy_core_editor.ts | 56 +------ .../__tests__/integration.test.js | 145 +++++++++--------- .../models/sense_editor/sense_editor.ts | 1 - .../url_autocomplete.test.js | 1 + .../url_params.test.js | 4 + .../public/lib/autocomplete/autocomplete.ts | 71 +++++++-- .../public/lib/autocomplete/body_completer.js | 1 + .../console/public/lib/autocomplete/engine.js | 2 +- .../console/public/types/core_editor.ts | 12 -- .../lib/spec_definitions/es_6_0/query/dsl.js | 8 +- 10 files changed, 140 insertions(+), 161 deletions(-) rename src/plugins/console/public/lib/autocomplete/{__jest__ => __tests__}/url_autocomplete.test.js (99%) rename src/plugins/console/public/lib/autocomplete/{__jest__ => __tests__}/url_params.test.js (95%) diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 49093dd3527b5..47947e985092b 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -18,17 +18,9 @@ */ import ace from 'brace'; -import { Editor as IAceEditor, IEditSession as IAceEditSession } from 'brace'; +import { Editor as IAceEditor } from 'brace'; import $ from 'jquery'; -import { - CoreEditor, - Position, - Range, - Token, - TokensProvider, - EditorEvent, - AutoCompleterFunction, -} from '../../../types'; +import { CoreEditor, Position, Range, Token, TokensProvider, EditorEvent } from '../../../types'; import { AceTokensProvider } from '../../../lib/ace_token_provider'; import * as curl from '../sense_editor/curl'; import smartResize from './smart_resize'; @@ -362,48 +354,4 @@ export class LegacyCoreEditor implements CoreEditor { } } } - - registerAutocompleter(getCompletions: AutoCompleterFunction): void { - // Hook into Ace - - // disable standard context based autocompletion. - // @ts-ignore - ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( - require: any, - exports: any - ) { - exports.getCompletions = function( - innerEditor: any, - session: any, - pos: any, - prefix: any, - callback: any - ) { - callback(null, []); - }; - }); - - const langTools = ace.acequire('ace/ext/language_tools'); - - langTools.setCompleters([ - { - identifierRegexps: [ - /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character - ], - getCompletions: ( - DO_NOT_USE_1: IAceEditor, - DO_NOT_USE_2: IAceEditSession, - pos: { row: number; column: number }, - prefix: string, - callback: (...args: any[]) => void - ) => { - const position: Position = { - lineNumber: pos.row + 1, - column: pos.column + 1, - }; - getCompletions(position, prefix, callback); - }, - }, - ]); - } } diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js index c5a0c2ebddf71..1a09b6b00da9c 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/integration.test.js @@ -84,90 +84,93 @@ describe('Integration', () => { changeListener: function() {}, }; // mimic auto complete - senseEditor.autocomplete._test.getCompletions(senseEditor, null, cursor, '', function( - err, - terms - ) { - if (testToRun.assertThrows) { - done(); - return; - } + senseEditor.autocomplete._test.getCompletions( + senseEditor, + null, + { row: cursor.lineNumber - 1, column: cursor.column - 1 }, + '', + function(err, terms) { + if (testToRun.assertThrows) { + done(); + return; + } - if (err) { - throw err; - } + if (err) { + throw err; + } - if (testToRun.no_context) { - expect(!terms || terms.length === 0).toBeTruthy(); - } else { - expect(terms).not.toBeNull(); - expect(terms.length).toBeGreaterThan(0); - } + if (testToRun.no_context) { + expect(!terms || terms.length === 0).toBeTruthy(); + } else { + expect(terms).not.toBeNull(); + expect(terms.length).toBeGreaterThan(0); + } - if (!terms || terms.length === 0) { - done(); - return; - } + if (!terms || terms.length === 0) { + done(); + return; + } - if (testToRun.autoCompleteSet) { - const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { - if (typeof t !== 'object') { - t = { name: t }; - } - return t; - }); - if (terms.length !== expectedTerms.length) { - expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); - } else { - const filteredActualTerms = _.map(terms, function(actualTerm, i) { - const expectedTerm = expectedTerms[i]; - const filteredTerm = {}; - _.each(expectedTerm, function(v, p) { - filteredTerm[p] = actualTerm[p]; - }); - return filteredTerm; + if (testToRun.autoCompleteSet) { + const expectedTerms = _.map(testToRun.autoCompleteSet, function(t) { + if (typeof t !== 'object') { + t = { name: t }; + } + return t; }); - expect(filteredActualTerms).toEqual(expectedTerms); + if (terms.length !== expectedTerms.length) { + expect(_.pluck(terms, 'name')).toEqual(_.pluck(expectedTerms, 'name')); + } else { + const filteredActualTerms = _.map(terms, function(actualTerm, i) { + const expectedTerm = expectedTerms[i]; + const filteredTerm = {}; + _.each(expectedTerm, function(v, p) { + filteredTerm[p] = actualTerm[p]; + }); + return filteredTerm; + }); + expect(filteredActualTerms).toEqual(expectedTerms); + } } - } - const context = terms[0].context; - const { - cursor: { lineNumber, column }, - } = testToRun; - senseEditor.autocomplete._test.addReplacementInfoToContext( - context, - { lineNumber, column }, - terms[0].value - ); + const context = terms[0].context; + const { + cursor: { lineNumber, column }, + } = testToRun; + senseEditor.autocomplete._test.addReplacementInfoToContext( + context, + { lineNumber, column }, + terms[0].value + ); - function ac(prop, propTest) { - if (typeof testToRun[prop] !== 'undefined') { - if (propTest) { - propTest(context[prop], testToRun[prop], prop); - } else { - expect(context[prop]).toEqual(testToRun[prop]); + function ac(prop, propTest) { + if (typeof testToRun[prop] !== 'undefined') { + if (propTest) { + propTest(context[prop], testToRun[prop], prop); + } else { + expect(context[prop]).toEqual(testToRun[prop]); + } } } - } - function posCompare(actual, expected) { - expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); - expect(actual.column).toEqual(expected.column); - } + function posCompare(actual, expected) { + expect(actual.lineNumber).toEqual(expected.lineNumber + lineOffset); + expect(actual.column).toEqual(expected.column); + } - function rangeCompare(actual, expected, name) { - posCompare(actual.start, expected.start, name + '.start'); - posCompare(actual.end, expected.end, name + '.end'); - } + function rangeCompare(actual, expected, name) { + posCompare(actual.start, expected.start, name + '.start'); + posCompare(actual.end, expected.end, name + '.end'); + } - ac('prefixToAdd'); - ac('suffixToAdd'); - ac('addTemplate'); - ac('textBoxPosition', posCompare); - ac('rangeToReplace', rangeCompare); - done(); - }); + ac('prefixToAdd'); + ac('suffixToAdd'); + ac('addTemplate'); + ac('textBoxPosition', posCompare); + ac('rangeToReplace', rangeCompare); + done(); + } + ); }); } diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index b1444bdf2bbab..f559f5dfcd707 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -44,7 +44,6 @@ export class SenseEditor { coreEditor, parser: this.parser, }); - this.coreEditor.registerAutocompleter(this.autocomplete.getCompletions); this.coreEditor.on( 'tokenizerUpdate', this.highlightCurrentRequestsAndUpdateActionBar.bind(this) diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js b/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js similarity index 99% rename from src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js rename to src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js index 0758a75695566..40fcd551fb6f7 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_autocomplete.test.js +++ b/src/plugins/console/public/lib/autocomplete/__tests__/url_autocomplete.test.js @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import '../../../application/models/sense_editor/sense_editor.test.mocks'; const _ = require('lodash'); import { diff --git a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js b/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js similarity index 95% rename from src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js rename to src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js index 72fce53c4f1fe..ce2a2553b19ee 100644 --- a/src/plugins/console/public/lib/autocomplete/__jest__/url_params.test.js +++ b/src/plugins/console/public/lib/autocomplete/__tests__/url_params.test.js @@ -16,6 +16,10 @@ * specific language governing permissions and limitations * under the License. */ +import '../../../application/models/sense_editor/sense_editor.test.mocks'; +import 'brace'; +import 'brace/mode/javascript'; +import 'brace/mode/json'; const _ = require('lodash'); import { UrlParams } from '../../autocomplete/url_params'; import { populateContext } from '../../autocomplete/engine'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index d4f10ff4e4277..e09024ccfc859 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -18,9 +18,9 @@ */ import _ from 'lodash'; +import ace, { Editor as AceEditor, IEditSession } from 'brace'; import { i18n } from '@kbn/i18n'; -// TODO: All of these imports need to be moved to the core editor so that it can inject components from there. import { getTopLevelUrlCompleteComponents, getEndpointBodyCompleteComponents, @@ -39,7 +39,7 @@ import { createTokenIterator } from '../../application/factories'; import { Position, Token, Range, CoreEditor } from '../../types'; -let lastEvaluatedToken: any = null; +let LAST_EVALUATED_TOKEN: any = null; function isUrlParamsToken(token: any) { switch ((token || {}).type) { @@ -889,7 +889,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (!currentToken) { if (pos.lineNumber === 1) { - lastEvaluatedToken = null; + LAST_EVALUATED_TOKEN = null; return; } currentToken = { position: { column: 0, lineNumber: 0 }, value: '', type: '' }; // empty row @@ -902,26 +902,26 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor if (parser.isEmptyToken(nextToken)) { // Empty line, or we're not on the edge of current token. Save the current position as base currentToken.position.column = pos.column; - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; } else { nextToken.position.lineNumber = pos.lineNumber; - lastEvaluatedToken = nextToken; + LAST_EVALUATED_TOKEN = nextToken; } return; } - if (!lastEvaluatedToken) { - lastEvaluatedToken = currentToken; + if (!LAST_EVALUATED_TOKEN) { + LAST_EVALUATED_TOKEN = currentToken; return; // wait for the next typing. } if ( - lastEvaluatedToken.position.column !== currentToken.position.column || - lastEvaluatedToken.position.lineNumber !== currentToken.position.lineNumber || - lastEvaluatedToken.value === currentToken.value + LAST_EVALUATED_TOKEN.position.column !== currentToken.position.column || + LAST_EVALUATED_TOKEN.position.lineNumber !== currentToken.position.lineNumber || + LAST_EVALUATED_TOKEN.value === currentToken.value ) { // not on the same place or nothing changed, cache and wait for the next time - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; return; } @@ -935,7 +935,7 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor return; } - lastEvaluatedToken = currentToken; + LAST_EVALUATED_TOKEN = currentToken; editor.execCommand('startAutocomplete'); }, 100); @@ -947,7 +947,17 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor } } - function getCompletions(position: Position, prefix: string, callback: (...args: any[]) => void) { + function getCompletions( + DO_NOT_USE: AceEditor, + DO_NOT_USE_SESSION: IEditSession, + pos: { row: number; column: number }, + prefix: string, + callback: (...args: any[]) => void + ) { + const position: Position = { + lineNumber: pos.row + 1, + column: pos.column + 1, + }; try { const context = getAutoCompleteContext(editor, position); if (!context) { @@ -1018,12 +1028,39 @@ export default function({ coreEditor: editor, parser }: { coreEditor: CoreEditor editor.on('changeSelection', editorChangeListener); + // Hook into Ace + + // disable standard context based autocompletion. + // @ts-ignore + ace.define('ace/autocomplete/text_completer', ['require', 'exports', 'module'], function( + require: any, + exports: any + ) { + exports.getCompletions = function( + innerEditor: any, + session: any, + pos: any, + prefix: any, + callback: any + ) { + callback(null, []); + }; + }); + + const langTools = ace.acequire('ace/ext/language_tools'); + + langTools.setCompleters([ + { + identifierRegexps: [ + /[a-zA-Z_0-9\.\$\-\u00A2-\uFFFF]/, // adds support for dot character + ], + getCompletions, + }, + ]); + return { - getCompletions, - // TODO: This needs to be cleaned up _test: { - getCompletions: (_editor: any, _editSession: any, pos: any, prefix: any, callback: any) => - getCompletions(pos, prefix, callback), + getCompletions, addReplacementInfoToContext, addChangeListener: () => editor.on('changeSelection', editorChangeListener), removeChangeListener: () => editor.off('changeSelection', editorChangeListener), diff --git a/src/plugins/console/public/lib/autocomplete/body_completer.js b/src/plugins/console/public/lib/autocomplete/body_completer.js index 1aa315c50b9bf..e23a58780a362 100644 --- a/src/plugins/console/public/lib/autocomplete/body_completer.js +++ b/src/plugins/console/public/lib/autocomplete/body_completer.js @@ -115,6 +115,7 @@ class ScopeResolver extends SharedComponent { next: [], }; const components = this.resolveLinkToComponents(context, editor); + _.each(components, function(component) { const componentResult = component.match(token, context, editor); if (componentResult && componentResult.next) { diff --git a/src/plugins/console/public/lib/autocomplete/engine.js b/src/plugins/console/public/lib/autocomplete/engine.js index 7b64d91c95374..f4df8af871eba 100644 --- a/src/plugins/console/public/lib/autocomplete/engine.js +++ b/src/plugins/console/public/lib/autocomplete/engine.js @@ -43,7 +43,7 @@ export function wrapComponentWithDefaults(component, defaults) { const tracer = function() { if (window.engine_trace) { - console.log.call(console, ...arguments); + console.log.call(console, arguments); } }; diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index 84a2c64a80888..79dc3ca74200b 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -29,12 +29,6 @@ export type EditorEvent = | 'change' | 'changeSelection'; -export type AutoCompleterFunction = ( - pos: Position, - prefix: string, - callback: (...args: any[]) => void -) => void; - export interface Position { /** * The line number, not zero-indexed. @@ -262,10 +256,4 @@ export interface CoreEditor { * Register a keyboard shortcut and provide a function to be called. */ registerKeyboardShortcut(opts: { keys: any; fn: () => void; name: string }): void; - - /** - * Register a completions function that will be called when the editor - * detects a change - */ - registerAutocompleter(getCompletions: AutoCompleterFunction): void; } diff --git a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js index 16b952fe0fe4f..a5f0d15dee0e9 100644 --- a/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js +++ b/src/plugins/console/server/lib/spec_definitions/es_6_0/query/dsl.js @@ -281,11 +281,9 @@ export function queryDsl(api) { __scope_link: '.', }, ], - filter: [ - { - __scope_link: 'GLOBAL.filter', - }, - ], + filter: { + __scope_link: 'GLOBAL.filter', + }, minimum_should_match: 1, boost: 1.0, }, From 2e6c76fda7517314543fabe5e4aa1999aab4c631 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 17 Mar 2020 16:33:37 -0700 Subject: [PATCH 20/35] Disabled edit alert button on management ui for non registered UI alert types (#60439) --- x-pack/plugins/alerting/server/plugin.ts | 10 ---------- .../alerts_list/components/alerts_list.test.tsx | 14 +++++++++++++- .../alerts_list/components/alerts_list.tsx | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) 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/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..f8f0c278c81e2 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 @@ -242,6 +242,8 @@ describe('alerts_list component with items', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(true); + wrapper = mountWithIntl( @@ -257,11 +259,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', () => { @@ -455,6 +461,8 @@ describe('alerts_list with show only capability', () => { alertTypeRegistry: alertTypeRegistry as any, }; + alertTypeRegistry.has.mockReturnValue(false); + wrapper = mountWithIntl( @@ -473,4 +481,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..8d8fc177b57a0 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 @@ -225,7 +225,7 @@ export const AlertsList: React.FunctionComponent = () => { ? [ { render: (item: AlertTableItem) => { - return ( + return alertTypeRegistry.has(item.alertTypeId) ? ( { id="xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editLinkTitle" /> + ) : ( + <> ); }, }, From 4deea08f23c63870daedc5612698be8ea17103d2 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 17 Mar 2020 18:15:26 -0700 Subject: [PATCH 21/35] Publish getIsNavDrawerLocked$ method on core chrome service. (#60191) * Remove isCollapsed, getIsCollapsed, and global_nav_state. --- ...blic.chromestart.getisnavdrawerlocked_.md} | 8 ++-- .../kibana-plugin-core-public.chromestart.md | 3 +- ...-core-public.chromestart.setiscollapsed.md | 24 ---------- src/core/public/chrome/chrome_service.mock.ts | 5 +-- src/core/public/chrome/chrome_service.test.ts | 40 ++--------------- src/core/public/chrome/chrome_service.tsx | 43 ++++++++---------- src/core/public/chrome/ui/header/header.tsx | 27 ++++++++--- .../chrome/ui/header/header_wrapper.tsx | 45 ------------------- src/core/public/chrome/ui/header/index.ts | 1 - src/core/public/chrome/ui/index.ts | 1 - src/core/public/public.api.md | 3 +- src/legacy/ui/public/chrome/chrome.js | 1 - .../chrome/services/global_nav_state.js | 45 ------------------- src/legacy/ui/public/chrome/services/index.js | 20 --------- 14 files changed, 50 insertions(+), 216 deletions(-) rename docs/development/core/public/{kibana-plugin-core-public.chromestart.getiscollapsed_.md => kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md} (51%) delete mode 100644 docs/development/core/public/kibana-plugin-core-public.chromestart.setiscollapsed.md delete mode 100644 src/core/public/chrome/ui/header/header_wrapper.tsx delete mode 100644 src/legacy/ui/public/chrome/services/global_nav_state.js delete mode 100644 src/legacy/ui/public/chrome/services/index.js 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/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/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/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/chrome/services/global_nav_state.js b/src/legacy/ui/public/chrome/services/global_nav_state.js deleted file mode 100644 index 5a67806852fe8..0000000000000 --- a/src/legacy/ui/public/chrome/services/global_nav_state.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { 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); - }, - }; -}); diff --git a/src/legacy/ui/public/chrome/services/index.js b/src/legacy/ui/public/chrome/services/index.js deleted file mode 100644 index 3b3967f51b2ff..0000000000000 --- a/src/legacy/ui/public/chrome/services/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './global_nav_state'; From c1435db29f8d90688ac2a645c5778ad2f438823f Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 17 Mar 2020 18:15:58 -0700 Subject: [PATCH 22/35] Edits UI text for ML nodes and job button (#60184) * Edits UI text for ML nodes and job button * Update x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js Co-Authored-By: Brandon Morelli * Update x-pack/plugins/ml/public/application/explorer/components/explorer_no_jobs_found/explorer_no_jobs_found.js Co-Authored-By: Brandon Morelli Co-authored-by: Elastic Machine Co-authored-by: Brandon Morelli --- .../ServiceIntegrations/MachineLearningFlyout/view.tsx | 2 +- .../__snapshots__/explorer_no_jobs_found.test.js.snap | 2 +- .../explorer_no_jobs_found/explorer_no_jobs_found.js | 5 +---- .../jobs_list/components/jobs_stats_bar/jobs_stats_bar.js | 2 +- .../jobs_list/components/new_job_button/new_job_button.js | 2 +- .../overview/components/anomaly_detection_panel/utils.ts | 2 +- x-pack/plugins/ml/public/application/services/job_service.js | 2 +- 7 files changed, 7 insertions(+), 10 deletions(-) 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/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/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, From 2207e0ab265fe0a7c204fdd54a3edbea732b283f Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 17 Mar 2020 18:20:00 -0700 Subject: [PATCH 23/35] Index Action - Moved index params fields to connector config (#60349) * Moved index params fields to connector config * Fixed type check issue * Fixing functional tests * Fixed due to comments * Fixed functional tests * Fixed tests and type check --- .../builtin_action_types/es_index.test.ts | 121 +++---- .../server/builtin_action_types/es_index.ts | 34 +- .../public/application/boot.tsx | 2 +- .../builtin_action_types/es_index.test.tsx | 39 +-- .../builtin_action_types/es_index.tsx | 297 ++++++++++++++---- .../components/builtin_action_types/types.ts | 7 +- .../threshold/expression.tsx | 95 +----- .../builtin_alert_types/threshold/types.ts | 6 - .../threshold/visualization.tsx | 2 +- .../action_connector_form.test.tsx | 1 + .../action_connector_form.tsx | 4 + .../connector_add_flyout.tsx | 1 + .../connector_add_modal.tsx | 1 + .../connector_edit_flyout.tsx | 1 + .../public/common/index_controls/index.ts | 90 ++++++ .../lib/index_threshold_api.ts} | 3 +- .../triggers_actions_ui/public/types.ts | 6 +- .../actions/builtin_action_types/es_index.ts | 119 ++++--- .../actions/builtin_action_types/es_index.ts | 32 +- 19 files changed, 528 insertions(+), 333 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts rename x-pack/plugins/triggers_actions_ui/public/{application/components/builtin_alert_types/threshold/lib/api.ts => common/lib/index_threshold_api.ts} (96%) 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/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/common/index_controls/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts new file mode 100644 index 0000000000000..32fb35d6adebb --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/common/index_controls/index.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpSetup } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { + loadIndexPatterns, + getMatchingIndicesForThresholdAlertType, + getThresholdAlertTypeFields, +} from '../lib/index_threshold_api'; + +export interface IOption { + label: string; + options: Array<{ value: string; label: string }>; +} + +export const getIndexPatterns = async () => { + const indexPatternObjects = await loadIndexPatterns(); + return indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); +}; + +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/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/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); From ac5e323af872f0db601724c3c22455bbcd946c45 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 17 Mar 2020 18:47:54 -0700 Subject: [PATCH 24/35] [Search service] Asynchronous ES search strategy (#53538) * Add async search strategy * Add async search * Fix async strategy and add tests * Move types to separate file * Revert changes to demo search * Update demo search strategy to use async * Add async es search strategy * Return response as rawResponse * Poll after initial request * Add cancellation to search strategies * Add tests * Simplify async search strategy * Move loadingCount to search strategy * Update abort controller library * Bootstrap * Abort when the request is aborted * Add utility and update value suggestions route * Fix bad merge conflict * Update tests * Move to data_enhanced plugin * Remove bad merge * Revert switching abort controller libraries * Revert package.json in lib * Move to previous abort controller * Add support for frozen indices * Fix test to use fake timers to run debounced handlers * Revert changes to example plugin * Fix loading bar not going away when cancelling * Call getSearchStrategy instead of passing directly * Add async demo search strategy * Fix error with setting state * Update how aborting works * Fix type checks * Add test for loading count * Attempt to fix broken example test * Revert changes to test * Fix test * Update name to camelCase * Fix failing test * Don't require data_enhanced in example plugin * Actually send DELETE request * Use waitForCompletion parameter * Use default search params * Add support for rollups * Only make changes needed for frozen indices/rollups * Only make changes needed for frozen indices/rollups * Add back in async functionality * Fix tests/types * Fix issue with sending empty body in GET * Don't include skipped in loaded/total * Don't wait before polling the next time * Simplify search logic * Fix merge error * Review feedback * Fix issue with hits.total Co-authored-by: Elastic Machine --- ...bana-plugin-plugins-data-server.icancel.md | 11 ---- ...lugin-plugins-data-server.isearchcancel.md | 11 ++++ .../kibana-plugin-plugins-data-server.md | 2 +- src/plugins/data/server/index.ts | 2 +- .../search/i_route_handler_search_context.ts | 4 +- src/plugins/data/server/search/i_search.ts | 4 +- .../data/server/search/i_search_strategy.ts | 4 +- src/plugins/data/server/search/index.ts | 8 ++- src/plugins/data/server/server.api.md | 12 ++-- .../public/search/es_search_strategy.ts | 16 ++++-- .../server/search/es_search_strategy.ts | 56 ++++++++++++++++--- 11 files changed, 90 insertions(+), 40 deletions(-) delete mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.icancel.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchcancel.md 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/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/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); } From 65a111f189d371e7c67f562eb78df29b45dafcc7 Mon Sep 17 00:00:00 2001 From: Candace Park <56409205+parkiino@users.noreply.github.com> Date: Tue, 17 Mar 2020 23:31:41 -0400 Subject: [PATCH 25/35] Task/host enhancements (#59671) functional tests and ui updates to endpoint host details --- .../endpoint/common/generate_data.test.ts | 12 +- .../plugins/endpoint/common/generate_data.ts | 8 +- x-pack/plugins/endpoint/common/types.ts | 12 +- .../endpoint/components/header_nav.tsx | 8 +- .../public/applications/endpoint/index.tsx | 86 ++++++------ .../applications/endpoint/store/action.ts | 4 +- .../endpoint/store/hosts/action.ts | 40 ++++++ .../endpoint/store/hosts/index.test.ts | 73 ++++++++++ .../store/{managing => hosts}/index.ts | 6 +- .../{managing => hosts}/middleware.test.ts | 37 ++--- .../store/{managing => hosts}/middleware.ts | 33 ++--- .../mock_host_result_list.ts | 12 +- .../store/{managing => hosts}/reducer.ts | 22 ++- .../endpoint/store/hosts/selectors.ts | 60 ++++++++ .../applications/endpoint/store/index.ts | 6 +- .../endpoint/store/managing/action.ts | 39 ------ .../endpoint/store/managing/index.test.ts | 93 ------------- .../endpoint/store/managing/selectors.ts | 60 -------- .../applications/endpoint/store/reducer.ts | 4 +- .../public/applications/endpoint/types.ts | 16 +-- .../endpoint/view/formatted_date_time.tsx | 24 ++++ .../view/{managing => hosts}/details.tsx | 71 ++++++---- .../view/{managing => hosts}/hooks.ts | 8 +- .../view/{managing => hosts}/index.test.tsx | 31 +++-- .../view/{managing => hosts}/index.tsx | 129 ++++++++++-------- .../url_from_query_params.ts | 4 +- .../endpoint/view/policy/policy_list.tsx | 25 +--- .../endpoint/scripts/resolver_generator.ts | 16 +-- .../endpoint/server/routes/metadata.test.ts | 28 ++-- .../endpoint/server/routes/metadata.ts | 26 ++-- .../api_integration/apis/endpoint/metadata.ts | 42 +++--- .../feature_controls/endpoint_spaces.ts | 19 +-- .../functional/apps/endpoint/header_nav.ts | 14 +- .../endpoint/{management.ts => host_list.ts} | 63 ++++++++- x-pack/test/functional/apps/endpoint/index.ts | 2 +- .../functional/page_objects/endpoint_page.ts | 35 ++++- 36 files changed, 623 insertions(+), 545 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/index.ts (60%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/middleware.test.ts (63%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/middleware.ts (59%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/mock_host_result_list.ts (82%) rename x-pack/plugins/endpoint/public/applications/endpoint/store/{managing => hosts}/reducer.ts (66%) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts delete mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/details.tsx (62%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/hooks.ts (61%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/index.test.tsx (75%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/index.tsx (55%) rename x-pack/plugins/endpoint/public/applications/endpoint/view/{managing => hosts}/url_from_query_params.ts (78%) rename x-pack/test/functional/apps/endpoint/{management.ts => host_list.ts} (58%) 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/types.ts b/x-pack/plugins/endpoint/common/types.ts index aa326c663965d..e423de56bf817 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. 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/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/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/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/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/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/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(); + }); + }, }; } From fae93176e2a0d3d251d90a35bd80f2493bf7325e Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Mar 2020 08:11:36 +0100 Subject: [PATCH 26/35] [Console] Fix for `_settings` and x-pack autocomplete (#60246) * Add settings completion to index create endpoint and clean up. The cleanup is largely for moving settings data completion to JS and removing the dynamic logic for loading different ES versions. This is unused and unnecessary at this point. * Add new settings JS files and move BOOLEAN to shared file. * Important fix for loading x-pack console extensions. After migrating the x-pack console extensions were being loaded too late and were not being served to the client. * Reorder imports to convention --- src/plugins/console/public/lib/kb/kb.js | 10 +- src/plugins/console/server/lib/index.ts | 2 +- .../lib/spec_definitions/{es_6_0.js => es.js} | 44 +++---- .../server/lib/spec_definitions/index.d.ts | 9 +- .../server/lib/spec_definitions/index.js | 6 +- .../{es_6_0 => js}/aggregations.js | 0 .../{es_6_0 => js}/aliases.js | 0 .../{es_6_0 => js}/document.js | 0 .../spec_definitions/{es_6_0 => js}/filter.js | 0 .../{es_6_0 => js}/globals.js | 0 .../spec_definitions/{es_6_0 => js}/ingest.js | 0 .../{es_6_0 => js}/mappings.js | 4 +- .../{es_6_0 => js}/query/dsl.js | 0 .../{es_6_0 => js}/query/index.js | 0 .../{es_6_0 => js}/query/templates.js | 0 .../{es_6_0 => js}/reindex.js | 0 .../spec_definitions/{es_6_0 => js}/search.js | 0 .../lib/spec_definitions/js/settings.js | 74 ++++++++++++ .../server/lib/spec_definitions/js/shared.js | 22 ++++ .../spec_definitions/{spec => json}/.eslintrc | 0 .../{spec => json}/generated/_common.json | 0 .../{spec => json}/generated/bulk.json | 0 .../{spec => json}/generated/cat.aliases.json | 0 .../generated/cat.allocation.json | 0 .../{spec => json}/generated/cat.count.json | 0 .../generated/cat.fielddata.json | 0 .../{spec => json}/generated/cat.health.json | 0 .../{spec => json}/generated/cat.help.json | 0 .../{spec => json}/generated/cat.indices.json | 0 .../{spec => json}/generated/cat.master.json | 0 .../generated/cat.nodeattrs.json | 0 .../{spec => json}/generated/cat.nodes.json | 0 .../generated/cat.pending_tasks.json | 0 .../{spec => json}/generated/cat.plugins.json | 0 .../generated/cat.recovery.json | 0 .../generated/cat.repositories.json | 0 .../generated/cat.segments.json | 0 .../{spec => json}/generated/cat.shards.json | 0 .../generated/cat.snapshots.json | 0 .../{spec => json}/generated/cat.tasks.json | 0 .../generated/cat.templates.json | 0 .../generated/cat.thread_pool.json | 0 .../generated/clear_scroll.json | 0 .../generated/cluster.allocation_explain.json | 0 .../generated/cluster.get_settings.json | 0 .../generated/cluster.health.json | 0 .../generated/cluster.pending_tasks.json | 0 .../generated/cluster.put_settings.json | 0 .../generated/cluster.remote_info.json | 0 .../generated/cluster.reroute.json | 0 .../generated/cluster.state.json | 0 .../generated/cluster.stats.json | 0 .../{spec => json}/generated/count.json | 0 .../{spec => json}/generated/create.json | 0 .../{spec => json}/generated/delete.json | 0 .../generated/delete_by_query.json | 0 .../generated/delete_by_query_rethrottle.json | 0 .../generated/delete_script.json | 0 .../{spec => json}/generated/exists.json | 0 .../generated/exists_source.json | 0 .../{spec => json}/generated/explain.json | 0 .../{spec => json}/generated/field_caps.json | 0 .../{spec => json}/generated/get.json | 0 .../{spec => json}/generated/get_script.json | 0 .../generated/get_script_context.json | 0 .../generated/get_script_languages.json | 0 .../{spec => json}/generated/get_source.json | 0 .../{spec => json}/generated/index.json | 0 .../generated/indices.analyze.json | 0 .../generated/indices.clear_cache.json | 0 .../generated/indices.clone.json | 0 .../generated/indices.close.json | 0 .../generated/indices.create.json | 0 .../generated/indices.delete.json | 0 .../generated/indices.delete_alias.json | 0 .../generated/indices.delete_template.json | 0 .../generated/indices.exists.json | 0 .../generated/indices.exists_alias.json | 0 .../generated/indices.exists_template.json | 0 .../generated/indices.exists_type.json | 0 .../generated/indices.flush.json | 0 .../generated/indices.flush_synced.json | 0 .../generated/indices.forcemerge.json | 0 .../{spec => json}/generated/indices.get.json | 0 .../generated/indices.get_alias.json | 0 .../generated/indices.get_field_mapping.json | 0 .../generated/indices.get_mapping.json | 0 .../generated/indices.get_settings.json | 0 .../generated/indices.get_template.json | 0 .../generated/indices.get_upgrade.json | 0 .../generated/indices.open.json | 0 .../generated/indices.put_alias.json | 0 .../generated/indices.put_mapping.json | 0 .../generated/indices.put_settings.json | 0 .../generated/indices.put_template.json | 0 .../generated/indices.recovery.json | 0 .../generated/indices.refresh.json | 0 .../generated/indices.rollover.json | 0 .../generated/indices.segments.json | 0 .../generated/indices.shard_stores.json | 0 .../generated/indices.shrink.json | 0 .../generated/indices.split.json | 0 .../generated/indices.stats.json | 0 .../generated/indices.update_aliases.json | 0 .../generated/indices.upgrade.json | 0 .../generated/indices.validate_query.json | 0 .../{spec => json}/generated/info.json | 0 .../generated/ingest.delete_pipeline.json | 0 .../generated/ingest.get_pipeline.json | 0 .../generated/ingest.processor_grok.json | 0 .../generated/ingest.put_pipeline.json | 0 .../generated/ingest.simulate.json | 0 .../{spec => json}/generated/mget.json | 0 .../{spec => json}/generated/msearch.json | 0 .../generated/msearch_template.json | 0 .../generated/mtermvectors.json | 0 .../generated/nodes.hot_threads.json | 0 .../{spec => json}/generated/nodes.info.json | 0 .../nodes.reload_secure_settings.json | 0 .../{spec => json}/generated/nodes.stats.json | 0 .../{spec => json}/generated/nodes.usage.json | 0 .../{spec => json}/generated/ping.json | 0 .../{spec => json}/generated/put_script.json | 0 .../{spec => json}/generated/rank_eval.json | 0 .../{spec => json}/generated/reindex.json | 0 .../generated/reindex_rethrottle.json | 0 .../generated/render_search_template.json | 0 .../generated/scripts_painless_execute.json | 0 .../{spec => json}/generated/scroll.json | 0 .../{spec => json}/generated/search.json | 0 .../generated/search_shards.json | 0 .../generated/search_template.json | 0 .../snapshot.cleanup_repository.json | 0 .../generated/snapshot.create.json | 0 .../generated/snapshot.create_repository.json | 0 .../generated/snapshot.delete.json | 0 .../generated/snapshot.delete_repository.json | 0 .../generated/snapshot.get.json | 0 .../generated/snapshot.get_repository.json | 0 .../generated/snapshot.restore.json | 0 .../generated/snapshot.status.json | 0 .../generated/snapshot.verify_repository.json | 0 .../generated/tasks.cancel.json | 0 .../{spec => json}/generated/tasks.get.json | 0 .../{spec => json}/generated/tasks.list.json | 0 .../{spec => json}/generated/termvectors.json | 0 .../{spec => json}/generated/update.json | 0 .../generated/update_by_query.json | 0 .../generated/update_by_query_rethrottle.json | 0 .../spec_definitions/{spec => json}/index.js | 0 .../overrides/clear_scroll.json | 0 .../overrides/cluster.health.json | 0 .../overrides/cluster.put_settings.json | 0 .../overrides/cluster.reroute.json | 0 .../{spec => json}/overrides/count.json | 0 .../overrides/indices.analyze.json | 0 .../overrides/indices.clone.json | 0 .../overrides/indices.create.json | 0 .../overrides/indices.delete_template.json | 0 .../overrides/indices.exists_template.json | 0 .../overrides/indices.get_field_mapping.json | 0 .../overrides/indices.get_mapping.json | 0 .../overrides/indices.get_template.json | 0 .../overrides/indices.put_alias.json | 0 .../json/overrides/indices.put_settings.json | 7 ++ .../overrides/indices.put_template.json | 0 .../overrides/indices.rollover.json | 0 .../overrides/indices.update_aliases.json | 0 .../overrides/indices.validate_query.json | 0 .../overrides/snapshot.create.json | 0 .../overrides/snapshot.create_repository.json | 0 .../overrides/snapshot.restore.json | 0 .../server/lib/spec_definitions/server.js | 21 +--- .../lib/spec_definitions/server.test.js | 51 --------- .../spec/overrides/indices.put_settings.json | 108 ------------------ src/plugins/console/server/plugin.ts | 11 +- .../api/console/spec_definitions/index.ts | 22 +--- .../generated/ml.estimate_memory_usage.json | 2 +- 178 files changed, 163 insertions(+), 230 deletions(-) rename src/plugins/console/server/lib/spec_definitions/{es_6_0.js => es.js} (54%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/aggregations.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/aliases.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/document.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/filter.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/globals.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/ingest.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/mappings.js (99%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/dsl.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/index.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/query/templates.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/reindex.js (100%) rename src/plugins/console/server/lib/spec_definitions/{es_6_0 => js}/search.js (100%) create mode 100644 src/plugins/console/server/lib/spec_definitions/js/settings.js create mode 100644 src/plugins/console/server/lib/spec_definitions/js/shared.js rename src/plugins/console/server/lib/spec_definitions/{spec => json}/.eslintrc (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/_common.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/bulk.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.allocation.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.fielddata.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.help.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.indices.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.master.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.nodeattrs.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.nodes.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.pending_tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.plugins.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.recovery.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.repositories.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.segments.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.shards.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.snapshots.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.templates.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cat.thread_pool.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/clear_scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.allocation_explain.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.get_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.pending_tasks.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.remote_info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.reroute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.state.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/cluster.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_by_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_by_query_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/delete_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/exists.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/exists_source.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/explain.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/field_caps.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script_context.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_script_languages.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/get_source.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/index.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.analyze.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.clear_cache.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.clone.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.close.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.delete_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.exists_type.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.flush.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.flush_synced.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.forcemerge.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_field_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.get_upgrade.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.open.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_alias.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.put_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.recovery.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.refresh.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.rollover.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.segments.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.shard_stores.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.shrink.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.split.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.update_aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.upgrade.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/indices.validate_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.delete_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.get_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.processor_grok.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.put_pipeline.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ingest.simulate.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/mget.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/msearch.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/msearch_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/mtermvectors.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.hot_threads.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.info.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.reload_secure_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.stats.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/nodes.usage.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/ping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/put_script.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/rank_eval.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/reindex.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/reindex_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/render_search_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/scripts_painless_execute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search_shards.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/search_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.cleanup_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.create_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.delete.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.delete_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.get_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.restore.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.status.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/snapshot.verify_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.cancel.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.get.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/tasks.list.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/termvectors.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update_by_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/generated/update_by_query_rethrottle.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/index.js (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/clear_scroll.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.health.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.put_settings.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/cluster.reroute.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/count.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.analyze.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.clone.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.delete_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.exists_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_field_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_mapping.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.get_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.put_alias.json (100%) create mode 100644 src/plugins/console/server/lib/spec_definitions/json/overrides/indices.put_settings.json rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.put_template.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.rollover.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.update_aliases.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/indices.validate_query.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.create.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.create_repository.json (100%) rename src/plugins/console/server/lib/spec_definitions/{spec => json}/overrides/snapshot.restore.json (100%) delete mode 100644 src/plugins/console/server/lib/spec_definitions/server.test.js delete mode 100644 src/plugins/console/server/lib/spec_definitions/spec/overrides/indices.put_settings.json 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 100% 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 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/plugins/console/server/lib/spec_definitions/js/shared.js b/src/plugins/console/server/lib/spec_definitions/js/shared.js new file mode 100644 index 0000000000000..ace189e2d0913 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/js/shared.js @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export 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/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" From 2fbf38b57ade3585cf092359e51208178a72a461 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Wed, 18 Mar 2020 10:25:30 +0300 Subject: [PATCH 27/35] [NP] Use local helper shortenDottedString for discover (#60271) * Move shortenDottedString into kibana_utils * Move helper back to data utils * Use local helper for discover * Clean up --- .../kibana/public/discover/kibana_services.ts | 2 -- .../angular/directives/field_name/field_name.tsx | 2 +- .../components/table_header/helpers.tsx | 3 ++- .../discover/np_ready/helpers/index.ts} | 16 +--------------- .../np_ready/helpers/shorten_dotted_string.ts} | 7 +------ 5 files changed, 5 insertions(+), 25 deletions(-) rename src/legacy/core_plugins/kibana/{common/utils/__tests__/shorten_dotted_string.js => public/discover/np_ready/helpers/index.ts} (60%) rename src/legacy/core_plugins/kibana/{common/utils/shorten_dotted_string.js => public/discover/np_ready/helpers/shorten_dotted_string.ts} (81%) 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..5f3dbb65fd8ff 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -53,8 +53,6 @@ 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 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/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/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts similarity index 60% rename from src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js rename to src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts index 267ca74c7c42a..7196c96989e97 100644 --- a/src/legacy/core_plugins/kibana/common/utils/__tests__/shorten_dotted_string.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/helpers/index.ts @@ -17,18 +17,4 @@ * 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 { 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.'); From fd16c461289700eca91e0929851a5051f22c4523 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Wed, 18 Mar 2020 08:33:53 +0000 Subject: [PATCH 28/35] [ML] Re-enabling file upload telemetry (#60418) * [ML] Re-enabling file upload telemetry * small refactor * removing exported function * removing commented out code * removing commented out include * cleaning up types --- x-pack/plugins/ml/mappings.json | 13 -- x-pack/plugins/ml/public/plugin.ts | 3 + .../ml/server/lib/ml_telemetry/index.ts | 15 -- .../ml_telemetry/make_ml_usage_collector.ts | 41 ------ .../lib/ml_telemetry/ml_telemetry.test.ts | 128 ------------------ .../server/lib/ml_telemetry/ml_telemetry.ts | 72 ---------- .../plugins/ml/server/lib/telemetry/index.ts | 8 ++ .../lib/telemetry/internal_repository.ts | 15 ++ .../ml/server/lib/telemetry/mappings.ts | 25 ++++ .../lib/telemetry/ml_usage_collector.ts | 32 +++++ .../ml/server/lib/telemetry/telemetry.test.ts | 49 +++++++ .../ml/server/lib/telemetry/telemetry.ts | 81 +++++++++++ x-pack/plugins/ml/server/plugin.ts | 6 +- .../ml/server/routes/file_data_visualizer.ts | 4 +- 14 files changed, 217 insertions(+), 275 deletions(-) delete mode 100644 x-pack/plugins/ml/mappings.json delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/index.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/make_ml_usage_collector.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.test.ts delete mode 100644 x-pack/plugins/ml/server/lib/ml_telemetry/ml_telemetry.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/index.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/internal_repository.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/mappings.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/ml_usage_collector.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/telemetry.test.ts create mode 100644 x-pack/plugins/ml/server/lib/telemetry/telemetry.ts 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/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 624e877bda49f..79aebece85af2 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -8,6 +8,7 @@ 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'; @@ -24,6 +25,7 @@ export interface MlSetupDependencies { security: SecurityPluginSetup; licensing: LicensingPluginSetup; management: ManagementSetup; + usageCollection: UsageCollectionSetup; } export class MlPlugin implements Plugin { @@ -47,6 +49,7 @@ export class MlPlugin implements Plugin { 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/plugins/ml/server/lib/telemetry/index.ts b/x-pack/plugins/ml/server/lib/telemetry/index.ts new file mode 100644 index 0000000000000..b5ec80daf1787 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/telemetry/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { 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( From 45f59f7d9e2592894a30d7e9482c4a4ce0504173 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Wed, 18 Mar 2020 12:19:50 +0100 Subject: [PATCH 29/35] Enforce `required` presence for value/key validation of `recordOf` and `mapOf`. (#60406) --- .../kbn-config-schema/src/internals/index.ts | 12 ++++++++---- .../src/types/map_of_type.test.ts | 18 ++++++++++++++++++ .../kbn-config-schema/src/types/map_type.ts | 5 ++++- .../src/types/record_of_type.test.ts | 18 ++++++++++++++++++ .../kbn-config-schema/src/types/record_type.ts | 5 ++++- .../roles/model/put_payload.test.ts | 2 +- 6 files changed, 53 insertions(+), 7 deletions(-) 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/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"` ); }); From d466cc9cca836790cdeb198d60a8d54b839c609d Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Wed, 18 Mar 2020 12:28:46 +0100 Subject: [PATCH 30/35] add data-test-subj where possible on SO management table (#60226) * add data-test-subj where possible * add per-action dts * update snapshots --- .../table/__jest__/__snapshots__/table.test.js.snap | 10 ++++++++++ .../components/objects_table/components/table/table.js | 8 ++++++++ 2 files changed, 18 insertions(+) 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}`, + })} />
    From 70c1b69eb08bc8af29d89194b8f356d70fd9e8a8 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 18 Mar 2020 14:04:25 +0200 Subject: [PATCH 31/35] [SIEM][Case] Update connector through flyout (#60307) * Move add flyout to parent * Disable mapping * Show edit flyout * Do not update connectors throught cases API * Fix uncontrolled input error * Disable edit button * Add comments * Change undefined to null Co-authored-by: Elastic Machine --- .../public/containers/case/configure/api.ts | 19 +---- .../public/containers/case/configure/types.ts | 8 +- .../case/configure/use_connectors.tsx | 55 +------------ .../siem/public/lib/connectors/servicenow.tsx | 10 ++- .../components/configure_cases/connectors.tsx | 43 +--------- .../case/components/configure_cases/index.tsx | 82 +++++++++++++++---- .../components/configure_cases/mapping.tsx | 56 ++++++++++--- .../configure_cases/translations.ts | 4 + .../api/cases/configure/patch_connector.ts | 68 --------------- .../plugins/case/server/routes/api/index.ts | 2 - 10 files changed, 131 insertions(+), 216 deletions(-) delete mode 100644 x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts 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 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/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); From a97ecaae69d4ba4ba5e7a8e21b2d49671eb2596c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Wed, 18 Mar 2020 08:31:03 -0400 Subject: [PATCH 32/35] Fix create alert button from not showing in alerts list (#60444) --- .../plugins/triggers_actions_ui/index.ts | 7 ----- x-pack/plugins/triggers_actions_ui/README.md | 6 ---- .../public/application/app.tsx | 1 - .../actions_connectors_list.test.tsx | 4 --- .../sections/alert_form/alert_add.test.tsx | 1 - .../components/alerts_list.test.tsx | 28 ------------------- .../alerts_list/components/alerts_list.tsx | 4 +-- .../triggers_actions_ui/public/plugin.ts | 1 - x-pack/test/functional_with_es_ssl/config.ts | 2 -- 9 files changed, 1 insertion(+), 53 deletions(-) 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/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/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/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 f8f0c278c81e2..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: { @@ -315,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: { @@ -439,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: { 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 8d8fc177b57a0..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([]); @@ -273,7 +271,7 @@ export const AlertsList: React.FunctionComponent = () => { />, ]; - if (canSave && createAlertUiEnabled) { + if (canSave) { toolsRight.push( Date: Wed, 18 Mar 2020 13:36:20 +0100 Subject: [PATCH 33/35] [License Management] NP migration (#60250) --- x-pack/.i18nrc.json | 2 +- x-pack/index.js | 2 - .../upload_license.test.tsx.snap | 2826 ---------------- .../plugins/license_management/index.ts | 49 - .../public/management_section.ts | 20 - .../public/np_ready/application/boot.tsx | 79 - .../np_ready/application/breadcrumbs.ts | 37 - .../store/actions/set_breadcrumb.ts | 22 - .../public/np_ready/plugin.ts | 52 - .../public/register_route.ts | 87 - .../server/np_ready/lib/start_trial.ts | 47 - .../server/np_ready/plugin.ts | 35 - .../api/license/register_permissions_route.ts | 29 - .../api/license/register_start_basic_route.ts | 27 - .../license/register_start_trial_routes.ts | 34 - .../server/np_ready/types.ts | 24 - .../xpack_main/public/components/index.js | 6 +- .../__snapshots__/add_license.test.js.snap | 0 .../__snapshots__/license_status.test.js.snap | 0 .../request_trial_extension.test.js.snap | 0 .../revert_to_basic.test.js.snap | 0 .../__snapshots__/start_trial.test.js.snap | 0 .../upload_license.test.tsx.snap | 2891 +++++++++++++++++ .../__jest__/add_license.test.js | 2 +- .../__jest__/api_responses/index.js | 0 .../__jest__/api_responses/upload_license.js | 0 .../__jest__/license_status.test.js | 2 +- .../__jest__/request_trial_extension.test.js | 2 +- .../__jest__/revert_to_basic.test.js | 2 +- .../__jest__/start_trial.test.js | 4 +- .../__jest__/upload_license.test.tsx | 72 +- .../license_management/__jest__/util/index.js | 0 .../license_management/__jest__/util/util.js | 24 +- .../__mocks__/focus-trap-react.js | 0 .../common/constants/base_path.ts | 2 + .../common/constants/external_links.ts | 0 .../common/constants/index.ts | 2 +- .../common/constants/permissions.ts | 0 .../common/constants/plugin.ts | 4 +- x-pack/plugins/license_management/kibana.json | 9 + .../application/_license_management.scss | 0 .../public}/application/app.container.js | 0 .../public}/application/app.js | 2 +- .../public/application/app_context.tsx | 53 + .../public/application/app_providers.tsx | 55 + .../public/application/breadcrumbs.ts | 72 + .../components/telemetry_opt_in/index.ts | 0 .../telemetry_opt_in/telemetry_opt_in.tsx | 0 .../public}/application/index.scss | 5 +- .../public/application/index.tsx | 35 + .../public}/application/lib/es.ts | 13 +- .../public}/application/lib/telemetry.ts | 6 +- .../public}/application/sections/index.js | 0 .../add_license/add_license.js | 2 +- .../license_dashboard/add_license/index.js | 0 .../sections/license_dashboard/index.js | 0 .../license_dashboard.container.js | 0 .../license_dashboard/license_dashboard.js | 0 .../license_dashboard/license_status/index.js | 0 .../license_status.container.js | 0 .../license_status/license_status.js | 0 .../request_trial_extension/index.js | 0 .../request_trial_extension.container.js | 0 .../request_trial_extension.js | 2 +- .../revert_to_basic/index.js | 0 .../revert_to_basic.container.js | 0 .../revert_to_basic/revert_to_basic.js | 2 +- .../license_dashboard/start_trial/index.ts | 0 .../start_trial/start_trial.container.js | 0 .../start_trial/start_trial.tsx | 36 +- .../sections/upload_license/index.js | 0 .../upload_license.container.js | 0 .../sections/upload_license/upload_license.js | 2 +- .../store/actions/add_error_message.js | 0 .../application/store/actions/add_license.js | 0 .../application/store/actions/index.js | 0 .../application/store/actions/permissions.js | 0 .../store/actions/set_breadcrumb.ts | 17 + .../application/store/actions/start_basic.js | 4 +- .../application/store/actions/start_trial.js | 8 +- .../store/actions/upload_license.js | 6 +- .../public}/application/store/index.js | 0 .../application/store/reducers/index.js | 0 .../application/store/reducers/license.js | 0 .../store/reducers/license_management.js | 0 .../application/store/reducers/permissions.js | 0 .../reducers/start_basic_license_status.js | 0 .../store/reducers/trial_status.js | 0 .../store/reducers/upload_error_message.js | 0 .../store/reducers/upload_status.js | 0 .../public}/application/store/store.js | 0 .../license_management/public}/index.ts | 4 +- .../license_management/public/plugin.ts | 83 + .../license_management/public/types.ts} | 5 +- .../license_management/server/config.ts | 16 + .../license_management/server}/index.ts | 11 +- .../server/lib/is_es_error.ts} | 10 +- .../license_management/server}/lib/license.ts | 36 +- .../server}/lib/permissions.ts | 20 +- .../server}/lib/start_basic.ts | 25 +- .../server/lib/start_trial.ts | 45 + .../license_management/server/plugin.ts | 35 + .../server}/routes/api/license/index.ts | 0 .../api/license/register_license_route.ts | 23 +- .../api/license/register_permissions_route.ts | 26 + .../api/license/register_start_basic_route.ts | 33 + .../license/register_start_trial_routes.ts | 31 + .../server/routes/helpers.ts} | 4 +- .../license_management/server/routes/index.ts | 23 + .../license_management/server/types.ts | 32 + 110 files changed, 3652 insertions(+), 3524 deletions(-) delete mode 100644 x-pack/legacy/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap delete mode 100644 x-pack/legacy/plugins/license_management/index.ts delete mode 100644 x-pack/legacy/plugins/license_management/public/management_section.ts delete mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/application/boot.tsx delete mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/application/breadcrumbs.ts delete mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/application/store/actions/set_breadcrumb.ts delete mode 100644 x-pack/legacy/plugins/license_management/public/np_ready/plugin.ts delete mode 100644 x-pack/legacy/plugins/license_management/public/register_route.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/lib/start_trial.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/plugin.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_permissions_route.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_basic_route.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/routes/api/license/register_start_trial_routes.ts delete mode 100644 x-pack/legacy/plugins/license_management/server/np_ready/types.ts rename x-pack/{legacy => }/plugins/license_management/__jest__/__snapshots__/add_license.test.js.snap (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/__snapshots__/license_status.test.js.snap (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap (100%) create mode 100644 x-pack/plugins/license_management/__jest__/__snapshots__/upload_license.test.tsx.snap rename x-pack/{legacy => }/plugins/license_management/__jest__/add_license.test.js (90%) rename x-pack/{legacy => }/plugins/license_management/__jest__/api_responses/index.js (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/api_responses/upload_license.js (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/license_status.test.js (88%) rename x-pack/{legacy => }/plugins/license_management/__jest__/request_trial_extension.test.js (96%) rename x-pack/{legacy => }/plugins/license_management/__jest__/revert_to_basic.test.js (94%) rename x-pack/{legacy => }/plugins/license_management/__jest__/start_trial.test.js (96%) rename x-pack/{legacy => }/plugins/license_management/__jest__/upload_license.test.tsx (55%) rename x-pack/{legacy => }/plugins/license_management/__jest__/util/index.js (100%) rename x-pack/{legacy => }/plugins/license_management/__jest__/util/util.js (60%) rename x-pack/{legacy => }/plugins/license_management/__mocks__/focus-trap-react.js (100%) rename x-pack/{legacy => }/plugins/license_management/common/constants/base_path.ts (87%) rename x-pack/{legacy => }/plugins/license_management/common/constants/external_links.ts (100%) rename x-pack/{legacy => }/plugins/license_management/common/constants/index.ts (87%) rename x-pack/{legacy => }/plugins/license_management/common/constants/permissions.ts (100%) rename x-pack/{legacy => }/plugins/license_management/common/constants/plugin.ts (79%) create mode 100644 x-pack/plugins/license_management/kibana.json rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/_license_management.scss (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/app.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/app.js (97%) create mode 100644 x-pack/plugins/license_management/public/application/app_context.tsx create mode 100644 x-pack/plugins/license_management/public/application/app_providers.tsx create mode 100644 x-pack/plugins/license_management/public/application/breadcrumbs.ts rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/components/telemetry_opt_in/index.ts (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/components/telemetry_opt_in/telemetry_opt_in.tsx (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/index.scss (63%) create mode 100644 x-pack/plugins/license_management/public/application/index.tsx rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/lib/es.ts (79%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/lib/telemetry.ts (69%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/add_license/add_license.js (94%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/add_license/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/license_dashboard.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/license_dashboard.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/license_status/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/license_status/license_status.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/license_status/license_status.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/request_trial_extension/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/request_trial_extension/request_trial_extension.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/request_trial_extension/request_trial_extension.js (96%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/revert_to_basic/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/revert_to_basic/revert_to_basic.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/revert_to_basic/revert_to_basic.js (98%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/start_trial/index.ts (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/start_trial/start_trial.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/license_dashboard/start_trial/start_trial.tsx (92%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/upload_license/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/upload_license/upload_license.container.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/sections/upload_license/upload_license.js (99%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/add_error_message.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/add_license.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/permissions.js (100%) create mode 100644 x-pack/plugins/license_management/public/application/store/actions/set_breadcrumb.ts rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/start_basic.js (95%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/start_trial.js (85%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/actions/upload_license.js (96%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/index.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/license.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/license_management.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/permissions.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/start_basic_license_status.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/trial_status.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/upload_error_message.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/reducers/upload_status.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/application/store/store.js (100%) rename x-pack/{legacy/plugins/license_management/public/np_ready => plugins/license_management/public}/index.ts (86%) create mode 100644 x-pack/plugins/license_management/public/plugin.ts rename x-pack/{legacy/plugins/license_management/public/legacy.ts => plugins/license_management/public/types.ts} (78%) create mode 100644 x-pack/plugins/license_management/server/config.ts rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/index.ts (57%) rename x-pack/{legacy/plugins/license_management/public/np_ready/application/lib/docs_links.ts => plugins/license_management/server/lib/is_es_error.ts} (55%) rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/lib/license.ts (50%) rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/lib/permissions.ts (53%) rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/lib/start_basic.ts (50%) create mode 100644 x-pack/plugins/license_management/server/lib/start_trial.ts create mode 100644 x-pack/plugins/license_management/server/plugin.ts rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/routes/api/license/index.ts (100%) rename x-pack/{legacy/plugins/license_management/server/np_ready => plugins/license_management/server}/routes/api/license/register_license_route.ts (50%) create mode 100644 x-pack/plugins/license_management/server/routes/api/license/register_permissions_route.ts create mode 100644 x-pack/plugins/license_management/server/routes/api/license/register_start_basic_route.ts create mode 100644 x-pack/plugins/license_management/server/routes/api/license/register_start_trial_routes.ts rename x-pack/{legacy/plugins/license_management/public/np_ready/application/index.ts => plugins/license_management/server/routes/helpers.ts} (65%) create mode 100644 x-pack/plugins/license_management/server/routes/index.ts create mode 100644 x-pack/plugins/license_management/server/types.ts 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/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. -
    • -
    -
    -
    -
    -
    -
    - -
    - - - - - -`; - -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/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/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/license_management/public/np_ready/application/index.ts b/x-pack/plugins/license_management/server/routes/helpers.ts similarity index 65% rename from x-pack/legacy/plugins/license_management/public/np_ready/application/index.ts rename to x-pack/plugins/license_management/server/routes/helpers.ts index 1f963d7f8fcce..f1bbfd5fd4497 100644 --- a/x-pack/legacy/plugins/license_management/public/np_ready/application/index.ts +++ b/x-pack/plugins/license_management/server/routes/helpers.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './boot'; +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']; From 59a522b4ef862bda5d515da3d9e47c3d82995425 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 18 Mar 2020 14:27:56 +0100 Subject: [PATCH 34/35] Upgrade @types/node to match Node.js runtime (#60368) Kibana uses Node.js v10.19.0. The closest version of @types/node to this version is currently v10.17.17. This commit updates the resolutions field in package.json to ensure that the latest version less than 10.20.0 is always used. --- package.json | 4 ++-- packages/kbn-dev-utils/src/run/run.ts | 8 +++++++- packages/kbn-pm/dist/index.js | 5 ++++- packages/kbn-pm/package.json | 2 +- packages/kbn-test/src/functional_test_runner/cli.ts | 7 ++++++- x-pack/package.json | 4 ++-- yarn.lock | 8 ++++---- 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 583e99158da72..aa9c8f6c40160 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", @@ -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-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 16fc0d891185f..9fab74ea47a87 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -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) => { diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 278fdbd2bc9a4..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", 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/x-pack/package.json b/x-pack/package.json index 6f15b46e28f53..192ecd25b582c 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", diff --git a/yarn.lock b/yarn.lock index eaee706101a7b..b4945cc3f4100 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4894,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" From 95a42ed2c9d5c5726410eb7f0a0fe602a7d37e3a Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 18 Mar 2020 14:43:30 +0100 Subject: [PATCH 35/35] [Uptime] replace fetch with kibana http (#59881) * use kibana http * unused import * fix type * update type * refactor * fix types * fix type * fix type --- .../uptime/common/constants/rest_api.ts | 12 +- .../connected/charts/snapshot_container.tsx | 16 +-- .../monitor/list_drawer_container.tsx | 10 +- .../monitor/status_bar_container.tsx | 10 +- .../monitor/status_details_container.tsx | 10 +- .../parameterize_values.test.ts.snap | 5 - .../lib/helper/__tests__/get_api_path.test.ts | 24 ---- .../__tests__/parameterize_values.test.ts | 30 ----- .../uptime/public/lib/helper/get_api_path.ts | 8 -- .../plugins/uptime/public/lib/helper/index.ts | 2 - .../public/lib/helper/parameterize_values.ts | 16 --- .../plugins/uptime/public/pages/monitor.tsx | 4 +- .../uptime/public/state/actions/monitor.ts | 113 +++--------------- .../public/state/actions/monitor_status.ts | 13 +- .../uptime/public/state/actions/snapshot.ts | 52 +------- .../__snapshots__/snapshot.test.ts.snap | 6 +- .../state/api/__tests__/snapshot.test.ts | 72 ++++++----- .../uptime/public/state/api/index_pattern.ts | 17 +-- .../uptime/public/state/api/index_status.ts | 26 +--- .../uptime/public/state/api/monitor.ts | 47 ++------ .../public/state/api/monitor_duration.ts | 23 +--- .../uptime/public/state/api/monitor_status.ts | 33 ++--- .../public/state/api/overview_filters.ts | 44 ++----- .../plugins/uptime/public/state/api/ping.ts | 17 +-- .../uptime/public/state/api/snapshot.ts | 28 ++--- .../plugins/uptime/public/state/api/types.ts | 3 +- .../plugins/uptime/public/state/api/utils.ts | 80 +++++++++++++ .../public/state/effects/fetch_effect.ts | 21 ++-- .../uptime/public/state/effects/monitor.ts | 64 ++++------ .../public/state/effects/monitor_status.ts | 68 ++++------- .../uptime/public/state/effects/snapshot.ts | 14 ++- .../uptime/public/state/kibana_service.ts | 34 ++++++ .../state/reducers/__tests__/snapshot.test.ts | 54 ++++----- .../uptime/public/state/reducers/monitor.ts | 28 ++--- .../public/state/reducers/monitor_status.ts | 24 ++-- .../public/state/reducers/overview_filters.ts | 1 + .../uptime/public/state/reducers/snapshot.ts | 16 +-- .../uptime/public/state/selectors/index.ts | 4 +- .../plugins/uptime/public/uptime_app.tsx | 3 + .../lib/requests/get_snapshot_counts.ts | 14 +-- .../rest_api/index_state/get_index_pattern.ts | 3 +- .../rest_api/index_state/get_index_status.ts | 4 +- .../rest_api/monitors/monitor_locations.ts | 3 +- .../rest_api/monitors/monitors_details.ts | 3 +- .../rest_api/monitors/monitors_durations.ts | 4 +- .../uptime/server/rest_api/monitors/status.ts | 7 +- .../overview_filters/get_overview_filters.ts | 3 +- .../uptime/server/rest_api/pings/get_all.ts | 3 +- .../rest_api/pings/get_ping_histogram.ts | 3 +- .../uptime/server/rest_api/pings/get_pings.ts | 3 +- .../rest_api/snapshot/get_snapshot_count.ts | 3 +- .../apis/uptime/feature_controls.ts | 4 +- .../apis/uptime/rest/doc_count.ts | 4 +- 53 files changed, 443 insertions(+), 670 deletions(-) delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/__snapshots__/parameterize_values.test.ts.snap delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/get_api_path.test.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/__tests__/parameterize_values.test.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/parameterize_values.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/api/utils.ts create mode 100644 x-pack/legacy/plugins/uptime/public/state/kibana_service.ts 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/get_api_path.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts deleted file mode 100644 index 398d58f8460ba..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/get_api_path.ts +++ /dev/null @@ -1,8 +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 getApiPath = (path: string, basePath?: string) => - basePath ? `${basePath}${path}` : path; 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/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/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'); });