diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 2cdc6d1c297c..0d9f5c9d9245 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -37,22 +37,31 @@ pipeline { deleteDir() gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: false, shallow: false, reference: "/var/lib/jenkins/.git-references/kibana.git") + + // Filter when to run based on the below reasons: + // - On a PRs when: + // - There are changes related to the APM UI project + // - only when the owners of those changes are members of the apm-ui team (new filter) + // - On merges to branches when: + // - There are changes related to the APM UI project + // - FORCE parameter is set to true. script { + def apm_updated = false dir("${BASE_DIR}"){ - def regexps =[ "^x-pack/plugins/apm/.*" ] - env.APM_UPDATED = isGitRegionMatch(patterns: regexps) + apm_updated = isGitRegionMatch(patterns: [ "^x-pack/plugins/apm/.*" ]) + } + if (isPR()) { + def isMember = isMemberOf(user: env.CHANGE_AUTHOR, team: 'apm-ui') + setEnvVar('RUN_APM_E2E', params.FORCE || (apm_updated && isMember)) + } else { + setEnvVar('RUN_APM_E2E', params.FORCE || apm_updated) } } } } stage('Prepare Kibana') { options { skipDefaultCheckout() } - when { - anyOf { - expression { return params.FORCE } - expression { return env.APM_UPDATED != "false" } - } - } + when { expression { return env.RUN_APM_E2E != "false" } } environment { JENKINS_NODE_COOKIE = 'dontKillMe' } @@ -70,12 +79,7 @@ pipeline { } stage('Smoke Tests'){ options { skipDefaultCheckout() } - when { - anyOf { - expression { return params.FORCE } - expression { return env.APM_UPDATED != "false" } - } - } + when { expression { return env.RUN_APM_E2E != "false" } } steps{ notifyTestStatus('Running smoke tests', 'PENDING') dir("${BASE_DIR}"){ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bcb477447584..5efbaba32e00 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -169,6 +169,7 @@ /x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security /x-pack/plugins/security/ @elastic/kibana-security /x-pack/test/api_integration/apis/security/ @elastic/kibana-security +/x-pack/test/ui_capabilities/ @elastic/kibana-security /x-pack/test/encrypted_saved_objects_api_integration/ @elastic/kibana-security /x-pack/test/functional/apps/security/ @elastic/kibana-security /x-pack/test/kerberos_api_integration/ @elastic/kibana-security diff --git a/.github/ISSUE_TEMPLATE/APM.md b/.github/ISSUE_TEMPLATE/APM.md index 983806f70bc3..c3abbdd67269 100644 --- a/.github/ISSUE_TEMPLATE/APM.md +++ b/.github/ISSUE_TEMPLATE/APM.md @@ -2,7 +2,7 @@ name: APM Issue about: Issues related to the APM solution in Kibana labels: Team:apm -title: [APM] +title: "[APM]" --- **Versions** diff --git a/docs/developer/architecture/security/feature-registration.asciidoc b/docs/developer/architecture/security/feature-registration.asciidoc index 3724624dbb91..3ff83e9db8c4 100644 --- a/docs/developer/architecture/security/feature-registration.asciidoc +++ b/docs/developer/architecture/security/feature-registration.asciidoc @@ -9,13 +9,12 @@ Registering features also gives your plugin access to “UI Capabilities”. The === Registering a feature -Feature registration is controlled via the built-in `xpack_main` plugin. To register a feature, call `xpack_main`'s `registerFeature` function from your plugin's `init` function, and provide the appropriate details: +Feature registration is controlled via the built-in `features` plugin. To register a feature, call `features`'s `registerKibanaFeature` function from your plugin's `setup` lifecycle function, and provide the appropriate details: ["source","javascript"] ----------- -init(server) { - const xpackMainPlugin = server.plugins.xpack_main; - xpackMainPlugin.registerFeature({ +setup(core, { features }) { + features.registerKibanaFeature({ // feature details here. }); } @@ -45,12 +44,12 @@ Registering a feature consists of the following fields. For more information, co |An array of applications this feature enables. Typically, all of your plugin's apps (from `uiExports`) will be included here. |`privileges` (required) -|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`]. +|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`KibanaFeatureConfig`]. |See <> and <> |The set of privileges this feature requires to function. |`subFeatures` (optional) -|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`]. +|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`KibanaFeatureConfig`]. |See <> |The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher. @@ -73,15 +72,17 @@ For a full explanation of fields and options, consult the {kib-repo}blob/{branch === Using UI Capabilities UI Capabilities are available to your public (client) plugin code. These capabilities are read-only, and are used to inform the UI. This object is namespaced by feature id. For example, if your feature id is “foo”, then your UI Capabilities are stored at `uiCapabilities.foo`. -To access capabilities, import them from `ui/capabilities`: +Capabilities can be accessed from your plugin's `start` lifecycle from the `core.application` service: ["source","javascript"] ----------- -import { uiCapabilities } from 'ui/capabilities'; +public start(core) { + const { capabilities } = core.application; -const canUserSave = uiCapabilities.foo.save; -if (canUserSave) { - // show save button + const canUserSave = capabilities.foo.save; + if (canUserSave) { + // show save button + } } ----------- @@ -89,9 +90,8 @@ if (canUserSave) { === Example 1: Canvas Application ["source","javascript"] ----------- -init(server) { - const xpackMainPlugin = server.plugins.xpack_main; - xpackMainPlugin.registerFeature({ +public setup(core, { features }) { + features.registerKibanaFeature({ id: 'canvas', name: 'Canvas', icon: 'canvasApp', @@ -130,11 +130,13 @@ The `all` privilege defines a single “save” UI Capability. To access this in ["source","javascript"] ----------- -import { uiCapabilities } from 'ui/capabilities'; +public start(core) { + const { capabilities } = core.application; -const canUserSave = uiCapabilities.canvas.save; -if (canUserSave) { - // show save button + const canUserSave = capabilities.canvas.save; + if (canUserSave) { + // show save button + } } ----------- @@ -145,9 +147,8 @@ Because the `read` privilege does not define the `save` capability, users with r ["source","javascript"] ----------- -init(server) { - const xpackMainPlugin = server.plugins.xpack_main; - xpackMainPlugin.registerFeature({ +public setup(core, { features }) { + features.registerKibanaFeature({ id: 'dev_tools', name: i18n.translate('xpack.features.devToolsFeatureName', { defaultMessage: 'Dev Tools', @@ -206,9 +207,8 @@ a single "Create Short URLs" subfeature privilege is defined, which allows users ["source","javascript"] ----------- -init(server) { - const xpackMainPlugin = server.plugins.xpack_main; - xpackMainPlugin.registerFeature({ +public setup(core, { features }) { + features.registerKibanaFeature({ { id: 'discover', name: i18n.translate('xpack.features.discoverFeatureName', { diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index b3180a7a0387..275fdf8fb69a 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -95,7 +95,7 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel. |{kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy] -|This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. +|This plugin contains several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. |{kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact] @@ -172,6 +172,10 @@ which also contains the timelion APIs and backend, look at the vis_type_timelion |An API for: +|{kib-repo}blob/{branch}/src/plugins/url_forwarding/README.md[urlForwarding] +|This plugins contains helpers to redirect legacy URLs. It can be used to forward old URLs to their new counterparts. + + |{kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection] |Usage Collection allows collecting usage data for other services to consume (telemetry and monitoring). To integrate with the telemetry services for usage collection of your feature, there are 2 steps: @@ -412,10 +416,6 @@ using the CURL scripts in the scripts folder. |This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI. -|{kib-repo}blob/{branch}/x-pack/plugins/oss_telemetry[ossTelemetry] -|WARNING: Missing README. - - |{kib-repo}blob/{branch}/x-pack/plugins/painless_lab[painlessLab] |WARNING: Missing README. diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 85e1da08b00a..f7b55b0650d8 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -10,6 +10,9 @@ readonly links: { readonly dashboard: { readonly drilldowns: string; + readonly drilldownsTriggerPicker: string; + readonly urlDrilldownTemplateSyntax: string; + readonly urlDrilldownVariables: string; }; readonly filebeat: { readonly base: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 4644dc432bc9..3f58cf08ee6b 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly drilldowns: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly visualize: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly visualize: Record<string, string>;
} | | diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index dfffdffb08a0..c16600d1d049 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -28,6 +28,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) | | | [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) | | | [SavedObjectsSerializer](./kibana-plugin-core-server.savedobjectsserializer.md) | A serializer that can be used to manually convert [raw](./kibana-plugin-core-server.savedobjectsrawdoc.md) or [sanitized](./kibana-plugin-core-server.savedobjectsanitizeddoc.md) documents to the other kind. | +| [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) | | | [SavedObjectTypeRegistry](./kibana-plugin-core-server.savedobjecttyperegistry.md) | Registry holding information about all the registered [saved object types](./kibana-plugin-core-server.savedobjectstype.md). | ## Enumerations diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.md index e079e0fa51aa..d71eda600928 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.md @@ -17,5 +17,6 @@ export interface SavedObjectsBulkUpdateObject extends PickPartial<T> | The data for a Saved Object is stored as an object in the attributes property. | | [id](./kibana-plugin-core-server.savedobjectsbulkupdateobject.id.md) | string | The ID of this Saved Object, guaranteed to be unique for all objects of the same type | +| [namespace](./kibana-plugin-core-server.savedobjectsbulkupdateobject.namespace.md) | string | Optional namespace string to use when searching for this object. If this is defined, it will supersede the namespace ID that is in [SavedObjectsBulkUpdateOptions](./kibana-plugin-core-server.savedobjectsbulkupdateoptions.md).Note: the default namespace's string representation is 'default', and its ID representation is undefined. | | [type](./kibana-plugin-core-server.savedobjectsbulkupdateobject.type.md) | string | The type of this Saved Object. Each plugin can define it's own custom Saved Object types. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.namespace.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.namespace.md new file mode 100644 index 000000000000..544efcd3be90 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkupdateobject.namespace.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsBulkUpdateObject](./kibana-plugin-core-server.savedobjectsbulkupdateobject.md) > [namespace](./kibana-plugin-core-server.savedobjectsbulkupdateobject.namespace.md) + +## SavedObjectsBulkUpdateObject.namespace property + +Optional namespace string to use when searching for this object. If this is defined, it will supersede the namespace ID that is in [SavedObjectsBulkUpdateOptions](./kibana-plugin-core-server.savedobjectsbulkupdateoptions.md). + +Note: the default namespace's string representation is `'default'`, and its ID representation is `undefined`. + +Signature: + +```typescript +namespace?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md new file mode 100644 index 000000000000..e365dfbcb514 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) + +## SavedObjectsUtils class + + +Signature: + +```typescript +export declare class SavedObjectsUtils +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [namespaceIdToString](./kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md) | static | (namespace?: string | undefined) => string | Converts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with the exception of the undefined namespace ID (which has a namespace string of 'default'). | +| [namespaceStringToId](./kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md) | static | (namespace: string) => string | undefined | Converts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with the exception of the 'default' namespace string (which has a namespace ID of undefined). | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md new file mode 100644 index 000000000000..591505892e64 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) > [namespaceIdToString](./kibana-plugin-core-server.savedobjectsutils.namespaceidtostring.md) + +## SavedObjectsUtils.namespaceIdToString property + +Converts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with the exception of the `undefined` namespace ID (which has a namespace string of `'default'`). + +Signature: + +```typescript +static namespaceIdToString: (namespace?: string | undefined) => string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md new file mode 100644 index 000000000000..e052fe493b5e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsUtils](./kibana-plugin-core-server.savedobjectsutils.md) > [namespaceStringToId](./kibana-plugin-core-server.savedobjectsutils.namespacestringtoid.md) + +## SavedObjectsUtils.namespaceStringToId property + +Converts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with the exception of the `'default'` namespace string (which has a namespace ID of `undefined`). + +Signature: + +```typescript +static namespaceStringToId: (namespace: string) => string | undefined; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig._constructor_.md new file mode 100644 index 000000000000..9287a08ff196 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [(constructor)](./kibana-plugin-plugins-data-public.aggconfig._constructor_.md) + +## AggConfig.(constructor) + +Constructs a new instance of the `AggConfig` class + +Signature: + +```typescript +constructor(aggConfigs: IAggConfigs, opts: AggConfigOptions); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| aggConfigs | IAggConfigs | | +| opts | AggConfigOptions | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.aggconfigs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.aggconfigs.md new file mode 100644 index 000000000000..f552bbd2d1cf --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.aggconfigs.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [aggConfigs](./kibana-plugin-plugins-data-public.aggconfig.aggconfigs.md) + +## AggConfig.aggConfigs property + +Signature: + +```typescript +aggConfigs: IAggConfigs; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.brandnew.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.brandnew.md new file mode 100644 index 000000000000..eb1f3af4c5b0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.brandnew.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [brandNew](./kibana-plugin-plugins-data-public.aggconfig.brandnew.md) + +## AggConfig.brandNew property + +Signature: + +```typescript +brandNew?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.createfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.createfilter.md new file mode 100644 index 000000000000..7ec0350f6532 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.createfilter.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [createFilter](./kibana-plugin-plugins-data-public.aggconfig.createfilter.md) + +## AggConfig.createFilter() method + +Signature: + +```typescript +createFilter(key: string, params?: {}): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| key | string | | +| params | {} | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.enabled.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.enabled.md new file mode 100644 index 000000000000..82595ee5f5b6 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.enabled.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [enabled](./kibana-plugin-plugins-data-public.aggconfig.enabled.md) + +## AggConfig.enabled property + +Signature: + +```typescript +enabled: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.ensureids.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.ensureids.md new file mode 100644 index 000000000000..04e0b82187a5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.ensureids.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [ensureIds](./kibana-plugin-plugins-data-public.aggconfig.ensureids.md) + +## AggConfig.ensureIds() method + +Ensure that all of the objects in the list have ids, the objects and list are modified by reference. + +Signature: + +```typescript +static ensureIds(list: any[]): any[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| list | any[] | | + +Returns: + +`any[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldistimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldistimefield.md new file mode 100644 index 000000000000..a1fde4dec25b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldistimefield.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [fieldIsTimeField](./kibana-plugin-plugins-data-public.aggconfig.fieldistimefield.md) + +## AggConfig.fieldIsTimeField() method + +Signature: + +```typescript +fieldIsTimeField(): boolean | "" | undefined; +``` +Returns: + +`boolean | "" | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldname.md new file mode 100644 index 000000000000..2d3acb7f026f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.fieldname.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [fieldName](./kibana-plugin-plugins-data-public.aggconfig.fieldname.md) + +## AggConfig.fieldName() method + +Signature: + +```typescript +fieldName(): any; +``` +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getaggparams.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getaggparams.md new file mode 100644 index 000000000000..f898844ff027 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getaggparams.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getAggParams](./kibana-plugin-plugins-data-public.aggconfig.getaggparams.md) + +## AggConfig.getAggParams() method + +Signature: + +```typescript +getAggParams(): import("./param_types/agg").AggParamType[]; +``` +Returns: + +`import("./param_types/agg").AggParamType[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfield.md new file mode 100644 index 000000000000..1fb6f88c4317 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfield.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getField](./kibana-plugin-plugins-data-public.aggconfig.getfield.md) + +## AggConfig.getField() method + +Signature: + +```typescript +getField(): any; +``` +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfielddisplayname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfielddisplayname.md new file mode 100644 index 000000000000..710499cee62d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getfielddisplayname.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getFieldDisplayName](./kibana-plugin-plugins-data-public.aggconfig.getfielddisplayname.md) + +## AggConfig.getFieldDisplayName() method + +Signature: + +```typescript +getFieldDisplayName(): any; +``` +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getindexpattern.md new file mode 100644 index 000000000000..ed0e9d0fbb5d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getindexpattern.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getIndexPattern](./kibana-plugin-plugins-data-public.aggconfig.getindexpattern.md) + +## AggConfig.getIndexPattern() method + +Signature: + +```typescript +getIndexPattern(): import("../../../public").IndexPattern; +``` +Returns: + +`import("../../../public").IndexPattern` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getkey.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getkey.md new file mode 100644 index 000000000000..a2a59fcf9ae3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getkey.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getKey](./kibana-plugin-plugins-data-public.aggconfig.getkey.md) + +## AggConfig.getKey() method + +Signature: + +```typescript +getKey(bucket: any, key?: string): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| bucket | any | | +| key | string | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getparam.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getparam.md new file mode 100644 index 000000000000..ad4cd2fa175f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getparam.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getParam](./kibana-plugin-plugins-data-public.aggconfig.getparam.md) + +## AggConfig.getParam() method + +Signature: + +```typescript +getParam(key: string): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| key | string | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getrequestaggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getrequestaggs.md new file mode 100644 index 000000000000..773c2f5a7c0e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getrequestaggs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getRequestAggs](./kibana-plugin-plugins-data-public.aggconfig.getrequestaggs.md) + +## AggConfig.getRequestAggs() method + +Signature: + +```typescript +getRequestAggs(): AggConfig[]; +``` +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getresponseaggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getresponseaggs.md new file mode 100644 index 000000000000..cf515e68dcc5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getresponseaggs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getResponseAggs](./kibana-plugin-plugins-data-public.aggconfig.getresponseaggs.md) + +## AggConfig.getResponseAggs() method + +Signature: + +```typescript +getResponseAggs(): AggConfig[]; +``` +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.gettimerange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.gettimerange.md new file mode 100644 index 000000000000..897a6d8dda3f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.gettimerange.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getTimeRange](./kibana-plugin-plugins-data-public.aggconfig.gettimerange.md) + +## AggConfig.getTimeRange() method + +Signature: + +```typescript +getTimeRange(): import("../../../public").TimeRange | undefined; +``` +Returns: + +`import("../../../public").TimeRange | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getvalue.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getvalue.md new file mode 100644 index 000000000000..4fab1af3f646 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.getvalue.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [getValue](./kibana-plugin-plugins-data-public.aggconfig.getvalue.md) + +## AggConfig.getValue() method + +Signature: + +```typescript +getValue(bucket: any): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| bucket | any | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.id.md new file mode 100644 index 000000000000..1fa7a5c57e2a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [id](./kibana-plugin-plugins-data-public.aggconfig.id.md) + +## AggConfig.id property + +Signature: + +```typescript +id: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.isfilterable.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.isfilterable.md new file mode 100644 index 000000000000..a795ab1e91c2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.isfilterable.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [isFilterable](./kibana-plugin-plugins-data-public.aggconfig.isfilterable.md) + +## AggConfig.isFilterable() method + +Signature: + +```typescript +isFilterable(): boolean; +``` +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.makelabel.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.makelabel.md new file mode 100644 index 000000000000..65923ed0ae88 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.makelabel.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [makeLabel](./kibana-plugin-plugins-data-public.aggconfig.makelabel.md) + +## AggConfig.makeLabel() method + +Signature: + +```typescript +makeLabel(percentageMode?: boolean): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| percentageMode | boolean | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.md new file mode 100644 index 000000000000..ceb90cffbf6c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.md @@ -0,0 +1,62 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) + +## AggConfig class + +Signature: + +```typescript +export declare class AggConfig +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(aggConfigs, opts)](./kibana-plugin-plugins-data-public.aggconfig._constructor_.md) | | Constructs a new instance of the AggConfig class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [aggConfigs](./kibana-plugin-plugins-data-public.aggconfig.aggconfigs.md) | | IAggConfigs | | +| [brandNew](./kibana-plugin-plugins-data-public.aggconfig.brandnew.md) | | boolean | | +| [enabled](./kibana-plugin-plugins-data-public.aggconfig.enabled.md) | | boolean | | +| [id](./kibana-plugin-plugins-data-public.aggconfig.id.md) | | string | | +| [params](./kibana-plugin-plugins-data-public.aggconfig.params.md) | | any | | +| [parent](./kibana-plugin-plugins-data-public.aggconfig.parent.md) | | IAggConfigs | | +| [schema](./kibana-plugin-plugins-data-public.aggconfig.schema.md) | | string | | +| [type](./kibana-plugin-plugins-data-public.aggconfig.type.md) | | IAggType | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [createFilter(key, params)](./kibana-plugin-plugins-data-public.aggconfig.createfilter.md) | | | +| [ensureIds(list)](./kibana-plugin-plugins-data-public.aggconfig.ensureids.md) | static | Ensure that all of the objects in the list have ids, the objects and list are modified by reference. | +| [fieldIsTimeField()](./kibana-plugin-plugins-data-public.aggconfig.fieldistimefield.md) | | | +| [fieldName()](./kibana-plugin-plugins-data-public.aggconfig.fieldname.md) | | | +| [getAggParams()](./kibana-plugin-plugins-data-public.aggconfig.getaggparams.md) | | | +| [getField()](./kibana-plugin-plugins-data-public.aggconfig.getfield.md) | | | +| [getFieldDisplayName()](./kibana-plugin-plugins-data-public.aggconfig.getfielddisplayname.md) | | | +| [getIndexPattern()](./kibana-plugin-plugins-data-public.aggconfig.getindexpattern.md) | | | +| [getKey(bucket, key)](./kibana-plugin-plugins-data-public.aggconfig.getkey.md) | | | +| [getParam(key)](./kibana-plugin-plugins-data-public.aggconfig.getparam.md) | | | +| [getRequestAggs()](./kibana-plugin-plugins-data-public.aggconfig.getrequestaggs.md) | | | +| [getResponseAggs()](./kibana-plugin-plugins-data-public.aggconfig.getresponseaggs.md) | | | +| [getTimeRange()](./kibana-plugin-plugins-data-public.aggconfig.gettimerange.md) | | | +| [getValue(bucket)](./kibana-plugin-plugins-data-public.aggconfig.getvalue.md) | | | +| [isFilterable()](./kibana-plugin-plugins-data-public.aggconfig.isfilterable.md) | | | +| [makeLabel(percentageMode)](./kibana-plugin-plugins-data-public.aggconfig.makelabel.md) | | | +| [nextId(list)](./kibana-plugin-plugins-data-public.aggconfig.nextid.md) | static | Calculate the next id based on the ids in this list {array} list - a list of objects with id properties | +| [onSearchRequestStart(searchSource, options)](./kibana-plugin-plugins-data-public.aggconfig.onsearchrequeststart.md) | | Hook for pre-flight logic, see AggType\#onSearchRequestStart | +| [serialize()](./kibana-plugin-plugins-data-public.aggconfig.serialize.md) | | | +| [setParams(from)](./kibana-plugin-plugins-data-public.aggconfig.setparams.md) | | Write the current values to this.params, filling in the defaults as we go | +| [setType(type)](./kibana-plugin-plugins-data-public.aggconfig.settype.md) | | | +| [toDsl(aggConfigs)](./kibana-plugin-plugins-data-public.aggconfig.todsl.md) | | Convert this aggConfig to its dsl syntax.Adds params and adhoc subaggs to a pojo, then returns it | +| [toExpressionAst()](./kibana-plugin-plugins-data-public.aggconfig.toexpressionast.md) | | | +| [toJSON()](./kibana-plugin-plugins-data-public.aggconfig.tojson.md) | | | +| [toSerializedFieldFormat()](./kibana-plugin-plugins-data-public.aggconfig.toserializedfieldformat.md) | | Returns a serialized field format for the field used in this agg. This can be passed to fieldFormats.deserialize to get the field format instance. | +| [write(aggs)](./kibana-plugin-plugins-data-public.aggconfig.write.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.nextid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.nextid.md new file mode 100644 index 000000000000..ab524a6d1c4f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.nextid.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [nextId](./kibana-plugin-plugins-data-public.aggconfig.nextid.md) + +## AggConfig.nextId() method + +Calculate the next id based on the ids in this list + + {array} list - a list of objects with id properties + +Signature: + +```typescript +static nextId(list: IAggConfig[]): number; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| list | IAggConfig[] | | + +Returns: + +`number` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.onsearchrequeststart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.onsearchrequeststart.md new file mode 100644 index 000000000000..81df7866560e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.onsearchrequeststart.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [onSearchRequestStart](./kibana-plugin-plugins-data-public.aggconfig.onsearchrequeststart.md) + +## AggConfig.onSearchRequestStart() method + +Hook for pre-flight logic, see AggType\#onSearchRequestStart + +Signature: + +```typescript +onSearchRequestStart(searchSource: ISearchSource, options?: ISearchOptions): Promise | Promise; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| searchSource | ISearchSource | | +| options | ISearchOptions | | + +Returns: + +`Promise | Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.params.md new file mode 100644 index 000000000000..5bdb67f53b51 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.params.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [params](./kibana-plugin-plugins-data-public.aggconfig.params.md) + +## AggConfig.params property + +Signature: + +```typescript +params: any; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.parent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.parent.md new file mode 100644 index 000000000000..53d028457a9a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.parent.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [parent](./kibana-plugin-plugins-data-public.aggconfig.parent.md) + +## AggConfig.parent property + +Signature: + +```typescript +parent?: IAggConfigs; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.schema.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.schema.md new file mode 100644 index 000000000000..afbf68595135 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.schema.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [schema](./kibana-plugin-plugins-data-public.aggconfig.schema.md) + +## AggConfig.schema property + +Signature: + +```typescript +schema?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.serialize.md new file mode 100644 index 000000000000..b0eebdbcc11e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.serialize.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [serialize](./kibana-plugin-plugins-data-public.aggconfig.serialize.md) + +## AggConfig.serialize() method + +Signature: + +```typescript +serialize(): AggConfigSerialized; +``` +Returns: + +`AggConfigSerialized` + +Returns a serialized representation of an AggConfig. + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.setparams.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.setparams.md new file mode 100644 index 000000000000..cb495b7653f8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.setparams.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [setParams](./kibana-plugin-plugins-data-public.aggconfig.setparams.md) + +## AggConfig.setParams() method + +Write the current values to this.params, filling in the defaults as we go + +Signature: + +```typescript +setParams(from: any): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| from | any | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.settype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.settype.md new file mode 100644 index 000000000000..0b07186a6ca3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.settype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [setType](./kibana-plugin-plugins-data-public.aggconfig.settype.md) + +## AggConfig.setType() method + +Signature: + +```typescript +setType(type: IAggType): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | IAggType | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.todsl.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.todsl.md new file mode 100644 index 000000000000..ac655c2a88a7 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.todsl.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [toDsl](./kibana-plugin-plugins-data-public.aggconfig.todsl.md) + +## AggConfig.toDsl() method + +Convert this aggConfig to its dsl syntax. + +Adds params and adhoc subaggs to a pojo, then returns it + +Signature: + +```typescript +toDsl(aggConfigs?: IAggConfigs): any; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| aggConfigs | IAggConfigs | | + +Returns: + +`any` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toexpressionast.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toexpressionast.md new file mode 100644 index 000000000000..99001e81fde4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toexpressionast.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [toExpressionAst](./kibana-plugin-plugins-data-public.aggconfig.toexpressionast.md) + +## AggConfig.toExpressionAst() method + +Signature: + +```typescript +toExpressionAst(): ExpressionAstFunction | undefined; +``` +Returns: + +`ExpressionAstFunction | undefined` + +Returns an ExpressionAst representing the function for this agg type. + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md new file mode 100644 index 000000000000..aa639aa57407 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.tojson.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [toJSON](./kibana-plugin-plugins-data-public.aggconfig.tojson.md) + +## AggConfig.toJSON() method + +> Warning: This API is now obsolete. +> +> - Use serialize() instead. +> + +Signature: + +```typescript +toJSON(): AggConfigSerialized; +``` +Returns: + +`AggConfigSerialized` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toserializedfieldformat.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toserializedfieldformat.md new file mode 100644 index 000000000000..7a75950f9cc6 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.toserializedfieldformat.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [toSerializedFieldFormat](./kibana-plugin-plugins-data-public.aggconfig.toserializedfieldformat.md) + +## AggConfig.toSerializedFieldFormat() method + +Returns a serialized field format for the field used in this agg. This can be passed to fieldFormats.deserialize to get the field format instance. + +Signature: + +```typescript +toSerializedFieldFormat(): {} | Ensure, SerializableState>; +``` +Returns: + +`{} | Ensure, SerializableState>` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.type.md new file mode 100644 index 000000000000..9dc44caee42e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.type.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [type](./kibana-plugin-plugins-data-public.aggconfig.type.md) + +## AggConfig.type property + +Signature: + +```typescript +get type(): IAggType; + +set type(type: IAggType); +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.write.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.write.md new file mode 100644 index 000000000000..f98394b57cac --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfig.write.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) > [write](./kibana-plugin-plugins-data-public.aggconfig.write.md) + +## AggConfig.write() method + +Signature: + +```typescript +write(aggs?: IAggConfigs): Record; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| aggs | IAggConfigs | | + +Returns: + +`Record` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md new file mode 100644 index 000000000000..c9e08b971248 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md @@ -0,0 +1,32 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [(constructor)](./kibana-plugin-plugins-data-public.aggconfigs._constructor_.md) + +## AggConfigs.(constructor) + +Constructs a new instance of the `AggConfigs` class + +Signature: + +```typescript +constructor(indexPattern: IndexPattern, configStates: Pick & Pick<{ + type: string | IAggType; + }, "type"> & Pick<{ + type: string | IAggType; + }, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined, opts: AggConfigsOptions); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| indexPattern | IndexPattern | | +| configStates | Pick<Pick<{
type: string;
enabled?: boolean | undefined;
id?: string | undefined;
params?: {} | import("./agg_config").SerializableState | undefined;
schema?: string | undefined;
}, "enabled" | "schema" | "id" | "params"> & Pick<{
type: string | IAggType;
}, "type"> & Pick<{
type: string | IAggType;
}, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined | | +| opts | AggConfigsOptions | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.aggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.aggs.md new file mode 100644 index 000000000000..0d217e037ecb --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.aggs.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [aggs](./kibana-plugin-plugins-data-public.aggconfigs.aggs.md) + +## AggConfigs.aggs property + +Signature: + +```typescript +aggs: IAggConfig[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byid.md new file mode 100644 index 000000000000..14d65ada5e39 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byid.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [byId](./kibana-plugin-plugins-data-public.aggconfigs.byid.md) + +## AggConfigs.byId() method + +Signature: + +```typescript +byId(id: string): AggConfig | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`AggConfig | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byindex.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byindex.md new file mode 100644 index 000000000000..5977c81ddaf3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byindex.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [byIndex](./kibana-plugin-plugins-data-public.aggconfigs.byindex.md) + +## AggConfigs.byIndex() method + +Signature: + +```typescript +byIndex(index: number): AggConfig; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| index | number | | + +Returns: + +`AggConfig` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byname.md new file mode 100644 index 000000000000..772ba1f074d0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byname.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [byName](./kibana-plugin-plugins-data-public.aggconfigs.byname.md) + +## AggConfigs.byName() method + +Signature: + +```typescript +byName(name: string): AggConfig[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byschemaname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byschemaname.md new file mode 100644 index 000000000000..3a7c6a5f89e1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.byschemaname.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [bySchemaName](./kibana-plugin-plugins-data-public.aggconfigs.byschemaname.md) + +## AggConfigs.bySchemaName() method + +Signature: + +```typescript +bySchemaName(schema: string): AggConfig[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| schema | string | | + +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytype.md new file mode 100644 index 000000000000..8bbf85ce4f29 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytype.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [byType](./kibana-plugin-plugins-data-public.aggconfigs.bytype.md) + +## AggConfigs.byType() method + +Signature: + +```typescript +byType(type: string): AggConfig[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | + +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytypename.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytypename.md new file mode 100644 index 000000000000..97f05837493f --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.bytypename.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [byTypeName](./kibana-plugin-plugins-data-public.aggconfigs.bytypename.md) + +## AggConfigs.byTypeName() method + +Signature: + +```typescript +byTypeName(type: string): AggConfig[]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | + +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.clone.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.clone.md new file mode 100644 index 000000000000..0206f3c6b475 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.clone.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [clone](./kibana-plugin-plugins-data-public.aggconfigs.clone.md) + +## AggConfigs.clone() method + +Signature: + +```typescript +clone({ enabledOnly }?: { + enabledOnly?: boolean | undefined; + }): AggConfigs; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| { enabledOnly } | {
enabledOnly?: boolean | undefined;
} | | + +Returns: + +`AggConfigs` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md new file mode 100644 index 000000000000..2ccded7c74e4 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [createAggConfig](./kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md) + +## AggConfigs.createAggConfig property + +Signature: + +```typescript +createAggConfig: (params: CreateAggConfigParams, { addToAggConfigs }?: { + addToAggConfigs?: boolean | undefined; + }) => T; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getall.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getall.md new file mode 100644 index 000000000000..091ec1ce416c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getall.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [getAll](./kibana-plugin-plugins-data-public.aggconfigs.getall.md) + +## AggConfigs.getAll() method + +Signature: + +```typescript +getAll(): AggConfig[]; +``` +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggbyid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggbyid.md new file mode 100644 index 000000000000..f375648ca1cb --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggbyid.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [getRequestAggById](./kibana-plugin-plugins-data-public.aggconfigs.getrequestaggbyid.md) + +## AggConfigs.getRequestAggById() method + +Signature: + +```typescript +getRequestAggById(id: string): AggConfig | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`AggConfig | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggs.md new file mode 100644 index 000000000000..f4db6e373f5c --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getrequestaggs.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [getRequestAggs](./kibana-plugin-plugins-data-public.aggconfigs.getrequestaggs.md) + +## AggConfigs.getRequestAggs() method + +Signature: + +```typescript +getRequestAggs(): AggConfig[]; +``` +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggbyid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggbyid.md new file mode 100644 index 000000000000..ab31c74f6000 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggbyid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [getResponseAggById](./kibana-plugin-plugins-data-public.aggconfigs.getresponseaggbyid.md) + +## AggConfigs.getResponseAggById() method + +Find a response agg by it's id. This may be an agg in the aggConfigs, or one created specifically for a response value + +Signature: + +```typescript +getResponseAggById(id: string): AggConfig | undefined; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| id | string | | + +Returns: + +`AggConfig | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggs.md new file mode 100644 index 000000000000..47e26bdea9e9 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.getresponseaggs.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [getResponseAggs](./kibana-plugin-plugins-data-public.aggconfigs.getresponseaggs.md) + +## AggConfigs.getResponseAggs() method + +Gets the AggConfigs (and possibly ResponseAggConfigs) that represent the values that will be produced when all aggs are run. + +With multi-value metric aggs it is possible for a single agg request to result in multiple agg values, which is why the length of a vis' responseValuesAggs may be different than the vis' aggs + + {array\[AggConfig\]} + +Signature: + +```typescript +getResponseAggs(): AggConfig[]; +``` +Returns: + +`AggConfig[]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md new file mode 100644 index 000000000000..9bd91e185df1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [indexPattern](./kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md) + +## AggConfigs.indexPattern property + +Signature: + +```typescript +indexPattern: IndexPattern; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md new file mode 100644 index 000000000000..d94c3959cd6a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [jsonDataEquals](./kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md) + +## AggConfigs.jsonDataEquals() method + +Data-by-data comparison of this Aggregation Ignores the non-array indexes + +Signature: + +```typescript +jsonDataEquals(aggConfigs: AggConfig[]): boolean; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| aggConfigs | AggConfig[] | | + +Returns: + +`boolean` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.md new file mode 100644 index 000000000000..c0ba1bbeea33 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.md @@ -0,0 +1,48 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) + +## AggConfigs class + +Signature: + +```typescript +export declare class AggConfigs +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(indexPattern, configStates, opts)](./kibana-plugin-plugins-data-public.aggconfigs._constructor_.md) | | Constructs a new instance of the AggConfigs class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [aggs](./kibana-plugin-plugins-data-public.aggconfigs.aggs.md) | | IAggConfig[] | | +| [createAggConfig](./kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md) | | <T extends AggConfig = AggConfig>(params: CreateAggConfigParams, { addToAggConfigs }?: {
addToAggConfigs?: boolean | undefined;
}) => T | | +| [indexPattern](./kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md) | | IndexPattern | | +| [timeRange](./kibana-plugin-plugins-data-public.aggconfigs.timerange.md) | | TimeRange | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [byId(id)](./kibana-plugin-plugins-data-public.aggconfigs.byid.md) | | | +| [byIndex(index)](./kibana-plugin-plugins-data-public.aggconfigs.byindex.md) | | | +| [byName(name)](./kibana-plugin-plugins-data-public.aggconfigs.byname.md) | | | +| [bySchemaName(schema)](./kibana-plugin-plugins-data-public.aggconfigs.byschemaname.md) | | | +| [byType(type)](./kibana-plugin-plugins-data-public.aggconfigs.bytype.md) | | | +| [byTypeName(type)](./kibana-plugin-plugins-data-public.aggconfigs.bytypename.md) | | | +| [clone({ enabledOnly })](./kibana-plugin-plugins-data-public.aggconfigs.clone.md) | | | +| [getAll()](./kibana-plugin-plugins-data-public.aggconfigs.getall.md) | | | +| [getRequestAggById(id)](./kibana-plugin-plugins-data-public.aggconfigs.getrequestaggbyid.md) | | | +| [getRequestAggs()](./kibana-plugin-plugins-data-public.aggconfigs.getrequestaggs.md) | | | +| [getResponseAggById(id)](./kibana-plugin-plugins-data-public.aggconfigs.getresponseaggbyid.md) | | Find a response agg by it's id. This may be an agg in the aggConfigs, or one created specifically for a response value | +| [getResponseAggs()](./kibana-plugin-plugins-data-public.aggconfigs.getresponseaggs.md) | | Gets the AggConfigs (and possibly ResponseAggConfigs) that represent the values that will be produced when all aggs are run.With multi-value metric aggs it is possible for a single agg request to result in multiple agg values, which is why the length of a vis' responseValuesAggs may be different than the vis' aggs {array\[AggConfig\]} | +| [jsonDataEquals(aggConfigs)](./kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md) | | Data-by-data comparison of this Aggregation Ignores the non-array indexes | +| [onSearchRequestStart(searchSource, options)](./kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md) | | | +| [setTimeRange(timeRange)](./kibana-plugin-plugins-data-public.aggconfigs.settimerange.md) | | | +| [toDsl(hierarchical)](./kibana-plugin-plugins-data-public.aggconfigs.todsl.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md new file mode 100644 index 000000000000..3ae7af408563 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [onSearchRequestStart](./kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md) + +## AggConfigs.onSearchRequestStart() method + +Signature: + +```typescript +onSearchRequestStart(searchSource: ISearchSource, options?: ISearchOptions): Promise<[unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown]>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| searchSource | ISearchSource | | +| options | ISearchOptions | | + +Returns: + +`Promise<[unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown]>` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.settimerange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.settimerange.md new file mode 100644 index 000000000000..77530f02bc9a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.settimerange.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [setTimeRange](./kibana-plugin-plugins-data-public.aggconfigs.settimerange.md) + +## AggConfigs.setTimeRange() method + +Signature: + +```typescript +setTimeRange(timeRange: TimeRange): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| timeRange | TimeRange | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.timerange.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.timerange.md new file mode 100644 index 000000000000..b4caef6c7f6d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.timerange.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [timeRange](./kibana-plugin-plugins-data-public.aggconfigs.timerange.md) + +## AggConfigs.timeRange property + +Signature: + +```typescript +timeRange?: TimeRange; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.todsl.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.todsl.md new file mode 100644 index 000000000000..055c4113ca3e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs.todsl.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) > [toDsl](./kibana-plugin-plugins-data-public.aggconfigs.todsl.md) + +## AggConfigs.toDsl() method + +Signature: + +```typescript +toDsl(hierarchical?: boolean): Record; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| hierarchical | boolean | | + +Returns: + +`Record` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggsstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggsstart.md new file mode 100644 index 000000000000..7bdf9d650120 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggsstart.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) + +## AggsStart type + +AggsStart represents the actual external contract as AggsCommonStart is only used internally. The difference is that AggsStart includes the typings for the registry with initialized agg types. + +Signature: + +```typescript +export declare type AggsStart = Assign; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.autocompletestart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.autocompletestart.md new file mode 100644 index 000000000000..44cee8c32421 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.autocompletestart.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) + +## AutocompleteStart type + +\* + +Signature: + +```typescript +export declare type AutocompleteStart = ReturnType; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.md index dba1d79e7868..fc5624aeddce 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginsetup.md @@ -4,6 +4,8 @@ ## DataPublicPluginSetup interface +Data plugin public Setup contract + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md index 25ce6eaa688f..10997c94fab0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md @@ -4,11 +4,10 @@ ## DataPublicPluginStart.actions property +filter creation utilities [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) + Signature: ```typescript -actions: { - createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; - createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; - }; +actions: DataPublicPluginStartActions; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md index d2e5aee7d90d..8a09a10cccb2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart.autocomplete property +autocomplete service [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md index dd4b38f64d10..344044b38f7d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart.fieldFormats property +field formats service [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md index b3dd6a61760a..0cf1e3101713 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart.indexPatterns property +index patterns service [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md index 4f43f10ce089..7bae0bca701b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart interface +Data plugin public Start contract + Signature: ```typescript @@ -14,11 +16,11 @@ export interface DataPublicPluginStart | Property | Type | Description | | --- | --- | --- | -| [actions](./kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md) | {
createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction;
createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction;
} | | -| [autocomplete](./kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md) | AutocompleteStart | | -| [fieldFormats](./kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md) | FieldFormatsStart | | -| [indexPatterns](./kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md) | IndexPatternsContract | | -| [query](./kibana-plugin-plugins-data-public.datapublicpluginstart.query.md) | QueryStart | | -| [search](./kibana-plugin-plugins-data-public.datapublicpluginstart.search.md) | ISearchStart | | -| [ui](./kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md) | {
IndexPatternSelect: React.ComponentType<IndexPatternSelectProps>;
SearchBar: React.ComponentType<StatefulSearchBarProps>;
} | | +| [actions](./kibana-plugin-plugins-data-public.datapublicpluginstart.actions.md) | DataPublicPluginStartActions | filter creation utilities [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) | +| [autocomplete](./kibana-plugin-plugins-data-public.datapublicpluginstart.autocomplete.md) | AutocompleteStart | autocomplete service [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) | +| [fieldFormats](./kibana-plugin-plugins-data-public.datapublicpluginstart.fieldformats.md) | FieldFormatsStart | field formats service [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | +| [indexPatterns](./kibana-plugin-plugins-data-public.datapublicpluginstart.indexpatterns.md) | IndexPatternsContract | index patterns service [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) | +| [query](./kibana-plugin-plugins-data-public.datapublicpluginstart.query.md) | QueryStart | query service [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | +| [search](./kibana-plugin-plugins-data-public.datapublicpluginstart.search.md) | ISearchStart | search service [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) | +| [ui](./kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md) | DataPublicPluginStartUi | prewired UI components [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.query.md index a44e250077ed..16ba5dafbb26 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.query.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.query.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart.query property +query service [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.search.md index eec00e7b13e9..98832d7ca11d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.search.md @@ -4,6 +4,8 @@ ## DataPublicPluginStart.search property +search service [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md index 9c2421683437..671a1814ac64 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstart.ui.md @@ -4,11 +4,10 @@ ## DataPublicPluginStart.ui property +prewired UI components [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) + Signature: ```typescript -ui: { - IndexPatternSelect: React.ComponentType; - SearchBar: React.ComponentType; - }; +ui: DataPublicPluginStartUi; ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromrangeselectaction.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromrangeselectaction.md new file mode 100644 index 000000000000..c954e0095cbb --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromrangeselectaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) > [createFiltersFromRangeSelectAction](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromrangeselectaction.md) + +## DataPublicPluginStartActions.createFiltersFromRangeSelectAction property + +Signature: + +```typescript +createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromvalueclickaction.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromvalueclickaction.md new file mode 100644 index 000000000000..70bd5091f360 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromvalueclickaction.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) > [createFiltersFromValueClickAction](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromvalueclickaction.md) + +## DataPublicPluginStartActions.createFiltersFromValueClickAction property + +Signature: + +```typescript +createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.md new file mode 100644 index 000000000000..d44c9e892cb8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartactions.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) + +## DataPublicPluginStartActions interface + +utilities to generate filters from action context + +Signature: + +```typescript +export interface DataPublicPluginStartActions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [createFiltersFromRangeSelectAction](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromrangeselectaction.md) | typeof createFiltersFromRangeSelectAction | | +| [createFiltersFromValueClickAction](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.createfiltersfromvalueclickaction.md) | typeof createFiltersFromValueClickAction | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.indexpatternselect.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.indexpatternselect.md new file mode 100644 index 000000000000..eac29dc5de70 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.indexpatternselect.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) > [IndexPatternSelect](./kibana-plugin-plugins-data-public.datapublicpluginstartui.indexpatternselect.md) + +## DataPublicPluginStartUi.IndexPatternSelect property + +Signature: + +```typescript +IndexPatternSelect: React.ComponentType; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.md new file mode 100644 index 000000000000..3d827c0db465 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) + +## DataPublicPluginStartUi interface + +Data plugin prewired UI components + +Signature: + +```typescript +export interface DataPublicPluginStartUi +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [IndexPatternSelect](./kibana-plugin-plugins-data-public.datapublicpluginstartui.indexpatternselect.md) | React.ComponentType<IndexPatternSelectProps> | | +| [SearchBar](./kibana-plugin-plugins-data-public.datapublicpluginstartui.searchbar.md) | React.ComponentType<StatefulSearchBarProps> | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.searchbar.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.searchbar.md new file mode 100644 index 000000000000..06339d14cde2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.datapublicpluginstartui.searchbar.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) > [SearchBar](./kibana-plugin-plugins-data-public.datapublicpluginstartui.searchbar.md) + +## DataPublicPluginStartUi.SearchBar property + +Signature: + +```typescript +SearchBar: React.ComponentType; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md new file mode 100644 index 000000000000..1a0a08f44451 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.fieldformatsstart.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) + +## FieldFormatsStart type + + +Signature: + +```typescript +export declare type FieldFormatsStart = Omit & { + deserialize: FormatFactory; +}; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.aggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.aggs.md new file mode 100644 index 000000000000..ad97820d4d76 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.aggs.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) > [aggs](./kibana-plugin-plugins-data-public.isearchsetup.aggs.md) + +## ISearchSetup.aggs property + +Signature: + +```typescript +aggs: AggsSetup; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md new file mode 100644 index 000000000000..b68c4d61e4e0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) + +## ISearchSetup interface + +The setup contract exposed by the Search plugin exposes the search strategy extension point. + +Signature: + +```typescript +export interface ISearchSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aggs](./kibana-plugin-plugins-data-public.isearchsetup.aggs.md) | AggsSetup | | +| [usageCollector](./kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md) | SearchUsageCollector | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md new file mode 100644 index 000000000000..908a842974f2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) > [usageCollector](./kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md) + +## ISearchSetup.usageCollector property + +Signature: + +```typescript +usageCollector?: SearchUsageCollector; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md index 4b9f6e3594dc..43e10d0bef57 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsource.md @@ -4,7 +4,7 @@ ## ISearchSource type -\* +search source interface Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.aggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.aggs.md new file mode 100644 index 000000000000..993c6bf5a922 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.aggs.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) + +## ISearchStart.aggs property + +agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) + +Signature: + +```typescript +aggs: AggsStart; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md new file mode 100644 index 000000000000..cee213fc6e7e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) + +## ISearchStart interface + +search service + +Signature: + +```typescript +export interface ISearchStart +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | +| [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | +| [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.search.md new file mode 100644 index 000000000000..80e140e9fdd5 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.search.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) + +## ISearchStart.search property + +low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) + +Signature: + +```typescript +search: ISearchGeneric; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.searchsource.md new file mode 100644 index 000000000000..5d4b884b2c25 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.searchsource.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) + +## ISearchStart.searchSource property + +high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) + +Signature: + +```typescript +searchSource: ISearchStartSearchSource; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.create.md new file mode 100644 index 000000000000..7f6344b82d27 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.create.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) > [create](./kibana-plugin-plugins-data-public.isearchstartsearchsource.create.md) + +## ISearchStartSearchSource.create property + +creates [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) based on provided serialized [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) + +Signature: + +```typescript +create: (fields?: SearchSourceFields) => Promise; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.createempty.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.createempty.md new file mode 100644 index 000000000000..b13b5d227c8b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.createempty.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) > [createEmpty](./kibana-plugin-plugins-data-public.isearchstartsearchsource.createempty.md) + +## ISearchStartSearchSource.createEmpty property + +creates empty [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) + +Signature: + +```typescript +createEmpty: () => ISearchSource; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.md new file mode 100644 index 000000000000..f10d5bb002a0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstartsearchsource.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) + +## ISearchStartSearchSource interface + +high level search service + +Signature: + +```typescript +export interface ISearchStartSearchSource +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [create](./kibana-plugin-plugins-data-public.isearchstartsearchsource.create.md) | (fields?: SearchSourceFields) => Promise<ISearchSource> | creates [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) based on provided serialized [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | +| [createEmpty](./kibana-plugin-plugins-data-public.isearchstartsearchsource.createempty.md) | () => ISearchSource | creates empty [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 0c493ca49295..f51549c81fb6 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -8,6 +8,8 @@ | Class | Description | | --- | --- | +| [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) | | +| [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) | | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | @@ -18,6 +20,7 @@ | [Plugin](./kibana-plugin-plugins-data-public.plugin.md) | | | [RequestTimeoutError](./kibana-plugin-plugins-data-public.requesttimeouterror.md) | Class used to signify that a request timed out. Useful for applications to conditionally handle this type of error differently than other errors. | | [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) | | +| [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | \* | | [TimeHistory](./kibana-plugin-plugins-data-public.timehistory.md) | | ## Enumerations @@ -47,8 +50,10 @@ | --- | --- | | [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | | | [ApplyGlobalFilterActionContext](./kibana-plugin-plugins-data-public.applyglobalfilteractioncontext.md) | | -| [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | | -| [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | | +| [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | Data plugin public Setup contract | +| [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | Data plugin public Start contract | +| [DataPublicPluginStartActions](./kibana-plugin-plugins-data-public.datapublicpluginstartactions.md) | utilities to generate filters from action context | +| [DataPublicPluginStartUi](./kibana-plugin-plugins-data-public.datapublicpluginstartui.md) | Data plugin prewired UI components | | [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | | [FieldMappingSpec](./kibana-plugin-plugins-data-public.fieldmappingspec.md) | | @@ -65,6 +70,9 @@ | [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | Use data plugin interface instead | | [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) | | +| [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) | The setup contract exposed by the Search plugin exposes the search strategy extension point. | +| [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) | search service | +| [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | high level search service | | [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | | [Query](./kibana-plugin-plugins-data-public.query.md) | | @@ -79,7 +87,7 @@ | [SavedQueryService](./kibana-plugin-plugins-data-public.savedqueryservice.md) | | | [SearchError](./kibana-plugin-plugins-data-public.searcherror.md) | | | [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) | | -| [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | | +| [SearchSourceFields](./kibana-plugin-plugins-data-public.searchsourcefields.md) | search source fields | | [TabbedAggColumn](./kibana-plugin-plugins-data-public.tabbedaggcolumn.md) | \* | | [TabbedTable](./kibana-plugin-plugins-data-public.tabbedtable.md) | \* | | [TimeRange](./kibana-plugin-plugins-data-public.timerange.md) | | @@ -125,6 +133,8 @@ | [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | | | [AggGroupName](./kibana-plugin-plugins-data-public.agggroupname.md) | | | [AggParam](./kibana-plugin-plugins-data-public.aggparam.md) | | +| [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | AggsStart represents the actual external contract as AggsCommonStart is only used internally. The difference is that AggsStart includes the typings for the registry with initialized agg types. | +| [AutocompleteStart](./kibana-plugin-plugins-data-public.autocompletestart.md) | \* | | [CustomFilter](./kibana-plugin-plugins-data-public.customfilter.md) | | | [EsaggsExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.esaggsexpressionfunctiondefinition.md) | | | [EsdslExpressionFunctionDefinition](./kibana-plugin-plugins-data-public.esdslexpressionfunctiondefinition.md) | | @@ -134,6 +144,7 @@ | [FieldFormatId](./kibana-plugin-plugins-data-public.fieldformatid.md) | id type is needed for creating custom converters. | | [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) | \* | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) | | +| [FieldFormatsStart](./kibana-plugin-plugins-data-public.fieldformatsstart.md) | | | [IAggConfig](./kibana-plugin-plugins-data-public.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | | [IAggType](./kibana-plugin-plugins-data-public.iaggtype.md) | | | [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | @@ -145,12 +156,13 @@ | [InputTimeRange](./kibana-plugin-plugins-data-public.inputtimerange.md) | | | [ISearch](./kibana-plugin-plugins-data-public.isearch.md) | | | [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | -| [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | \* | +| [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | search source interface | | [MappingObject](./kibana-plugin-plugins-data-public.mappingobject.md) | | | [MatchAllFilter](./kibana-plugin-plugins-data-public.matchallfilter.md) | | | [ParsedInterval](./kibana-plugin-plugins-data-public.parsedinterval.md) | | | [PhraseFilter](./kibana-plugin-plugins-data-public.phrasefilter.md) | | | [PhrasesFilter](./kibana-plugin-plugins-data-public.phrasesfilter.md) | | +| [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) | | | [QuerySuggestion](./kibana-plugin-plugins-data-public.querysuggestion.md) | \* | | [QuerySuggestionGetFn](./kibana-plugin-plugins-data-public.querysuggestiongetfn.md) | | | [RangeFilter](./kibana-plugin-plugins-data-public.rangefilter.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystart.md new file mode 100644 index 000000000000..f48a9ee7a79e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystart.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QueryStart](./kibana-plugin-plugins-data-public.querystart.md) + +## QueryStart type + +Signature: + +```typescript +export declare type QueryStart = ReturnType; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md index 3dbfd9430e91..cf171d9ee9f3 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querystringinput.md @@ -7,5 +7,5 @@ Signature: ```typescript -QueryStringInput: React.FC> +QueryStringInput: React.FC> ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md deleted file mode 100644 index ef36b3f37b0c..000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md +++ /dev/null @@ -1,17 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [getPendingCount$](./kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md) - -## SearchInterceptor.getPendingCount$() method - -Returns an `Observable` over the current number of pending searches. This could mean that one of the search requests is still in flight, or that it has only received partial responses. - -Signature: - -```typescript -getPendingCount$(): Observable; -``` -Returns: - -`Observable` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index fd9f23a7f005..5cee345db6cd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -21,11 +21,11 @@ export declare class SearchInterceptor | Property | Modifiers | Type | Description | | --- | --- | --- | --- | | [deps](./kibana-plugin-plugins-data-public.searchinterceptor.deps.md) | | SearchInterceptorDeps | | +| [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) | | ((e: Error) => void) & import("lodash").Cancelable | | ## Methods | Method | Modifiers | Description | | --- | --- | --- | -| [getPendingCount$()](./kibana-plugin-plugins-data-public.searchinterceptor.getpendingcount_.md) | | Returns an Observable over the current number of pending searches. This could mean that one of the search requests is still in flight, or that it has only received partial responses. | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md new file mode 100644 index 000000000000..91ecb2821acb --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) > [showTimeoutError](./kibana-plugin-plugins-data-public.searchinterceptor.showtimeouterror.md) + +## SearchInterceptor.showTimeoutError property + +Signature: + +```typescript +protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md new file mode 100644 index 000000000000..00e9050ee8ff --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource._constructor_.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [(constructor)](./kibana-plugin-plugins-data-public.searchsource._constructor_.md) + +## SearchSource.(constructor) + +Constructs a new instance of the `SearchSource` class + +Signature: + +```typescript +constructor(fields: SearchSourceFields | undefined, dependencies: SearchSourceDependencies); +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| fields | SearchSourceFields | undefined | | +| dependencies | SearchSourceDependencies | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md new file mode 100644 index 000000000000..4264c3ff224b --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.create.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [create](./kibana-plugin-plugins-data-public.searchsource.create.md) + +## SearchSource.create() method + +> Warning: This API is now obsolete. +> +> Don't use. +> + +Signature: + +```typescript +create(): SearchSource; +``` +Returns: + +`SearchSource` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md new file mode 100644 index 000000000000..0c2e75651b35 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createchild.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [createChild](./kibana-plugin-plugins-data-public.searchsource.createchild.md) + +## SearchSource.createChild() method + +creates a new child search source + +Signature: + +```typescript +createChild(options?: {}): SearchSource; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | {} | | + +Returns: + +`SearchSource` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md new file mode 100644 index 000000000000..1053d31010d0 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.createcopy.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [createCopy](./kibana-plugin-plugins-data-public.searchsource.createcopy.md) + +## SearchSource.createCopy() method + +creates a copy of this search source (without its children) + +Signature: + +```typescript +createCopy(): SearchSource; +``` +Returns: + +`SearchSource` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md new file mode 100644 index 000000000000..8a7cc5ee75d1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.destroy.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [destroy](./kibana-plugin-plugins-data-public.searchsource.destroy.md) + +## SearchSource.destroy() method + +Completely destroy the SearchSource. {undefined} + +Signature: + +```typescript +destroy(): void; +``` +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md new file mode 100644 index 000000000000..8fd17e6b1a1d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.fetch.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [fetch](./kibana-plugin-plugins-data-public.searchsource.fetch.md) + +## SearchSource.fetch() method + +Fetch this source and reject the returned Promise on error + + +Signature: + +```typescript +fetch(options?: ISearchOptions): Promise>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| options | ISearchOptions | | + +Returns: + +`Promise>` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md new file mode 100644 index 000000000000..7c516cc29df1 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfield.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getField](./kibana-plugin-plugins-data-public.searchsource.getfield.md) + +## SearchSource.getField() method + +Gets a single field from the fields + +Signature: + +```typescript +getField(field: K, recurse?: boolean): SearchSourceFields[K]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | K | | +| recurse | boolean | | + +Returns: + +`SearchSourceFields[K]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md new file mode 100644 index 000000000000..1980227bee62 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getfields.md @@ -0,0 +1,51 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getFields](./kibana-plugin-plugins-data-public.searchsource.getfields.md) + +## SearchSource.getFields() method + +returns all search source fields + +Signature: + +```typescript +getFields(): { + type?: string | undefined; + query?: import("../..").Query | undefined; + filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; + sort?: Record | Record[] | undefined; + highlight?: any; + highlightAll?: boolean | undefined; + aggs?: any; + from?: number | undefined; + size?: number | undefined; + source?: string | boolean | string[] | undefined; + version?: boolean | undefined; + fields?: string | boolean | string[] | undefined; + index?: import("../..").IndexPattern | undefined; + searchAfter?: import("./types").EsQuerySearchAfter | undefined; + timeout?: string | undefined; + terminate_after?: number | undefined; + }; +``` +Returns: + +`{ + type?: string | undefined; + query?: import("../..").Query | undefined; + filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; + sort?: Record | Record[] | undefined; + highlight?: any; + highlightAll?: boolean | undefined; + aggs?: any; + from?: number | undefined; + size?: number | undefined; + source?: string | boolean | string[] | undefined; + version?: boolean | undefined; + fields?: string | boolean | string[] | undefined; + index?: import("../..").IndexPattern | undefined; + searchAfter?: import("./types").EsQuerySearchAfter | undefined; + timeout?: string | undefined; + terminate_after?: number | undefined; + }` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md new file mode 100644 index 000000000000..b33410d86ae8 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getid.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getId](./kibana-plugin-plugins-data-public.searchsource.getid.md) + +## SearchSource.getId() method + +returns search source id + +Signature: + +```typescript +getId(): string; +``` +Returns: + +`string` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md new file mode 100644 index 000000000000..d5a133772264 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getownfield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getOwnField](./kibana-plugin-plugins-data-public.searchsource.getownfield.md) + +## SearchSource.getOwnField() method + +Get the field from our own fields, don't traverse up the chain + +Signature: + +```typescript +getOwnField(field: K): SearchSourceFields[K]; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | K | | + +Returns: + +`SearchSourceFields[K]` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md new file mode 100644 index 000000000000..14578f7949ba --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getparent.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getParent](./kibana-plugin-plugins-data-public.searchsource.getparent.md) + +## SearchSource.getParent() method + +Get the parent of this SearchSource {undefined\|searchSource} + +Signature: + +```typescript +getParent(): SearchSource | undefined; +``` +Returns: + +`SearchSource | undefined` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md new file mode 100644 index 000000000000..cc50d3f01797 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getSearchRequestBody](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) + +## SearchSource.getSearchRequestBody() method + +Returns body contents of the search request, often referred as query DSL. + +Signature: + +```typescript +getSearchRequestBody(): Promise; +``` +Returns: + +`Promise` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md new file mode 100644 index 000000000000..3f58a76b24cd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.getserializedfields.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [getSerializedFields](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) + +## SearchSource.getSerializedFields() method + +serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) + +Signature: + +```typescript +getSerializedFields(): SearchSourceFields; +``` +Returns: + +`SearchSourceFields` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md new file mode 100644 index 000000000000..e77c9dac7239 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.history.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [history](./kibana-plugin-plugins-data-public.searchsource.history.md) + +## SearchSource.history property + +Signature: + +```typescript +history: SearchRequest[]; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md new file mode 100644 index 000000000000..87346f81b13e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md @@ -0,0 +1,49 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) + +## SearchSource class + +\* + +Signature: + +```typescript +export declare class SearchSource +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(fields, dependencies)](./kibana-plugin-plugins-data-public.searchsource._constructor_.md) | | Constructs a new instance of the SearchSource class | + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [history](./kibana-plugin-plugins-data-public.searchsource.history.md) | | SearchRequest[] | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [create()](./kibana-plugin-plugins-data-public.searchsource.create.md) | | | +| [createChild(options)](./kibana-plugin-plugins-data-public.searchsource.createchild.md) | | creates a new child search source | +| [createCopy()](./kibana-plugin-plugins-data-public.searchsource.createcopy.md) | | creates a copy of this search source (without its children) | +| [destroy()](./kibana-plugin-plugins-data-public.searchsource.destroy.md) | | Completely destroy the SearchSource. {undefined} | +| [fetch(options)](./kibana-plugin-plugins-data-public.searchsource.fetch.md) | | Fetch this source and reject the returned Promise on error | +| [getField(field, recurse)](./kibana-plugin-plugins-data-public.searchsource.getfield.md) | | Gets a single field from the fields | +| [getFields()](./kibana-plugin-plugins-data-public.searchsource.getfields.md) | | returns all search source fields | +| [getId()](./kibana-plugin-plugins-data-public.searchsource.getid.md) | | returns search source id | +| [getOwnField(field)](./kibana-plugin-plugins-data-public.searchsource.getownfield.md) | | Get the field from our own fields, don't traverse up the chain | +| [getParent()](./kibana-plugin-plugins-data-public.searchsource.getparent.md) | | Get the parent of this SearchSource {undefined\|searchSource} | +| [getSearchRequestBody()](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) | | Returns body contents of the search request, often referred as query DSL. | +| [getSerializedFields()](./kibana-plugin-plugins-data-public.searchsource.getserializedfields.md) | | serializes search source fields (which can later be passed to [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md)) | +| [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | +| [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | +| [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | sets value to a single search source feild | +| [setFields(newFields)](./kibana-plugin-plugins-data-public.searchsource.setfields.md) | | Internal, do not use. Overrides all search source fields with the new field array. | +| [setParent(parent, options)](./kibana-plugin-plugins-data-public.searchsource.setparent.md) | | Set a searchSource that this source should inherit from | +| [setPreferredSearchStrategyId(searchStrategyId)](./kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md) | | internal, dont use | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md new file mode 100644 index 000000000000..a9386ddae44e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.onrequeststart.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [onRequestStart](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) + +## SearchSource.onRequestStart() method + +Add a handler that will be notified whenever requests start + +Signature: + +```typescript +onRequestStart(handler: (searchSource: SearchSource, options?: ISearchOptions) => Promise): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| handler | (searchSource: SearchSource, options?: ISearchOptions) => Promise<unknown> | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md new file mode 100644 index 000000000000..73ba8eb66040 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [serialize](./kibana-plugin-plugins-data-public.searchsource.serialize.md) + +## SearchSource.serialize() method + +Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object. + +The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named `kibanaSavedObjectMeta.searchSourceJSON.index` and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`. + +Using `createSearchSource`, the instance can be re-created. + +Signature: + +```typescript +serialize(): { + searchSourceJSON: string; + references: import("../../../../../core/public").SavedObjectReference[]; + }; +``` +Returns: + +`{ + searchSourceJSON: string; + references: import("../../../../../core/public").SavedObjectReference[]; + }` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md new file mode 100644 index 000000000000..22619940f158 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfield.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setField](./kibana-plugin-plugins-data-public.searchsource.setfield.md) + +## SearchSource.setField() method + +sets value to a single search source feild + +Signature: + +```typescript +setField(field: K, value: SearchSourceFields[K]): this; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| field | K | | +| value | SearchSourceFields[K] | | + +Returns: + +`this` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md new file mode 100644 index 000000000000..f92ffc0fc991 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setfields.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setFields](./kibana-plugin-plugins-data-public.searchsource.setfields.md) + +## SearchSource.setFields() method + +Internal, do not use. Overrides all search source fields with the new field array. + + +Signature: + +```typescript +setFields(newFields: SearchSourceFields): this; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| newFields | SearchSourceFields | | + +Returns: + +`this` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md new file mode 100644 index 000000000000..19bf10bec210 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setparent.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setParent](./kibana-plugin-plugins-data-public.searchsource.setparent.md) + +## SearchSource.setParent() method + +Set a searchSource that this source should inherit from + +Signature: + +```typescript +setParent(parent?: ISearchSource, options?: SearchSourceOptions): this; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| parent | ISearchSource | | +| options | SearchSourceOptions | | + +Returns: + +`this` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md new file mode 100644 index 000000000000..e3261873ba10 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [setPreferredSearchStrategyId](./kibana-plugin-plugins-data-public.searchsource.setpreferredsearchstrategyid.md) + +## SearchSource.setPreferredSearchStrategyId() method + +internal, dont use + +Signature: + +```typescript +setPreferredSearchStrategyId(searchStrategyId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| searchStrategyId | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.aggs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.aggs.md index 743646708b4c..f6bab8e42485 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.aggs.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.aggs.md @@ -4,6 +4,8 @@ ## SearchSourceFields.aggs property +[AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.filter.md index a14d33420a22..5fd615cc647d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.filter.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.filter.md @@ -4,6 +4,8 @@ ## SearchSourceFields.filter property +[Filter](./kibana-plugin-plugins-data-public.filter.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.index.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.index.md index fa1d1a552a56..cf1b1cfa253f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.index.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.index.md @@ -4,6 +4,7 @@ ## SearchSourceFields.index property + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md index 7a64af0f8b2b..d19f1da439ce 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.md @@ -4,6 +4,8 @@ ## SearchSourceFields interface +search source fields + Signature: ```typescript @@ -14,17 +16,17 @@ export interface SearchSourceFields | Property | Type | Description | | --- | --- | --- | -| [aggs](./kibana-plugin-plugins-data-public.searchsourcefields.aggs.md) | any | | +| [aggs](./kibana-plugin-plugins-data-public.searchsourcefields.aggs.md) | any | [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) | | [fields](./kibana-plugin-plugins-data-public.searchsourcefields.fields.md) | NameList | | -| [filter](./kibana-plugin-plugins-data-public.searchsourcefields.filter.md) | Filter[] | Filter | (() => Filter[] | Filter | undefined) | | +| [filter](./kibana-plugin-plugins-data-public.searchsourcefields.filter.md) | Filter[] | Filter | (() => Filter[] | Filter | undefined) | [Filter](./kibana-plugin-plugins-data-public.filter.md) | | [from](./kibana-plugin-plugins-data-public.searchsourcefields.from.md) | number | | | [highlight](./kibana-plugin-plugins-data-public.searchsourcefields.highlight.md) | any | | | [highlightAll](./kibana-plugin-plugins-data-public.searchsourcefields.highlightall.md) | boolean | | | [index](./kibana-plugin-plugins-data-public.searchsourcefields.index.md) | IndexPattern | | -| [query](./kibana-plugin-plugins-data-public.searchsourcefields.query.md) | Query | | +| [query](./kibana-plugin-plugins-data-public.searchsourcefields.query.md) | Query | [Query](./kibana-plugin-plugins-data-public.query.md) | | [searchAfter](./kibana-plugin-plugins-data-public.searchsourcefields.searchafter.md) | EsQuerySearchAfter | | | [size](./kibana-plugin-plugins-data-public.searchsourcefields.size.md) | number | | -| [sort](./kibana-plugin-plugins-data-public.searchsourcefields.sort.md) | EsQuerySortValue | EsQuerySortValue[] | | +| [sort](./kibana-plugin-plugins-data-public.searchsourcefields.sort.md) | EsQuerySortValue | EsQuerySortValue[] | [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | | [source](./kibana-plugin-plugins-data-public.searchsourcefields.source.md) | NameList | | | [terminate\_after](./kibana-plugin-plugins-data-public.searchsourcefields.terminate_after.md) | number | | | [timeout](./kibana-plugin-plugins-data-public.searchsourcefields.timeout.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md index 687dafce798d..661ce94a06af 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.query.md @@ -4,6 +4,8 @@ ## SearchSourceFields.query property +[Query](./kibana-plugin-plugins-data-public.query.md) + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.sort.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.sort.md index c10f556cef6d..32f513378e35 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.sort.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsourcefields.sort.md @@ -4,6 +4,8 @@ ## SearchSourceFields.sort property +[EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) + Signature: ```typescript diff --git a/docs/management/index-lifecycle-policies/manage-policy.asciidoc b/docs/management/index-lifecycle-policies/manage-policy.asciidoc index a57af8a33494..8e2dc96de4b9 100644 --- a/docs/management/index-lifecycle-policies/manage-policy.asciidoc +++ b/docs/management/index-lifecycle-policies/manage-policy.asciidoc @@ -25,4 +25,10 @@ created index. For more information, see {ref}/indices-templates.html[Index temp * *Delete a policy.* You can’t delete a policy that is currently in use or recover a deleted index. +[float] +=== Required permissions + +The `manage_ilm` cluster privilege is required to access *Index lifecycle policies*. + +You can add these privileges in *Stack Management > Security > Roles*. diff --git a/docs/management/managing-ccr.asciidoc b/docs/management/managing-ccr.asciidoc index 67193b3b5a03..9c06e479e28b 100644 --- a/docs/management/managing-ccr.asciidoc +++ b/docs/management/managing-ccr.asciidoc @@ -20,6 +20,13 @@ image::images/cross-cluster-replication-list-view.png[][Cross-cluster replicatio * The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster. Refer to {ref}/ccr-overview.html[this document] for more information. +[float] +=== Required permissions + +The `manage` and `manage_ccr` cluster privileges are required to access *Cross-Cluster Replication*. + +You can add these privileges in *Stack Management > Security > Roles*. + [float] [[configure-replication]] === Configure replication diff --git a/docs/management/managing-licenses.asciidoc b/docs/management/managing-licenses.asciidoc index 25ae29036f65..b53bda95466d 100644 --- a/docs/management/managing-licenses.asciidoc +++ b/docs/management/managing-licenses.asciidoc @@ -29,6 +29,13 @@ See {ref}/encrypting-communications.html[Encrypting communications]. {kib} and the {ref}/start-basic.html[start basic API] provide a list of all of the features that will no longer be supported if you revert to a basic license. +[float] +=== Required permissions + +The `manage` cluster privilege is required to access *License Management*. + +You can add this privilege in *Stack Management > Security > Roles*. + [discrete] [[update-license]] === Update your license diff --git a/docs/management/managing-remote-clusters.asciidoc b/docs/management/managing-remote-clusters.asciidoc index 83895838efec..92e0fa822b05 100644 --- a/docs/management/managing-remote-clusters.asciidoc +++ b/docs/management/managing-remote-clusters.asciidoc @@ -11,6 +11,13 @@ To get started, open the menu, then go to *Stack Management > Data > Remote Clus [role="screenshot"] image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button] +[float] +=== Required permissions + +The `manage` cluster privilege is required to access *Remote Clusters*. + +You can add this privilege in *Stack Management > Security > Roles*. + [float] [[managing-remote-clusters]] === Add a remote cluster diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc index 8aa57f50fe94..e20f384b5ed1 100644 --- a/docs/management/rollups/create_and_manage_rollups.asciidoc +++ b/docs/management/rollups/create_and_manage_rollups.asciidoc @@ -20,6 +20,13 @@ image::images/management_rollup_list.png[][List of currently active rollup jobs] Before using this feature, you should be familiar with how rollups work. {ref}/xpack-rollup.html[Rolling up historical data] is a good source for more detailed information. +[float] +=== Required permissions + +The `manage_rollup` cluster privilege is required to access *Rollup jobs*. + +You can add this privilege in *Stack Management > Security > Roles*. + [float] [[create-and-manage-rollup-job]] === Create a rollup job diff --git a/docs/management/upgrade-assistant/index.asciidoc b/docs/management/upgrade-assistant/index.asciidoc index c5fd6a3a555a..2b8c2da2ef57 100644 --- a/docs/management/upgrade-assistant/index.asciidoc +++ b/docs/management/upgrade-assistant/index.asciidoc @@ -13,6 +13,14 @@ Before you upgrade, make sure that you are using the latest released minor version of {es} to see the most up-to-date deprecation issues. For example, if you want to upgrade to to 7.0, make sure that you are using 6.8. +[float] +=== Required permissions + +The `manage` cluster privilege is required to access the *Upgrade assistant*. +Additional privileges may be needed to perform certain actions. + +You can add this privilege in *Stack Management > Security > Roles*. + [float] === Reindexing diff --git a/docs/migration/migrate_8_0.asciidoc b/docs/migration/migrate_8_0.asciidoc index b80503750a26..0cb28ce0fb6e 100644 --- a/docs/migration/migrate_8_0.asciidoc +++ b/docs/migration/migrate_8_0.asciidoc @@ -115,7 +115,7 @@ URL that it derived from the actual server address and `xpack.security.public` s *Impact:* Any workflow that involved manually clearing generated bundles will have to be updated with the new path. -[float]] +[float] === kibana.keystore has moved from the `data` folder to the `config` folder *Details:* By default, kibana.keystore has moved from the configured `path.data` folder to `/config` for archive distributions and `/etc/kibana` for package distributions. If a pre-existing keystore exists in the data directory that path will continue to be used. @@ -136,6 +136,18 @@ custom roles with {kibana-ref}/kibana-privileges.html[{kib} privileges]. instead be assigned the `kibana_admin` role to maintain their current access level. +[float] +=== `kibana_dashboard_only_user` role has been removed. + +*Details:* The `kibana_dashboard_only_user` role has been removed. +If you wish to restrict access to just the Dashboard feature, create +custom roles with {kibana-ref}/kibana-privileges.html[{kib} privileges]. + +*Impact:* Any users currently assigned the `kibana_dashboard_only_user` role will need to be assigned a custom role which only grants access to the Dashboard feature. + +Granting additional cluster or index privileges may enable certain +**Stack Monitoring** features. + [float] [[breaking_80_reporting_changes]] === Reporting changes diff --git a/examples/alerting_example/server/plugin.ts b/examples/alerting_example/server/plugin.ts index e74cad28f77f..8e246960937e 100644 --- a/examples/alerting_example/server/plugin.ts +++ b/examples/alerting_example/server/plugin.ts @@ -38,7 +38,7 @@ export class AlertingExamplePlugin implements Plugin.ts +``` + +### 1. Update the telemetryrc file + +Make sure your collector is not excluded in the `telemetryrc.json` files (located at the root of the kibana project, and another on in the `x-pack` dir). + +```s +[ + { + ... + "exclude": [ + "" + ] + } +] +``` + +Note that the check will fail if the collector in --path is excluded. + +### 2. Type the `fetch` function +1. Make sure the return of the `fetch` function is typed. + +The function `makeUsageCollector` accepts a generic type parameter of the returned type of the `fetch` function. + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + fetch: async () => { + return { + someStat: 3, + } + }, + ... +}) +``` + +The generic type passed to `makeUsageCollector` will automatically unwrap the `Promise` to check for the resolved type. + +### 3. Add a `schema` field + +Add a `schema` field to your collector. After passing the return type of the fetch function to the `makeUsageCollector` generic parameter. It will automaticallly figure out the correct type of the schema based on that provided type. + + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + schema: { + someStat: { + type: 'long' + } + }, + ... +}) +``` + +For full details on writing the `schema` object, check the [Writing the schema](#writing-the-schema) section. + +### 4. Run the telemetry check + +To make sure your changes pass the telemetry check you can run the following: + +``` +node scripts/telemetry_check.js --ignore-stored-json --path=.ts +``` + +### 5. Update the stored json files + +The `--fix` flag will automatically update the persisted json files used by the telemetry team. + +``` +node scripts/telemetry_check.js --fix +``` + +Note that any updates to the stored json files will require a review by the kibana-telemetry team to help us update the telemetry cluster mappings and ensure your changes adhere to our best practices. + + +## Updating the collector schema + +Simply update the fetch function to start returning the updated fields back to our cluster. The update the schema to accomodate these changes. + +Once youre run the changes to both the `fetch` function and the `schema` field run the following command + +``` +node scripts/telemetry_check.js --fix +``` + +The `--fix` flag will automatically update the persisted json files used by the telemetry team. Note that any updates to the stored json files will require a review by the kibana-telemetry team to help us update the telemetry cluster mappings and ensure your changes adhere to our best practices. + + +## Writing the schema + +We've designed the schema object to closely resemble elasticsearch mapping object to reduce any cognitive complexity. + +### Basics + +The function `makeUsageCollector` will automatically translate the returned `Usage` fetch type to the `schema` object. This way you'll have the typescript type checker helping you write the correct corrisponding schema. + +``` +interface Usage { + someStat: number; +} + +usageCollection.makeUsageCollector({ + schema: { + someStat: { + type: 'long' + } + }, + ... +}) +``` + + +### Allowed types + +Any field property in the schema accepts a `type` field. By default the type is `object` which accepts nested properties under it. Currently we accept the following property types: + +``` +AllowedSchemaTypes = + | 'keyword' + | 'text' + | 'number' + | 'boolean' + | 'long' + | 'date' + | 'float'; +``` + + +### Dealing with arrays + +You can optionally define a property to be an array by setting the `isArray` to `true`. Note that the `isArray` property is not currently required. + + +``` +interface Usage { + arrayOfStrings: string[]; + arrayOfObjects: {key: string; value: number; }[]; +} + +usageCollection.makeUsageCollector({ + fetch: () => { + return { + arrayOfStrings: ['item_one', 'item_two'], + arrayOfObjects: [ + { key: 'key_one', value: 13 }, + ] + } + } + schema: { + arrayOfStrings: { + type: 'keyword', + isArray: true, + }, + arrayOfObjects: { + isArray: true, + key: { + type: 'keyword', + }, + value: { + type: 'long', + }, + } + }, + ... +}) +``` + +Be careful adding arrays of objects due to the limitation in correlating the properties inside those objects inside kibana. It is advised to look for an alternative schema based on your use cases. + + +## Schema Restrictions + +We have enforced some restrictions to the schema object to adhere to our telemetry best practices. These practices are derived from the usablity of the sent data in our telemetry cluster. + + +### Root of schema can only be an object + +The root of the schema can only be an object. Currently any property must be nested inside the main schema object. \ No newline at end of file diff --git a/packages/kbn-telemetry-tools/package.json b/packages/kbn-telemetry-tools/package.json index 63a8fcf30335..4318cbcf2ec4 100644 --- a/packages/kbn-telemetry-tools/package.json +++ b/packages/kbn-telemetry-tools/package.json @@ -10,12 +10,12 @@ "kbn:watch": "yarn build --watch" }, "devDependencies": { - "lodash": "npm:@elastic/lodash@3.10.1-kibana4", + "lodash": "^4.17.20", "@kbn/dev-utils": "1.0.0", "@kbn/utility-types": "1.0.0", "@types/normalize-path": "^3.0.0", "normalize-path": "^3.0.0", - "@types/lodash": "^3.10.1", + "@types/lodash": "^4.14.159", "moment": "^2.24.0", "typescript": "4.0.2" } diff --git a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts index 2f85fd2cdd2a..87ba68c1bcb2 100644 --- a/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts +++ b/packages/kbn-telemetry-tools/src/cli/run_telemetry_check.ts @@ -35,7 +35,7 @@ import { export function runTelemetryCheck() { run( - async ({ flags: { fix = false, path }, log }) => { + async ({ flags: { fix = false, 'ignore-stored-json': ignoreStoredJson, path }, log }) => { if (typeof fix !== 'boolean') { throw createFailError(`${chalk.white.bgRed(' TELEMETRY ERROR ')} --fix can't have a value`); } @@ -50,6 +50,14 @@ export function runTelemetryCheck() { ); } + if (fix && typeof ignoreStoredJson !== 'undefined') { + throw createFailError( + `${chalk.white.bgRed( + ' TELEMETRY ERROR ' + )} --fix is incompatible with --ignore-stored-json flag.` + ); + } + const list = new Listr([ { title: 'Checking .telemetryrc.json files', @@ -59,11 +67,28 @@ export function runTelemetryCheck() { title: 'Extracting Collectors', task: (context) => new Listr(extractCollectorsTask(context, path), { exitOnError: true }), }, + { + enabled: () => typeof path !== 'undefined', + title: 'Checking collectors in --path are not excluded', + task: ({ roots }: TaskContext) => { + const totalCollections = roots.reduce((acc, root) => { + return acc + (root.parsedCollections?.length || 0); + }, 0); + const collectorsInPath = Array.isArray(path) ? path.length : 1; + + if (totalCollections !== collectorsInPath) { + throw new Error( + 'Collector specified in `path` is excluded; Check the telemetryrc.json files.' + ); + } + }, + }, { title: 'Checking Compatible collector.schema with collector.fetch type', task: (context) => new Listr(checkCompatibleTypesTask(context), { exitOnError: true }), }, { + enabled: (_) => !!ignoreStoredJson, title: 'Checking Matching collector.schema against stored json files', task: (context) => new Listr(checkMatchingSchemasTask(context, !fix), { exitOnError: true }), diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index d5412f64f361..7afe828298b4 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -18,7 +18,7 @@ */ import * as ts from 'typescript'; -import { uniq } from 'lodash'; +import { uniqBy } from 'lodash'; import { getResolvedModuleSourceFile, getIdentifierDeclarationFromSource, @@ -148,7 +148,7 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | .map((typeNode) => getDescriptor(typeNode, program)) .filter(discardNullOrUndefined); - const uniqueKinds = uniq(kinds, 'kind'); + const uniqueKinds = uniqBy(kinds, 'kind'); if (uniqueKinds.length !== 1) { throw Error('Mapping does not support conflicting union types.'); diff --git a/packages/kbn-telemetry-tools/src/tools/utils.ts b/packages/kbn-telemetry-tools/src/tools/utils.ts index c1424785b22a..3d6764117374 100644 --- a/packages/kbn-telemetry-tools/src/tools/utils.ts +++ b/packages/kbn-telemetry-tools/src/tools/utils.ts @@ -18,7 +18,18 @@ */ import * as ts from 'typescript'; -import { pick, isObject, each, isArray, reduce, isEmpty, merge, transform, isEqual } from 'lodash'; +import { + pick, + pickBy, + isObject, + forEach, + isArray, + reduce, + isEmpty, + merge, + transform, + isEqual, +} from 'lodash'; import * as path from 'path'; import glob from 'glob'; import { readFile, writeFile } from 'fs'; @@ -186,17 +197,17 @@ export function getPropertyValue( } } -export function pickDeep(collection: any, identity: any, thisArg?: any) { - const picked: any = pick(collection, identity, thisArg); - const collections = pick(collection, isObject, thisArg); +export function pickDeep(collection: any, identity: any) { + const picked: any = pick(collection, identity); + const collections = pickBy(collection, isObject); - each(collections, function (item, key) { + forEach(collections, function (item, key) { let object; if (isArray(item)) { object = reduce( item, function (result, value) { - const pickedDeep = pickDeep(value, identity, thisArg); + const pickedDeep = pickDeep(value, identity); if (!isEmpty(pickedDeep)) { result.push(pickedDeep); } @@ -205,7 +216,7 @@ export function pickDeep(collection: any, identity: any, thisArg?: any) { [] as any[] ); } else { - object = pickDeep(item, identity, thisArg); + object = pickDeep(item, identity); } if (!isEmpty(object)) { @@ -230,33 +241,38 @@ export const flattenKeys = (obj: any, keyPath: any[] = []): any => { return { [keyPath.join('.')]: obj }; }; +type ObjectDict = Record; export function difference(actual: any, expected: any) { - function changes(obj: { [key: string]: any }, base: { [key: string]: any }) { - return transform(obj, function (result, value, key) { - if (key && /@@INDEX@@/.test(`${key}`)) { - // The type definition is an Index Signature, fuzzy searching for similar keys - const regexp = new RegExp(`${key}`.replace(/@@INDEX@@/g, '(.+)?')); - const keysInBase = Object.keys(base) - .map((k) => { - const match = k.match(regexp); - return match && match[0]; - }) - .filter((s): s is string => !!s); - - if (keysInBase.length === 0) { - // Mark this key as wrong because we couldn't find any matching keys - result[key] = value; - } - - keysInBase.forEach((k) => { - if (!isEqual(value, base[k])) { - result[k] = isObject(value) && isObject(base[k]) ? changes(value, base[k]) : value; + function changes(obj: ObjectDict, base: ObjectDict) { + return transform( + obj, + function (result, value, key) { + if (key && /@@INDEX@@/.test(`${key}`)) { + // The type definition is an Index Signature, fuzzy searching for similar keys + const regexp = new RegExp(`${key}`.replace(/@@INDEX@@/g, '(.+)?')); + const keysInBase = Object.keys(base) + .map((k) => { + const match = k.match(regexp); + return match && match[0]; + }) + .filter((s): s is string => !!s); + + if (keysInBase.length === 0) { + // Mark this key as wrong because we couldn't find any matching keys + result[key] = value; } - }); - } else if (key && !isEqual(value, base[key])) { - result[key] = isObject(value) && isObject(base[key]) ? changes(value, base[key]) : value; - } - }); + + keysInBase.forEach((k) => { + if (!isEqual(value, base[k])) { + result[k] = isObject(value) && isObject(base[k]) ? changes(value, base[k]) : value; + } + }); + } else if (key && !isEqual(value, base[key])) { + result[key] = isObject(value) && isObject(base[key]) ? changes(value, base[key]) : value; + } + }, + {} as ObjectDict + ); } return changes(actual, expected); } diff --git a/packages/kbn-ui-framework/src/components/local_nav/_local_search.scss b/packages/kbn-ui-framework/src/components/local_nav/_local_search.scss index 130807790e98..740ae664c7f5 100644 --- a/packages/kbn-ui-framework/src/components/local_nav/_local_search.scss +++ b/packages/kbn-ui-framework/src/components/local_nav/_local_search.scss @@ -26,13 +26,6 @@ border-radius: 0; border-left-width: 0; } - -.kuiLocalSearchAssistedInput { - display: flex; - flex: 1 1 100%; - position: relative; -} - /** * 1. em used for right padding so documentation link and query string * won't overlap if the user increases their default browser font size diff --git a/packages/kbn-utility-types/package.json b/packages/kbn-utility-types/package.json index a999eb41eb78..d1d7a1c0397c 100644 --- a/packages/kbn-utility-types/package.json +++ b/packages/kbn-utility-types/package.json @@ -16,7 +16,7 @@ "utility-types": "^3.10.0" }, "devDependencies": { - "del-cli": "^3.0.0", - "tsd": "^0.7.4" + "del-cli": "^3.0.1", + "tsd": "^0.13.1" } } diff --git a/packages/kbn-utility-types/test-d/union_to_intersection.ts b/packages/kbn-utility-types/test-d/union_to_intersection.ts index ba385268475e..8b49436bdd95 100644 --- a/packages/kbn-utility-types/test-d/union_to_intersection.ts +++ b/packages/kbn-utility-types/test-d/union_to_intersection.ts @@ -17,12 +17,12 @@ * under the License. */ -import { expectType } from 'tsd'; +import { expectAssignable } from 'tsd'; import { UnionToIntersection } from '../index'; type INTERSECTED = UnionToIntersection<{ foo: 'bar' } | { baz: 'qux' }>; -expectType({ +expectAssignable({ foo: 'bar', baz: 'qux', }); diff --git a/packages/kbn-utility-types/test-d/unwrap_observable.ts b/packages/kbn-utility-types/test-d/unwrap_observable.ts index af4fa9abf6ec..e9791cfd36be 100644 --- a/packages/kbn-utility-types/test-d/unwrap_observable.ts +++ b/packages/kbn-utility-types/test-d/unwrap_observable.ts @@ -17,9 +17,9 @@ * under the License. */ -import { expectType } from 'tsd'; +import { expectAssignable } from 'tsd'; import { UnwrapObservable, ObservableLike } from '../index'; type STRING = UnwrapObservable>; -expectType('adf'); +expectAssignable('adf'); diff --git a/packages/kbn-utility-types/test-d/unwrap_promise.ts b/packages/kbn-utility-types/test-d/unwrap_promise.ts index 9c4b1bc76b80..b61b24e4b3f1 100644 --- a/packages/kbn-utility-types/test-d/unwrap_promise.ts +++ b/packages/kbn-utility-types/test-d/unwrap_promise.ts @@ -17,11 +17,11 @@ * under the License. */ -import { expectType } from 'tsd'; +import { expectAssignable } from 'tsd'; import { UnwrapPromise } from '../index'; type STRING = UnwrapPromise>; type TUPLE = UnwrapPromise>; -expectType('adf'); -expectType([1, 2]); +expectAssignable('adf'); +expectAssignable([1, 2]); diff --git a/packages/kbn-utility-types/test-d/values.ts b/packages/kbn-utility-types/test-d/values.ts index 9e50cfebde1d..69bee9c3c965 100644 --- a/packages/kbn-utility-types/test-d/values.ts +++ b/packages/kbn-utility-types/test-d/values.ts @@ -17,22 +17,22 @@ * under the License. */ -import { expectType } from 'tsd'; +import { expectAssignable } from 'tsd'; import { Values } from '../index'; // Arrays type STRING = Values; type ASDF_FOO = Values>; -expectType('adf'); -expectType('asdf'); -expectType('foo'); +expectAssignable('adf'); +expectAssignable('asdf'); +expectAssignable('foo'); // Objects type STRING2 = Values>; type FOO = Values>; type BAR = Values<{ foo: 'bar' }>; -expectType('adf'); -expectType('foo'); -expectType('bar'); +expectAssignable('adf'); +expectAssignable('foo'); +expectAssignable('bar'); diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index ea0e8d66d58f..6a21dcb1b068 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1231,7 +1231,7 @@ import { npStart: { plugins } } from 'ui/new_platform'; | `import 'ui/filter_bar'` | `import { FilterBar } from '../data/public'` | Directive is deprecated. | | `import 'ui/query_bar'` | `import { QueryStringInput } from '../data/public'` | Directives are deprecated. | | `import 'ui/search_bar'` | `import { SearchBar } from '../data/public'` | Directive is deprecated. | -| `import 'ui/kbn_top_nav'` | `import { TopNavMenu } from '../navigation/public'` | Directive was moved to `src/plugins/kibana_legacy`. | +| `import 'ui/kbn_top_nav'` | `import { TopNavMenu } from '../navigation/public'` | Directive was removed. | | `ui/saved_objects/components/saved_object_finder` | `import { SavedObjectFinder } from '../saved_objects/public'` | | | `core_plugins/interpreter` | `plugins.data.expressions` | | `ui/courier` | `plugins.data.search` | @@ -1284,7 +1284,7 @@ _See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-pl | Legacy Platform | New Platform | Notes | | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----- | -| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerFeature`](x-pack/plugins/features/server/plugin.ts) | | +| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerKibanaFeature`](x-pack/plugins/features/server/plugin.ts) | | | `server.plugins.xpack_main.feature(pluginID).registerLicenseCheckResultsGenerator` | [`x-pack licensing plugin`](/x-pack/plugins/licensing/README.md) | | #### UI Exports diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 95ac8bba5704..fae7a272c963 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -38,6 +38,9 @@ export class DocLinksService { links: { dashboard: { drilldowns: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/drilldowns.html`, + drilldownsTriggerPicker: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/url-drilldown.html#trigger-picker`, + urlDrilldownTemplateSyntax: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/url-drilldown.html#templating`, + urlDrilldownVariables: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/url-drilldown.html#variables`, }, filebeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/filebeat/${DOC_LINK_VERSION}`, @@ -143,6 +146,9 @@ export interface DocLinksStart { readonly links: { readonly dashboard: { readonly drilldowns: string; + readonly drilldownsTriggerPicker: string; + readonly urlDrilldownTemplateSyntax: string; + readonly urlDrilldownVariables: string; }; readonly filebeat: { readonly base: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index c473ea67d9bc..d90b8f780b67 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -490,6 +490,9 @@ export interface DocLinksStart { readonly links: { readonly dashboard: { readonly drilldowns: string; + readonly drilldownsTriggerPicker: string; + readonly urlDrilldownTemplateSyntax: string; + readonly urlDrilldownVariables: string; }; readonly filebeat: { readonly base: string; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 97aca74bfd48..d127471348d9 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -293,6 +293,7 @@ export { SavedObjectsTypeManagementDefinition, SavedObjectMigrationMap, SavedObjectMigrationFn, + SavedObjectsUtils, exportSavedObjectsToStream, importSavedObjectsFromStream, resolveSavedObjectsImportErrors, diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts index 64d1256be2f3..836aabf88147 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.test.ts @@ -116,6 +116,16 @@ test('logs warning if pluginId is not in camelCase format', async () => { `); }); +test('does not log pluginId format warning in dist mode', async () => { + mockReadFile.mockImplementation((path, cb) => { + cb(null, Buffer.from(JSON.stringify({ id: 'some_name', version: 'kibana', server: true }))); + }); + + expect(loggingSystemMock.collect(logger).warn).toHaveLength(0); + await parseManifest(pluginPath, { ...packageInfo, dist: true }, logger); + expect(loggingSystemMock.collect(logger).warn.length).toBe(0); +}); + test('return error when plugin version is missing', async () => { mockReadFile.mockImplementation((path, cb) => { cb(null, Buffer.from(JSON.stringify({ id: 'someId' }))); diff --git a/src/core/server/plugins/discovery/plugin_manifest_parser.ts b/src/core/server/plugins/discovery/plugin_manifest_parser.ts index 0d33e266c37d..cfc412cb60b5 100644 --- a/src/core/server/plugins/discovery/plugin_manifest_parser.ts +++ b/src/core/server/plugins/discovery/plugin_manifest_parser.ts @@ -116,7 +116,7 @@ export async function parseManifest( ); } - if (!isCamelCase(manifest.id)) { + if (!packageInfo.dist && !isCamelCase(manifest.id)) { log.warn(`Expect plugin "id" in camelCase, but found: ${manifest.id}`); } diff --git a/src/core/server/saved_objects/routes/bulk_update.ts b/src/core/server/saved_objects/routes/bulk_update.ts index c112833b29f3..882213644146 100644 --- a/src/core/server/saved_objects/routes/bulk_update.ts +++ b/src/core/server/saved_objects/routes/bulk_update.ts @@ -40,6 +40,7 @@ export const registerBulkUpdateRoute = (router: IRouter) => { }) ) ), + namespace: schema.maybe(schema.string({ minLength: 1 })), }) ), }, diff --git a/src/core/server/saved_objects/service/index.ts b/src/core/server/saved_objects/service/index.ts index 271d4dd67d43..c33a9f2f3b15 100644 --- a/src/core/server/saved_objects/service/index.ts +++ b/src/core/server/saved_objects/service/index.ts @@ -27,6 +27,7 @@ export { SavedObjectsErrorHelpers, SavedObjectsClientFactory, SavedObjectsClientFactoryProvider, + SavedObjectsUtils, } from './lib'; export * from './saved_objects_client'; diff --git a/src/core/server/saved_objects/service/lib/index.ts b/src/core/server/saved_objects/service/lib/index.ts index e103120388e3..eae8c5ef2e10 100644 --- a/src/core/server/saved_objects/service/lib/index.ts +++ b/src/core/server/saved_objects/service/lib/index.ts @@ -30,3 +30,5 @@ export { } from './scoped_client_provider'; export { SavedObjectsErrorHelpers } from './errors'; + +export { SavedObjectsUtils } from './utils'; diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index f2e3b3e633cd..7d30875b9079 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -155,27 +155,33 @@ describe('SavedObjectsRepository', () => { log: {}, }); - const getMockGetResponse = ({ type, id, references, namespace, originId }) => ({ - // NOTE: Elasticsearch returns more fields (_index, _type) but the SavedObjectsRepository method ignores these - found: true, - _id: `${registry.isSingleNamespace(type) && namespace ? `${namespace}:` : ''}${type}:${id}`, - ...mockVersionProps, - _source: { - ...(registry.isSingleNamespace(type) && { namespace }), - ...(registry.isMultiNamespace(type) && { namespaces: [namespace ?? 'default'] }), - ...(originId && { originId }), - type, - [type]: { title: 'Testing' }, - references, - specialProperty: 'specialValue', - ...mockTimestampFields, - }, - }); + const getMockGetResponse = ( + { type, id, references, namespace: objectNamespace, originId }, + namespace + ) => { + const namespaceId = objectNamespace === 'default' ? undefined : objectNamespace ?? namespace; + return { + // NOTE: Elasticsearch returns more fields (_index, _type) but the SavedObjectsRepository method ignores these + found: true, + _id: `${ + registry.isSingleNamespace(type) && namespaceId ? `${namespaceId}:` : '' + }${type}:${id}`, + ...mockVersionProps, + _source: { + ...(registry.isSingleNamespace(type) && { namespace: namespaceId }), + ...(registry.isMultiNamespace(type) && { namespaces: [namespaceId ?? 'default'] }), + ...(originId && { originId }), + type, + [type]: { title: 'Testing' }, + references, + specialProperty: 'specialValue', + ...mockTimestampFields, + }, + }; + }; const getMockMgetResponse = (objects, namespace) => ({ - docs: objects.map((obj) => - obj.found === false ? obj : getMockGetResponse({ ...obj, namespace }) - ), + docs: objects.map((obj) => (obj.found === false ? obj : getMockGetResponse(obj, namespace))), }); expect.extend({ @@ -586,6 +592,16 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await bulkCreateSuccess([obj1, obj2], { namespace: 'default' }); + const expected = expect.not.objectContaining({ namespace: 'default' }); + const body = [expect.any(Object), expected, expect.any(Object), expected]; + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }); + it(`doesn't add namespace to request body for any types that are not single-namespace`, async () => { const objects = [ { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }, @@ -653,19 +669,19 @@ describe('SavedObjectsRepository', () => { }); it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type, id) => `${namespace}:${type}:${id}`; + const getId = (type, id) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) await bulkCreateSuccess([obj1, obj2], { namespace }); expectClientCallArgsAction([obj1, obj2], { method: 'create', getId }); }); it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) await bulkCreateSuccess([obj1, obj2]); expectClientCallArgsAction([obj1, obj2], { method: 'create', getId }); }); it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) const objects = [ { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }, { ...obj2, type: MULTI_NAMESPACE_TYPE }, @@ -972,19 +988,25 @@ describe('SavedObjectsRepository', () => { describe('client calls', () => { it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type, id) => `${namespace}:${type}:${id}`; + const getId = (type, id) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) await bulkGetSuccess([obj1, obj2], { namespace }); _expectClientCallArgs([obj1, obj2], { getId }); }); it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) await bulkGetSuccess([obj1, obj2]); _expectClientCallArgs([obj1, obj2], { getId }); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await bulkGetSuccess([obj1, obj2], { namespace: 'default' }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) let objects = [obj1, obj2].map((obj) => ({ ...obj, type: NAMESPACE_AGNOSTIC_TYPE })); await bulkGetSuccess(objects, { namespace }); _expectClientCallArgs(objects, { getId }); @@ -1327,32 +1349,66 @@ describe('SavedObjectsRepository', () => { }); it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type, id) => `${namespace}:${type}:${id}`; + const getId = (type, id) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) await bulkUpdateSuccess([obj1, obj2], { namespace }); expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + + jest.clearAllMocks(); + // test again with object namespace string that supersedes the operation's namespace ID + await bulkUpdateSuccess([ + { ...obj1, namespace }, + { ...obj2, namespace }, + ]); + expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); }); it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) await bulkUpdateSuccess([obj1, obj2]); expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + + jest.clearAllMocks(); + // test again with object namespace string that supersedes the operation's namespace ID + await bulkUpdateSuccess( + [ + { ...obj1, namespace: 'default' }, + { ...obj2, namespace: 'default' }, + ], + { namespace } + ); + expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); }); - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + it(`normalizes options.namespace from 'default' to undefined`, async () => { const getId = (type, id) => `${type}:${id}`; - const objects1 = [{ ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }]; - await bulkUpdateSuccess(objects1, { namespace }); - expectClientCallArgsAction(objects1, { method: 'update', getId }); - client.bulk.mockClear(); + await bulkUpdateSuccess([obj1, obj2], { namespace: 'default' }); + expectClientCallArgsAction([obj1, obj2], { method: 'update', getId }); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) const overrides = { // bulkUpdate uses a preflight `get` request for multi-namespace saved objects, and specifies that version on `update` // we aren't testing for this here, but we need to include Jest assertions so this test doesn't fail if_primary_term: expect.any(Number), if_seq_no: expect.any(Number), }; - const objects2 = [{ ...obj2, type: MULTI_NAMESPACE_TYPE }]; - await bulkUpdateSuccess(objects2, { namespace }); - expectClientCallArgsAction(objects2, { method: 'update', getId, overrides }, 2); + const _obj1 = { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }; + const _obj2 = { ...obj2, type: MULTI_NAMESPACE_TYPE }; + + await bulkUpdateSuccess([_obj1], { namespace }); + expectClientCallArgsAction([_obj1], { method: 'update', getId }); + client.bulk.mockClear(); + await bulkUpdateSuccess([_obj2], { namespace }); + expectClientCallArgsAction([_obj2], { method: 'update', getId, overrides }, 2); + + jest.clearAllMocks(); + // test again with object namespace string that supersedes the operation's namespace ID + await bulkUpdateSuccess([{ ..._obj1, namespace }]); + expectClientCallArgsAction([_obj1], { method: 'update', getId }); + client.bulk.mockClear(); + await bulkUpdateSuccess([{ ..._obj2, namespace }]); + expectClientCallArgsAction([_obj2], { method: 'update', getId, overrides }, 2); }); }); @@ -1581,19 +1637,25 @@ describe('SavedObjectsRepository', () => { }); it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type, id) => `${namespace}:${type}:${id}`; + const getId = (type, id) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) await checkConflictsSuccess([obj1, obj2], { namespace }); _expectClientCallArgs([obj1, obj2], { getId }); }); it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) await checkConflictsSuccess([obj1, obj2]); _expectClientCallArgs([obj1, obj2], { getId }); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await checkConflictsSuccess([obj1, obj2], { namespace: 'default' }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type, id) => `${type}:${id}`; + const getId = (type, id) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) // obj3 is multi-namespace, and obj6 is namespace-agnostic await checkConflictsSuccess([obj3, obj6], { namespace }); _expectClientCallArgs([obj3, obj6], { getId }); @@ -1816,6 +1878,16 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await createSuccess(type, attributes, { id, namespace: 'default' }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); expect(client.create).toHaveBeenCalledWith( @@ -1852,11 +1924,7 @@ describe('SavedObjectsRepository', () => { }); it(`throws when there is a conflict with an existing multi-namespace saved object (get)`, async () => { - const response = getMockGetResponse({ - type: MULTI_NAMESPACE_TYPE, - id, - namespace: 'bar-namespace', - }); + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }, 'bar-namespace'); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -1959,7 +2027,7 @@ describe('SavedObjectsRepository', () => { const deleteSuccess = async (type, id, options) => { if (registry.isMultiNamespace(type)) { - const mockGetResponse = getMockGetResponse({ type, id, namespace: options?.namespace }); + const mockGetResponse = getMockGetResponse({ type, id }, options?.namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(mockGetResponse) ); @@ -2035,6 +2103,14 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await deleteSuccess(type, id, { namespace: 'default' }); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${type}:${id}` }), + expect.anything() + ); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { await deleteSuccess(NAMESPACE_AGNOSTIC_TYPE, id, { namespace }); expect(client.delete).toHaveBeenCalledWith( @@ -2085,7 +2161,7 @@ describe('SavedObjectsRepository', () => { }); it(`throws when the type is multi-namespace and the document exists, but not in this namespace`, async () => { - const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id, namespace }); + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }, namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -2660,14 +2736,16 @@ describe('SavedObjectsRepository', () => { const originId = 'some-origin-id'; const getSuccess = async (type, id, options, includeOriginId) => { - const response = getMockGetResponse({ - type, - id, - namespace: options?.namespace, - // "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the - // operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response. - ...(includeOriginId && { originId }), - }); + const response = getMockGetResponse( + { + type, + id, + // "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the + // operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response. + ...(includeOriginId && { originId }), + }, + options?.namespace + ); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -2702,6 +2780,16 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await getSuccess(type, id, { namespace: 'default' }); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { await getSuccess(NAMESPACE_AGNOSTIC_TYPE, id, { namespace }); expect(client.get).toHaveBeenCalledWith( @@ -2756,7 +2844,7 @@ describe('SavedObjectsRepository', () => { }); it(`throws when type is multi-namespace and the document exists, but not in this namespace`, async () => { - const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id, namespace }); + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }, namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -2812,7 +2900,7 @@ describe('SavedObjectsRepository', () => { const incrementCounterSuccess = async (type, id, field, options) => { const isMultiNamespace = registry.isMultiNamespace(type); if (isMultiNamespace) { - const response = getMockGetResponse({ type, id, namespace: options?.namespace }); + const response = getMockGetResponse({ type, id }, options?.namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -2883,6 +2971,16 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await incrementCounterSuccess(type, id, field, { namespace: 'default' }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { await incrementCounterSuccess(NAMESPACE_AGNOSTIC_TYPE, id, field, { namespace }); expect(client.update).toHaveBeenCalledWith( @@ -2949,11 +3047,7 @@ describe('SavedObjectsRepository', () => { }); it(`throws when there is a conflict with an existing multi-namespace saved object (get)`, async () => { - const response = getMockGetResponse({ - type: MULTI_NAMESPACE_TYPE, - id, - namespace: 'bar-namespace', - }); + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }, 'bar-namespace'); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); @@ -3246,7 +3340,7 @@ describe('SavedObjectsRepository', () => { expect(client.update).not.toHaveBeenCalled(); }); - it(`throws when type is not namespace-agnostic`, async () => { + it(`throws when type is not multi-namespace`, async () => { const test = async (type) => { const message = `${type} doesn't support multiple namespaces`; await expectBadRequestError(type, id, [namespace1, namespace2], message); @@ -3388,7 +3482,7 @@ describe('SavedObjectsRepository', () => { const updateSuccess = async (type, id, attributes, options, includeOriginId) => { if (registry.isMultiNamespace(type)) { - const mockGetResponse = getMockGetResponse({ type, id, namespace: options?.namespace }); + const mockGetResponse = getMockGetResponse({ type, id }, options?.namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(mockGetResponse) ); @@ -3519,6 +3613,14 @@ describe('SavedObjectsRepository', () => { ); }); + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await updateSuccess(type, id, attributes, { references, namespace: 'default' }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ id: expect.stringMatching(`${type}:${id}`) }), + expect.anything() + ); + }); + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { await updateSuccess(NAMESPACE_AGNOSTIC_TYPE, id, attributes, { namespace }); expect(client.update).toHaveBeenCalledWith( @@ -3589,7 +3691,7 @@ describe('SavedObjectsRepository', () => { }); it(`throws when type is multi-namespace and the document exists, but not in this namespace`, async () => { - const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id, namespace }); + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }, namespace); client.get.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index e3fb7d230646..125f97e7feb1 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -67,6 +67,7 @@ import { } from '../../types'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { validateConvertFilterToKueryNode } from './filter_utils'; +import { SavedObjectsUtils } from './utils'; // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. @@ -220,13 +221,13 @@ export class SavedObjectsRepository { const { id, migrationVersion, - namespace, overwrite = false, references = [], refresh = DEFAULT_REFRESH_SETTING, originId, version, } = options; + const namespace = normalizeNamespace(options.namespace); if (!this._allowedTypes.includes(type)) { throw SavedObjectsErrorHelpers.createUnsupportedTypeError(type); @@ -293,7 +294,8 @@ export class SavedObjectsRepository { objects: Array>, options: SavedObjectsCreateOptions = {} ): Promise> { - const { namespace, overwrite = false, refresh = DEFAULT_REFRESH_SETTING } = options; + const { overwrite = false, refresh = DEFAULT_REFRESH_SETTING } = options; + const namespace = normalizeNamespace(options.namespace); const time = this._getCurrentTime(); let bulkGetRequestIndexCounter = 0; @@ -468,7 +470,7 @@ export class SavedObjectsRepository { return { errors: [] }; } - const { namespace } = options; + const namespace = normalizeNamespace(options.namespace); let bulkGetRequestIndexCounter = 0; const expectedBulkGetResults: Either[] = objects.map((object) => { @@ -551,7 +553,8 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - const { namespace, refresh = DEFAULT_REFRESH_SETTING } = options; + const { refresh = DEFAULT_REFRESH_SETTING } = options; + const namespace = normalizeNamespace(options.namespace); const rawId = this._serializer.generateRawId(namespace, type, id); let preflightResult: SavedObjectsRawDoc | undefined; @@ -560,7 +563,7 @@ export class SavedObjectsRepository { preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult); const remainingNamespaces = existingNamespaces?.filter( - (x) => x !== getNamespaceString(namespace) + (x) => x !== SavedObjectsUtils.namespaceIdToString(namespace) ); if (remainingNamespaces?.length) { @@ -658,7 +661,7 @@ export class SavedObjectsRepository { } `, lang: 'painless', - params: { namespace: getNamespaceString(namespace) }, + params: { namespace }, }, conflicts: 'proceed', ...getSearchDsl(this._mappings, this._registry, { @@ -814,7 +817,7 @@ export class SavedObjectsRepository { objects: SavedObjectsBulkGetObject[] = [], options: SavedObjectsBaseOptions = {} ): Promise> { - const { namespace } = options; + const namespace = normalizeNamespace(options.namespace); if (objects.length === 0) { return { saved_objects: [] }; @@ -884,7 +887,9 @@ export class SavedObjectsRepository { const { originId, updated_at: updatedAt } = doc._source; let namespaces = []; if (!this._registry.isNamespaceAgnostic(type)) { - namespaces = doc._source.namespaces ?? [getNamespaceString(doc._source.namespace)]; + namespaces = doc._source.namespaces ?? [ + SavedObjectsUtils.namespaceIdToString(doc._source.namespace), + ]; } return { @@ -920,7 +925,7 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - const { namespace } = options; + const namespace = normalizeNamespace(options.namespace); const { body, statusCode } = await this.client.get>( { @@ -941,7 +946,9 @@ export class SavedObjectsRepository { let namespaces: string[] = []; if (!this._registry.isNamespaceAgnostic(type)) { - namespaces = body._source.namespaces ?? [getNamespaceString(body._source.namespace)]; + namespaces = body._source.namespaces ?? [ + SavedObjectsUtils.namespaceIdToString(body._source.namespace), + ]; } return { @@ -978,7 +985,8 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - const { version, namespace, references, refresh = DEFAULT_REFRESH_SETTING } = options; + const { version, references, refresh = DEFAULT_REFRESH_SETTING } = options; + const namespace = normalizeNamespace(options.namespace); let preflightResult: SavedObjectsRawDoc | undefined; if (this._registry.isMultiNamespace(type)) { @@ -1016,7 +1024,9 @@ export class SavedObjectsRepository { const { originId } = body.get._source; let namespaces = []; if (!this._registry.isNamespaceAgnostic(type)) { - namespaces = body.get._source.namespaces ?? [getNamespaceString(body.get._source.namespace)]; + namespaces = body.get._source.namespaces ?? [ + SavedObjectsUtils.namespaceIdToString(body.get._source.namespace), + ]; } return { @@ -1060,6 +1070,7 @@ export class SavedObjectsRepository { } const { version, namespace, refresh = DEFAULT_REFRESH_SETTING } = options; + // we do not need to normalize the namespace to its ID format, since it will be converted to a namespace string before being used const rawId = this._serializer.generateRawId(undefined, type, id); const preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); @@ -1122,6 +1133,7 @@ export class SavedObjectsRepository { } const { namespace, refresh = DEFAULT_REFRESH_SETTING } = options; + // we do not need to normalize the namespace to its ID format, since it will be converted to a namespace string before being used const rawId = this._serializer.generateRawId(undefined, type, id); const preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); @@ -1208,7 +1220,7 @@ export class SavedObjectsRepository { options: SavedObjectsBulkUpdateOptions = {} ): Promise> { const time = this._getCurrentTime(); - const { namespace } = options; + const namespace = normalizeNamespace(options.namespace); let bulkGetRequestIndexCounter = 0; const expectedBulkGetResults: Either[] = objects.map((object) => { @@ -1225,7 +1237,9 @@ export class SavedObjectsRepository { }; } - const { attributes, references, version } = object; + const { attributes, references, version, namespace: objectNamespace } = object; + // `objectNamespace` is a namespace string, while `namespace` is a namespace ID. + // The object namespace string, if defined, will supersede the operation's namespace ID. const documentToSave = { [type]: attributes, @@ -1242,16 +1256,24 @@ export class SavedObjectsRepository { id, version, documentToSave, + objectNamespace, ...(requiresNamespacesCheck && { esRequestIndex: bulkGetRequestIndexCounter++ }), }, }; }); + const getNamespaceId = (objectNamespace?: string) => + objectNamespace !== undefined + ? SavedObjectsUtils.namespaceStringToId(objectNamespace) + : namespace; + const getNamespaceString = (objectNamespace?: string) => + objectNamespace ?? SavedObjectsUtils.namespaceIdToString(namespace); + const bulkGetDocs = expectedBulkGetResults .filter(isRight) .filter(({ value }) => value.esRequestIndex !== undefined) - .map(({ value: { type, id } }) => ({ - _id: this._serializer.generateRawId(namespace, type, id), + .map(({ value: { type, id, objectNamespace } }) => ({ + _id: this._serializer.generateRawId(getNamespaceId(objectNamespace), type, id), _index: this.getIndexForType(type), _source: ['type', 'namespaces'], })); @@ -1276,14 +1298,25 @@ export class SavedObjectsRepository { return expectedBulkGetResult; } - const { esRequestIndex, id, type, version, documentToSave } = expectedBulkGetResult.value; + const { + esRequestIndex, + id, + type, + version, + documentToSave, + objectNamespace, + } = expectedBulkGetResult.value; + let namespaces; let versionProperties; if (esRequestIndex !== undefined) { const indexFound = bulkGetResponse?.statusCode !== 404; const actualResult = indexFound ? bulkGetResponse?.body.docs[esRequestIndex] : undefined; const docFound = indexFound && actualResult.found === true; - if (!docFound || !this.rawDocExistsInNamespace(actualResult, namespace)) { + if ( + !docFound || + !this.rawDocExistsInNamespace(actualResult, getNamespaceId(objectNamespace)) + ) { return { tag: 'Left' as 'Left', error: { @@ -1294,12 +1327,13 @@ export class SavedObjectsRepository { }; } namespaces = actualResult._source.namespaces ?? [ - getNamespaceString(actualResult._source.namespace), + SavedObjectsUtils.namespaceIdToString(actualResult._source.namespace), ]; versionProperties = getExpectedVersionProperties(version, actualResult); } else { if (this._registry.isSingleNamespace(type)) { - namespaces = [getNamespaceString(namespace)]; + // if `objectNamespace` is undefined, fall back to `options.namespace` + namespaces = [getNamespaceString(objectNamespace)]; } versionProperties = getExpectedVersionProperties(version); } @@ -1315,7 +1349,7 @@ export class SavedObjectsRepository { bulkUpdateParams.push( { update: { - _id: this._serializer.generateRawId(namespace, type, id), + _id: this._serializer.generateRawId(getNamespaceId(objectNamespace), type, id), _index: this.getIndexForType(type), ...versionProperties, }, @@ -1401,7 +1435,8 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createUnsupportedTypeError(type); } - const { migrationVersion, namespace, refresh = DEFAULT_REFRESH_SETTING } = options; + const { migrationVersion, refresh = DEFAULT_REFRESH_SETTING } = options; + const namespace = normalizeNamespace(options.namespace); const time = this._getCurrentTime(); let savedObjectNamespace; @@ -1495,7 +1530,7 @@ export class SavedObjectsRepository { const savedObject = this._serializer.rawToSavedObject(raw); const { namespace, type } = savedObject; if (this._registry.isSingleNamespace(type)) { - savedObject.namespaces = [getNamespaceString(namespace)]; + savedObject.namespaces = [SavedObjectsUtils.namespaceIdToString(namespace)]; } return omit(savedObject, 'namespace') as SavedObject; } @@ -1518,7 +1553,7 @@ export class SavedObjectsRepository { } const namespaces = raw._source.namespaces; - return namespaces?.includes(getNamespaceString(namespace)) ?? false; + return namespaces?.includes(SavedObjectsUtils.namespaceIdToString(namespace)) ?? false; } /** @@ -1623,14 +1658,6 @@ function getExpectedVersionProperties(version?: string, document?: SavedObjectsR return {}; } -/** - * Returns the string representation of a namespace. - * The default namespace is undefined, and is represented by the string 'default'. - */ -function getNamespaceString(namespace?: string) { - return namespace ?? 'default'; -} - /** * Returns a string array of namespaces for a given saved object. If the saved object is undefined, the result is an array that contains the * current namespace. Value may be undefined if an existing saved object has no namespaces attribute; this should not happen in normal @@ -1646,9 +1673,16 @@ function getSavedObjectNamespaces( if (document) { return document._source?.namespaces; } - return [getNamespaceString(namespace)]; + return [SavedObjectsUtils.namespaceIdToString(namespace)]; } +/** + * Ensure that a namespace is always in its namespace ID representation. + * This allows `'default'` to be used interchangeably with `undefined`. + */ +const normalizeNamespace = (namespace?: string) => + namespace === undefined ? namespace : SavedObjectsUtils.namespaceStringToId(namespace); + /** * Extracts the contents of a decorated error to return the attributes for bulk operations. */ diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index ad1a08187dc3..3ff72a86c2f8 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -21,6 +21,7 @@ import { esKuery, KueryNode } from '../../../../../../plugins/data/server'; import { getRootPropertiesObjects, IndexMapping } from '../../../mappings'; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; +import { DEFAULT_NAMESPACE_STRING } from '../utils'; /** * Gets the types based on the type. Uses mappings to support @@ -73,7 +74,7 @@ function getFieldsForTypes( */ function getClauseForType( registry: ISavedObjectTypeRegistry, - namespaces: string[] = ['default'], + namespaces: string[] = [DEFAULT_NAMESPACE_STRING], type: string ) { if (namespaces.length === 0) { @@ -88,11 +89,11 @@ function getClauseForType( }; } else if (registry.isSingleNamespace(type)) { const should: Array> = []; - const eligibleNamespaces = namespaces.filter((namespace) => namespace !== 'default'); + const eligibleNamespaces = namespaces.filter((x) => x !== DEFAULT_NAMESPACE_STRING); if (eligibleNamespaces.length > 0) { should.push({ terms: { namespace: eligibleNamespaces } }); } - if (namespaces.includes('default')) { + if (namespaces.includes(DEFAULT_NAMESPACE_STRING)) { should.push({ bool: { must_not: [{ exists: { field: 'namespace' } }] } }); } if (should.length === 0) { @@ -162,9 +163,7 @@ export function getQueryParams({ // would result in no results being returned, as the wildcard is treated as a literal, and not _actually_ as a wildcard. // We had a good discussion around the tradeoffs here: https://github.com/elastic/kibana/pull/67644#discussion_r441055716 const normalizedNamespaces = namespaces - ? Array.from( - new Set(namespaces.map((namespace) => (namespace === '*' ? 'default' : namespace))) - ) + ? Array.from(new Set(namespaces.map((x) => (x === '*' ? DEFAULT_NAMESPACE_STRING : x)))) : undefined; const bool: any = { diff --git a/src/core/server/saved_objects/service/lib/utils.test.ts b/src/core/server/saved_objects/service/lib/utils.test.ts new file mode 100644 index 000000000000..ea4fa68242be --- /dev/null +++ b/src/core/server/saved_objects/service/lib/utils.test.ts @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectsUtils } from './utils'; + +describe('SavedObjectsUtils', () => { + const { namespaceIdToString, namespaceStringToId } = SavedObjectsUtils; + + describe('#namespaceIdToString', () => { + it('converts `undefined` to default namespace string', () => { + expect(namespaceIdToString(undefined)).toEqual('default'); + }); + + it('leaves other namespace IDs as-is', () => { + expect(namespaceIdToString('foo')).toEqual('foo'); + }); + + it('throws an error when a namespace ID is an empty string', () => { + expect(() => namespaceIdToString('')).toThrowError('namespace cannot be an empty string'); + }); + }); + + describe('#namespaceStringToId', () => { + it('converts default namespace string to `undefined`', () => { + expect(namespaceStringToId('default')).toBeUndefined(); + }); + + it('leaves other namespace strings as-is', () => { + expect(namespaceStringToId('foo')).toEqual('foo'); + }); + + it('throws an error when a namespace string is falsy', () => { + const test = (arg: any) => + expect(() => namespaceStringToId(arg)).toThrowError('namespace must be a non-empty string'); + + test(undefined); + test(null); + test(''); + }); + }); +}); diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/src/core/server/saved_objects/service/lib/utils.ts new file mode 100644 index 000000000000..6101ad57cc40 --- /dev/null +++ b/src/core/server/saved_objects/service/lib/utils.ts @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const DEFAULT_NAMESPACE_STRING = 'default'; + +/** + * @public + */ +export class SavedObjectsUtils { + /** + * Converts a given saved object namespace ID to its string representation. All namespace IDs have an identical string representation, with + * the exception of the `undefined` namespace ID (which has a namespace string of `'default'`). + * + * @param namespace The namespace ID, which must be either a non-empty string or `undefined`. + */ + public static namespaceIdToString = (namespace?: string) => { + if (namespace === '') { + throw new TypeError('namespace cannot be an empty string'); + } + + return namespace ?? DEFAULT_NAMESPACE_STRING; + }; + + /** + * Converts a given saved object namespace string to its ID representation. All namespace strings have an identical ID representation, with + * the exception of the `'default'` namespace string (which has a namespace ID of `undefined`). + * + * @param namespace The namespace string, which must be non-empty. + */ + public static namespaceStringToId = (namespace: string) => { + if (!namespace) { + throw new TypeError('namespace must be a non-empty string'); + } + + return namespace !== DEFAULT_NAMESPACE_STRING ? namespace : undefined; + }; +} diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index 347c760f841b..8c96116de49c 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -80,6 +80,13 @@ export interface SavedObjectsBulkUpdateObject type: string; /** {@inheritdoc SavedObjectAttributes} */ attributes: Partial; + /** + * Optional namespace string to use when searching for this object. If this is defined, it will supersede the namespace ID that is in + * {@link SavedObjectsBulkUpdateOptions}. + * + * Note: the default namespace's string representation is `'default'`, and its ID representation is `undefined`. + **/ + namespace?: string; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index aef1bda9ccf4..ec457704e89c 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -2047,6 +2047,7 @@ export interface SavedObjectsBulkResponse { export interface SavedObjectsBulkUpdateObject extends Pick { attributes: Partial; id: string; + namespace?: string; type: string; } @@ -2630,6 +2631,12 @@ export interface SavedObjectsUpdateResponse extends Omit string; + static namespaceStringToId: (namespace: string) => string | undefined; +} + // @public export class SavedObjectTypeRegistry { getAllTypes(): SavedObjectsType[]; diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index d7f137e96532..b02b7cc16ec4 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -18,6 +18,8 @@ kibana_vars=( console.enabled console.proxyConfig console.proxyFilter + ops.cGroupOverrides.cpuPath + ops.cGroupOverrides.cpuAcctPath cpu.cgroup.path.override cpuacct.cgroup.path.override csp.rules @@ -279,4 +281,4 @@ umask 0002 # Therefore, we set this value here so that cgroup statistics are # available for the container this process will run in. -exec /usr/share/kibana/bin/kibana --cpu.cgroup.path.override=/ --cpuacct.cgroup.path.override=/ ${longopts} "$@" +exec /usr/share/kibana/bin/kibana --ops.cGroupOverrides.cpuPath=/ --ops.cGroupOverrides.cpuAcctPath=/ ${longopts} "$@" diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index dd65e45659ff..ce7a500a00dc 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -49,22 +49,6 @@ export default () => csp: HANDLED_IN_NEW_PLATFORM, - cpu: Joi.object({ - cgroup: Joi.object({ - path: Joi.object({ - override: Joi.string().default(), - }), - }), - }), - - cpuacct: Joi.object({ - cgroup: Joi.object({ - path: Joi.object({ - override: Joi.string().default(), - }), - }), - }), - server: Joi.object({ name: Joi.string().default(os.hostname()), // keep them for BWC, remove when not used in Legacy. @@ -144,6 +128,10 @@ export default () => ops: Joi.object({ interval: Joi.number().default(5000), + cGroupOverrides: Joi.object().keys({ + cpuPath: Joi.string().default(), + cpuAcctPath: Joi.string().default(), + }), }).default(), plugins: Joi.object({ diff --git a/src/legacy/server/status/lib/metrics.js b/src/legacy/server/status/lib/metrics.js index 2631b245e72a..478bf0829b1a 100644 --- a/src/legacy/server/status/lib/metrics.js +++ b/src/legacy/server/status/lib/metrics.js @@ -116,8 +116,8 @@ export class Metrics { async captureCGroups() { try { const cgroup = await cGroupStats({ - cpuPath: this.config.get('cpu.cgroup.path.override'), - cpuAcctPath: this.config.get('cpuacct.cgroup.path.override'), + cpuPath: this.config.get('ops.cGroupOverrides.cpuPath'), + cpuAcctPath: this.config.get('ops.cGroupOverrides.cpuAcctPath'), }); if (isObject(cgroup)) { diff --git a/src/plugins/dashboard/kibana.json b/src/plugins/dashboard/kibana.json index 1b38c6d124fe..531074f9fa60 100644 --- a/src/plugins/dashboard/kibana.json +++ b/src/plugins/dashboard/kibana.json @@ -6,6 +6,7 @@ "embeddable", "inspector", "kibanaLegacy", + "urlForwarding", "navigation", "uiActions", "savedObjects" diff --git a/src/plugins/dashboard/public/application/application.ts b/src/plugins/dashboard/public/application/application.ts index 21f423d009ee..b0a5b0472ec4 100644 --- a/src/plugins/dashboard/public/application/application.ts +++ b/src/plugins/dashboard/public/application/application.ts @@ -41,6 +41,7 @@ import { NavigationPublicPluginStart as NavigationStart } from '../../../navigat import { DataPublicPluginStart } from '../../../data/public'; import { SharePluginStart } from '../../../share/public'; import { KibanaLegacyStart, configureAppAngularModule } from '../../../kibana_legacy/public'; +import { UrlForwardingStart } from '../../../url_forwarding/public'; import { SavedObjectLoader, SavedObjectsStart } from '../../../saved_objects/public'; // required for i18nIdDirective @@ -69,8 +70,8 @@ export interface RenderDeps { localStorage: Storage; share?: SharePluginStart; usageCollection?: UsageCollectionSetup; - navigateToDefaultApp: KibanaLegacyStart['navigateToDefaultApp']; - navigateToLegacyKibanaUrl: KibanaLegacyStart['navigateToLegacyKibanaUrl']; + navigateToDefaultApp: UrlForwardingStart['navigateToDefaultApp']; + navigateToLegacyKibanaUrl: UrlForwardingStart['navigateToLegacyKibanaUrl']; scopedHistory: () => ScopedHistory; savedObjects: SavedObjectsStart; restorePreviousUrl: () => void; diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 212b54be9ae0..92d6f2ed91dd 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -88,8 +88,8 @@ import { AngularHttpError, KibanaLegacyStart, subscribeWithScope, - migrateLegacyQuery, } from '../../../kibana_legacy/public'; +import { migrateLegacyQuery } from './lib/migrate_legacy_query'; export interface DashboardAppControllerDependencies extends RenderDeps { $scope: DashboardAppScope; diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index 5fed38487dc5..910a2b470b2e 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -25,7 +25,7 @@ import { History } from 'history'; import { Filter, Query, TimefilterContract as Timefilter } from 'src/plugins/data/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; -import { migrateLegacyQuery } from '../../../kibana_legacy/public'; +import { migrateLegacyQuery } from './lib/migrate_legacy_query'; import { ViewMode } from '../embeddable_plugin'; import { getAppStateDefaults, migrateAppState, getDashboardIdFromUrl } from './lib'; diff --git a/src/plugins/kibana_legacy/common/migrate_legacy_query.ts b/src/plugins/dashboard/public/application/lib/migrate_legacy_query.ts similarity index 100% rename from src/plugins/kibana_legacy/common/migrate_legacy_query.ts rename to src/plugins/dashboard/public/application/lib/migrate_legacy_query.ts diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 0ce6f9489ea0..49584f62215e 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -33,6 +33,7 @@ import { SavedObjectsClientContract, ScopedHistory, } from 'src/core/public'; +import { UrlForwardingSetup, UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; import { CONTEXT_MENU_TRIGGER, @@ -125,6 +126,7 @@ interface SetupDependencies { embeddable: EmbeddableSetup; home?: HomePublicPluginSetup; kibanaLegacy: KibanaLegacySetup; + urlForwarding: UrlForwardingSetup; share?: SharePluginSetup; uiActions: UiActionsSetup; usageCollection?: UsageCollectionSetup; @@ -133,6 +135,7 @@ interface SetupDependencies { interface StartDependencies { data: DataPublicPluginStart; kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; embeddable: EmbeddableStart; inspector: InspectorStartContract; navigation: NavigationStart; @@ -190,7 +193,16 @@ export class DashboardPlugin public setup( core: CoreSetup, - { share, uiActions, embeddable, home, kibanaLegacy, data, usageCollection }: SetupDependencies + { + share, + uiActions, + embeddable, + home, + kibanaLegacy, + urlForwarding, + data, + usageCollection, + }: SetupDependencies ): Setup { this.dashboardFeatureFlagConfig = this.initializerContext.config.get< DashboardFeatureFlagConfig @@ -311,7 +323,8 @@ export class DashboardPlugin navigation, share: shareStart, data: dataStart, - kibanaLegacy: { dashboardConfig, navigateToDefaultApp, navigateToLegacyKibanaUrl }, + kibanaLegacy: { dashboardConfig }, + urlForwarding: { navigateToDefaultApp, navigateToLegacyKibanaUrl }, savedObjects, } = pluginsStart; @@ -357,7 +370,7 @@ export class DashboardPlugin initAngularBootstrap(); core.application.register(app); - kibanaLegacy.forwardApp( + urlForwarding.forwardApp( DashboardConstants.DASHBOARDS_ID, DashboardConstants.DASHBOARDS_ID, (path) => { @@ -366,7 +379,7 @@ export class DashboardPlugin return `#/list${tail || ''}`; } ); - kibanaLegacy.forwardApp( + urlForwarding.forwardApp( DashboardConstants.DASHBOARD_ID, DashboardConstants.DASHBOARDS_ID, (path) => { diff --git a/src/plugins/data/common/search/aggs/types.ts b/src/plugins/data/common/search/aggs/types.ts index dabd653463d4..aec3dcc9d068 100644 --- a/src/plugins/data/common/search/aggs/types.ts +++ b/src/plugins/data/common/search/aggs/types.ts @@ -93,7 +93,7 @@ export interface AggsCommonStart { * is only used internally. The difference is that AggsStart includes the * typings for the registry with initialized agg types. * - * @internal + * @public */ export type AggsStart = Assign; diff --git a/src/plugins/data/kibana.json b/src/plugins/data/kibana.json index b4f20ec6225e..9cb9b1745373 100644 --- a/src/plugins/data/kibana.json +++ b/src/plugins/data/kibana.json @@ -13,7 +13,6 @@ "usageCollection", "kibanaUtils", "kibanaReact", - "kibanaLegacy", "inspector" ] } diff --git a/src/plugins/data/public/actions/apply_filter_action.ts b/src/plugins/data/public/actions/apply_filter_action.ts index a2621e6ce880..944da72bd11d 100644 --- a/src/plugins/data/public/actions/apply_filter_action.ts +++ b/src/plugins/data/public/actions/apply_filter_action.ts @@ -44,6 +44,7 @@ export function createFilterAction( return createAction({ type: ACTION_GLOBAL_APPLY_FILTER, id: ACTION_GLOBAL_APPLY_FILTER, + order: 100, getIconType: () => 'filter', getDisplayName: () => { return i18n.translate('data.filter.applyFilterActionTitle', { diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 553ee6bde5f2..5038af940931 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -172,7 +172,7 @@ import { } from '../common/field_formats'; import { DateNanosFormat, DateFormat } from './field_formats'; -export { baseFormattersPublic } from './field_formats'; +export { baseFormattersPublic, FieldFormatsStart } from './field_formats'; // Field formats helpers namespace: export const fieldFormats = { @@ -276,6 +276,7 @@ export { QuerySuggestionGetFnArgs, QuerySuggestionBasic, QuerySuggestionField, + AutocompleteStart, } from './autocomplete'; /* @@ -313,6 +314,7 @@ import { export { // aggs + AggConfigSerialized, AggGroupLabels, AggGroupName, AggGroupNames, @@ -337,6 +339,8 @@ export { TabbedTable, } from '../common'; +export type { AggConfigs, AggConfig } from '../common'; + export { // search ES_SEARCH_STRATEGY, @@ -350,6 +354,9 @@ export { IKibanaSearchResponse, injectSearchSourceReferences, ISearch, + ISearchSetup, + ISearchStart, + ISearchStartSearchSource, ISearchGeneric, ISearchSource, parseSearchSourceJSON, @@ -365,6 +372,8 @@ export { EsRawResponseExpressionTypeDefinition, } from './search'; +export type { SearchSource } from './search'; + export { ISearchOptions } from '../common'; // Search namespace @@ -430,8 +439,11 @@ export { TimefilterContract, TimeHistoryContract, QueryStateChange, + QueryStart, } from './query'; +export { AggsStart } from './search/aggs'; + export { getTime, // kbn field types @@ -455,7 +467,13 @@ export function plugin(initializerContext: PluginInitializerContext[]; + // (undocumented) + getField(): any; + // (undocumented) + getFieldDisplayName(): any; + // (undocumented) + getIndexPattern(): import("../../../public").IndexPattern; + // (undocumented) + getKey(bucket: any, key?: string): any; + // (undocumented) + getParam(key: string): any; + // (undocumented) + getRequestAggs(): AggConfig[]; + // (undocumented) + getResponseAggs(): AggConfig[]; + // (undocumented) + getTimeRange(): import("../../../public").TimeRange | undefined; + // (undocumented) + getValue(bucket: any): any; + // (undocumented) + id: string; + // (undocumented) + isFilterable(): boolean; + // (undocumented) + makeLabel(percentageMode?: boolean): any; + static nextId(list: IAggConfig[]): number; + onSearchRequestStart(searchSource: ISearchSource_2, options?: ISearchOptions): Promise | Promise; + // (undocumented) + params: any; + // Warning: (ae-incompatible-release-tags) The symbol "parent" is marked as @public, but its signature references "IAggConfigs" which is marked as @internal + // + // (undocumented) + parent?: IAggConfigs; + // (undocumented) + schema?: string; + // Warning: (ae-incompatible-release-tags) The symbol "serialize" is marked as @public, but its signature references "AggConfigSerialized" which is marked as @internal + // + // (undocumented) + serialize(): AggConfigSerialized; + setParams(from: any): void; + // (undocumented) + setType(type: IAggType): void; + // Warning: (ae-incompatible-release-tags) The symbol "toDsl" is marked as @public, but its signature references "IAggConfigs" which is marked as @internal + toDsl(aggConfigs?: IAggConfigs): any; + // (undocumented) + toExpressionAst(): ExpressionAstFunction | undefined; + // Warning: (ae-incompatible-release-tags) The symbol "toJSON" is marked as @public, but its signature references "AggConfigSerialized" which is marked as @internal + // + // @deprecated (undocumented) + toJSON(): AggConfigSerialized; + // Warning: (ae-forgotten-export) The symbol "SerializableState" needs to be exported by the entry point index.d.ts + toSerializedFieldFormat(): {} | Ensure, SerializableState>; + // (undocumented) + get type(): IAggType; + set type(type: IAggType); + // Warning: (ae-incompatible-release-tags) The symbol "write" is marked as @public, but its signature references "IAggConfigs" which is marked as @internal + // + // (undocumented) + write(aggs?: IAggConfigs): Record; +} + +// Warning: (ae-incompatible-release-tags) The symbol "AggConfigOptions" is marked as @public, but its signature references "AggConfigSerialized" which is marked as @internal // Warning: (ae-missing-release-tag) "AggConfigOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -92,6 +174,76 @@ export type AggConfigOptions = Assign; +// Warning: (ae-missing-release-tag) "AggConfigs" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class AggConfigs { + // Warning: (ae-forgotten-export) The symbol "AggConfigsOptions" needs to be exported by the entry point index.d.ts + constructor(indexPattern: IndexPattern, configStates: Pick & Pick<{ + type: string | IAggType; + }, "type"> & Pick<{ + type: string | IAggType; + }, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined, opts: AggConfigsOptions); + // (undocumented) + aggs: IAggConfig[]; + // (undocumented) + byId(id: string): AggConfig | undefined; + // (undocumented) + byIndex(index: number): AggConfig; + // (undocumented) + byName(name: string): AggConfig[]; + // (undocumented) + bySchemaName(schema: string): AggConfig[]; + // (undocumented) + byType(type: string): AggConfig[]; + // (undocumented) + byTypeName(type: string): AggConfig[]; + // (undocumented) + clone({ enabledOnly }?: { + enabledOnly?: boolean | undefined; + }): AggConfigs; + // Warning: (ae-forgotten-export) The symbol "CreateAggConfigParams" needs to be exported by the entry point index.d.ts + // + // (undocumented) + createAggConfig: (params: CreateAggConfigParams, { addToAggConfigs }?: { + addToAggConfigs?: boolean | undefined; + }) => T; + // (undocumented) + getAll(): AggConfig[]; + // (undocumented) + getRequestAggById(id: string): AggConfig | undefined; + // (undocumented) + getRequestAggs(): AggConfig[]; + getResponseAggById(id: string): AggConfig | undefined; + getResponseAggs(): AggConfig[]; + // (undocumented) + indexPattern: IndexPattern; + jsonDataEquals(aggConfigs: AggConfig[]): boolean; + // (undocumented) + onSearchRequestStart(searchSource: ISearchSource_2, options?: ISearchOptions_2): Promise<[unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown]>; + // (undocumented) + setTimeRange(timeRange: TimeRange): void; + // (undocumented) + timeRange?: TimeRange; + // (undocumented) + toDsl(hierarchical?: boolean): Record; + } + +// @internal (undocumented) +export type AggConfigSerialized = Ensure<{ + type: string; + enabled?: boolean; + id?: string; + params?: {} | SerializableState; + schema?: string; +}, SerializableState>; + // Warning: (ae-missing-release-tag) "AggGroupLabels" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -127,8 +279,6 @@ export type AggParam = BaseParamType; export interface AggParamOption { // (undocumented) display: string; - // Warning: (ae-forgotten-export) The symbol "AggConfig" needs to be exported by the entry point index.d.ts - // // (undocumented) enabled?(agg: AggConfig): boolean; // (undocumented) @@ -142,10 +292,19 @@ export class AggParamType extends Ba constructor(config: Record); // (undocumented) allowedAggs: string[]; + // Warning: (ae-incompatible-release-tags) The symbol "makeAgg" is marked as @public, but its signature references "AggConfigSerialized" which is marked as @internal + // // (undocumented) makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; } +// Warning: (ae-forgotten-export) The symbol "AggsCommonStart" needs to be exported by the entry point index.d.ts +// +// @public +export type AggsStart = Assign; + // Warning: (ae-missing-release-tag) "ApplyGlobalFilterActionContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -160,6 +319,11 @@ export interface ApplyGlobalFilterActionContext { timeFieldName?: string; } +// Warning: (ae-forgotten-export) The symbol "AutocompleteService" needs to be exported by the entry point index.d.ts +// +// @public (undocumented) +export type AutocompleteStart = ReturnType; + // Warning: (ae-forgotten-export) The symbol "DateFormat" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "DateNanosFormat" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -200,7 +364,6 @@ export enum BUCKET_TYPES { // @public export const castEsToKbnFieldTypeName: (esType: ES_FIELD_TYPES | string) => KBN_FIELD_TYPES; -// Warning: (ae-forgotten-export) The symbol "QueryStart" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "QuerySetup" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "BaseStateContainer" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "connectToQueryState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -227,7 +390,7 @@ export type CustomFilter = Filter & { // Warning: (ae-missing-release-tag) "DataPublicPluginSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface DataPublicPluginSetup { // Warning: (ae-forgotten-export) The symbol "DataPublicPluginEnhancements" needs to be exported by the entry point index.d.ts // @@ -243,42 +406,47 @@ export interface DataPublicPluginSetup { fieldFormats: FieldFormatsSetup; // (undocumented) query: QuerySetup; - // Warning: (ae-forgotten-export) The symbol "ISearchSetup" needs to be exported by the entry point index.d.ts - // // (undocumented) search: ISearchSetup; } // Warning: (ae-missing-release-tag) "DataPublicPluginStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface DataPublicPluginStart { - // (undocumented) - actions: { - createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; - createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; - }; - // Warning: (ae-forgotten-export) The symbol "AutocompleteStart" needs to be exported by the entry point index.d.ts - // - // (undocumented) + actions: DataPublicPluginStartActions; autocomplete: AutocompleteStart; - // Warning: (ae-forgotten-export) The symbol "FieldFormatsStart" needs to be exported by the entry point index.d.ts - // - // (undocumented) fieldFormats: FieldFormatsStart; - // (undocumented) indexPatterns: IndexPatternsContract; - // (undocumented) query: QueryStart; - // Warning: (ae-forgotten-export) The symbol "ISearchStart" needs to be exported by the entry point index.d.ts + search: ISearchStart; + ui: DataPublicPluginStartUi; +} + +// Warning: (ae-missing-release-tag) "DataPublicPluginStartActions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface DataPublicPluginStartActions { + // Warning: (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts // // (undocumented) - search: ISearchStart; + createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; + // Warning: (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts + // // (undocumented) - ui: { - IndexPatternSelect: React.ComponentType; - SearchBar: React.ComponentType; - }; + createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; +} + +// Warning: (ae-missing-release-tag) "DataPublicPluginStartUi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export interface DataPublicPluginStartUi { + // Warning: (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts + // + // (undocumented) + IndexPatternSelect: React.ComponentType; + // (undocumented) + SearchBar: React.ComponentType; } // @public (undocumented) @@ -595,6 +763,11 @@ export type FieldFormatsContentType = 'html' | 'text'; // @public (undocumented) export type FieldFormatsGetConfigFn = GetConfigFn; +// @public (undocumented) +export type FieldFormatsStart = Omit & { + deserialize: FormatFactory; +}; + // Warning: (ae-forgotten-export) The symbol "FieldSpec" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "fieldList" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -709,8 +882,6 @@ export function getTime(indexPattern: IIndexPattern | undefined, timeRange: Time // @public export type IAggConfig = AggConfig; -// Warning: (ae-forgotten-export) The symbol "AggConfigs" needs to be exported by the entry point index.d.ts -// // @internal export type IAggConfigs = AggConfigs; @@ -1231,11 +1402,40 @@ export interface ISearchOptions { strategy?: string; } -// Warning: (ae-forgotten-export) The symbol "SearchSource" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "ISearchSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public +export interface ISearchSetup { + // Warning: (ae-forgotten-export) The symbol "SearchEnhancements" needs to be exported by the entry point index.d.ts + // + // @internal (undocumented) + __enhance: (enhancements: SearchEnhancements) => void; + // Warning: (ae-forgotten-export) The symbol "AggsSetup" needs to be exported by the entry point index.d.ts + // + // (undocumented) + aggs: AggsSetup; + // Warning: (ae-forgotten-export) The symbol "SearchUsageCollector" needs to be exported by the entry point index.d.ts + // + // (undocumented) + usageCollector?: SearchUsageCollector; +} + +// @public export type ISearchSource = Pick; +// @public +export interface ISearchStart { + aggs: AggsStart; + search: ISearchGeneric; + searchSource: ISearchStartSearchSource; +} + +// @public +export interface ISearchStartSearchSource { + create: (fields?: SearchSourceFields) => Promise; + createEmpty: () => ISearchSource; +} + // Warning: (ae-missing-release-tag) "isFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1450,6 +1650,12 @@ export interface Query { }; } +// Warning: (ae-forgotten-export) The symbol "QueryService" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "QueryStart" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type QueryStart = ReturnType; + // Warning: (ae-missing-release-tag) "QueryState" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -1479,7 +1685,7 @@ export interface QueryStateChange extends QueryStateChangePartial { // Warning: (ae-missing-release-tag) "QueryStringInput" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const QueryStringInput: React.FC>; +export const QueryStringInput: React.FC>; // @public (undocumented) export type QuerySuggestion = QuerySuggestionBasic | QuerySuggestionField; @@ -1732,11 +1938,6 @@ export class SearchInterceptor { protected application: CoreStart['application']; // (undocumented) protected readonly deps: SearchInterceptorDeps; - getPendingCount$(): Observable; - // @internal (undocumented) - protected hideToast: () => void; - // @internal - protected longRunningToast?: Toast; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) @@ -1750,8 +1951,8 @@ export class SearchInterceptor { combinedSignal: AbortSignal; cleanup: () => void; }; - // @internal (undocumented) - protected showToast: () => void; + // (undocumented) + protected showTimeoutError: ((e: Error) => void) & import("lodash").Cancelable; // @internal protected timeoutSubscriptions: Subscription; } @@ -1768,8 +1969,6 @@ export interface SearchInterceptorDeps { toasts: ToastsSetup; // (undocumented) uiSettings: CoreSetup_2['uiSettings']; - // Warning: (ae-forgotten-export) The symbol "SearchUsageCollector" needs to be exported by the entry point index.d.ts - // // (undocumented) usageCollector?: SearchUsageCollector; } @@ -1777,9 +1976,59 @@ export interface SearchInterceptorDeps { // @internal export type SearchRequest = Record; +// @public (undocumented) +export class SearchSource { + // Warning: (ae-forgotten-export) The symbol "SearchSourceDependencies" needs to be exported by the entry point index.d.ts + constructor(fields: SearchSourceFields | undefined, dependencies: SearchSourceDependencies); + // @deprecated (undocumented) + create(): SearchSource; + createChild(options?: {}): SearchSource; + createCopy(): SearchSource; + destroy(): void; + fetch(options?: ISearchOptions): Promise>; + getField(field: K, recurse?: boolean): SearchSourceFields[K]; + getFields(): { + type?: string | undefined; + query?: import("../..").Query | undefined; + filter?: Filter | Filter[] | (() => Filter | Filter[] | undefined) | undefined; + sort?: Record | Record[] | undefined; + highlight?: any; + highlightAll?: boolean | undefined; + aggs?: any; + from?: number | undefined; + size?: number | undefined; + source?: string | boolean | string[] | undefined; + version?: boolean | undefined; + fields?: string | boolean | string[] | undefined; + index?: import("../..").IndexPattern | undefined; + searchAfter?: import("./types").EsQuerySearchAfter | undefined; + timeout?: string | undefined; + terminate_after?: number | undefined; + }; + getId(): string; + getOwnField(field: K): SearchSourceFields[K]; + getParent(): SearchSource | undefined; + getSearchRequestBody(): Promise; + getSerializedFields(): SearchSourceFields; + // Warning: (ae-incompatible-release-tags) The symbol "history" is marked as @public, but its signature references "SearchRequest" which is marked as @internal + // + // (undocumented) + history: SearchRequest[]; + onRequestStart(handler: (searchSource: SearchSource, options?: ISearchOptions) => Promise): void; + serialize(): { + searchSourceJSON: string; + references: import("../../../../../core/public").SavedObjectReference[]; + }; + setField(field: K, value: SearchSourceFields[K]): this; + setFields(newFields: SearchSourceFields): this; + // Warning: (ae-forgotten-export) The symbol "SearchSourceOptions" needs to be exported by the entry point index.d.ts + setParent(parent?: ISearchSource, options?: SearchSourceOptions): this; + setPreferredSearchStrategyId(searchStrategyId: string): void; +} + // Warning: (ae-missing-release-tag) "SearchSourceFields" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface SearchSourceFields { // (undocumented) aggs?: any; @@ -1793,6 +2042,8 @@ export interface SearchSourceFields { highlight?: any; // (undocumented) highlightAll?: boolean; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "IndexPatternService" + // // (undocumented) index?: IndexPattern; // (undocumented) @@ -1944,6 +2195,8 @@ export const UI_SETTINGS: { // src/plugins/data/common/es_query/filters/match_all_filter.ts:28:3 - (ae-forgotten-export) The symbol "MatchAllFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:33:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:31:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/search/aggs/types.ts:98:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/field_formats/field_formats_service.ts:67:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FilterLabel" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "FILTERS" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:66:23 - (ae-forgotten-export) The symbol "getDisplayValueFromFilter" needs to be exported by the entry point index.d.ts @@ -1976,25 +2229,22 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getFromSavedObject" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:371:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:373:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:374:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:382:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:395:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:45:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/types.ts:62:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/types.ts:63:5 - (ae-forgotten-export) The symbol "createFiltersFromRangeSelectAction" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/types.ts:71:5 - (ae-forgotten-export) The symbol "IndexPatternSelectProps" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/search/README.md b/src/plugins/data/public/search/README.md index 33e6d9ab0bd1..0a123ffa3f1e 100644 --- a/src/plugins/data/public/search/README.md +++ b/src/plugins/data/public/search/README.md @@ -1,13 +1,23 @@ # search -The `search` plugin provides the ability to register search strategies that take in a request -object, and return a response object, of a given shape. +The `search` service provides you with APIs to query Elasticsearch. -Both client side search strategies can be registered, as well as server side search strategies. +The services are split into two parts: (1) low-level API; and (2) high-level API. -The `search` plugin includes two one concrete client side implementations - - `SYNC_SEARCH_STRATEGY` and `ES_SEARCH_STRATEGY` which uses `SYNC_SEARCH_STRATEGY`. There is also one - default server side search strategy, `ES_SEARCH_STRATEGY`. +## Low-level API - Includes the `esSearch` plugin in order to search for data from Elasticsearch using Elasticsearch -DSL. +With low level API you work directly with elasticsearch DSL + +```typescript +const results = await data.search.search(request, params); +``` + +## High-level API + +Using high-level API you work with Kibana abstractions around Elasticsearch DSL: filters, queries, and aggregations. Provided by the *Search Source* service. + +```typescript +const search = data.search.searchSource.createEmpty(); +search.setField('query', data.query.queryString); +const results = await search.fetch(); +``` diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts index 315d4678cabf..9cadb1e796ad 100644 --- a/src/plugins/data/public/search/collectors/create_usage_collector.test.ts +++ b/src/plugins/data/public/search/collectors/create_usage_collector.test.ts @@ -63,31 +63,4 @@ describe('Search Usage Collector', () => { SEARCH_EVENT_TYPE.QUERIES_CANCELLED ); }); - - test('tracks long popups', async () => { - await usageCollector.trackLongQueryPopupShown(); - expect(mockUsageCollectionSetup.reportUiStats).toHaveBeenCalled(); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][1]).toBe(METRIC_TYPE.LOADED); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][2]).toBe( - SEARCH_EVENT_TYPE.LONG_QUERY_POPUP_SHOWN - ); - }); - - test('tracks long popups dismissed', async () => { - await usageCollector.trackLongQueryDialogDismissed(); - expect(mockUsageCollectionSetup.reportUiStats).toHaveBeenCalled(); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][1]).toBe(METRIC_TYPE.CLICK); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][2]).toBe( - SEARCH_EVENT_TYPE.LONG_QUERY_DIALOG_DISMISSED - ); - }); - - test('tracks run query beyond timeout', async () => { - await usageCollector.trackLongQueryRunBeyondTimeout(); - expect(mockUsageCollectionSetup.reportUiStats).toHaveBeenCalled(); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][1]).toBe(METRIC_TYPE.CLICK); - expect(mockUsageCollectionSetup.reportUiStats.mock.calls[0][2]).toBe( - SEARCH_EVENT_TYPE.LONG_QUERY_RUN_BEYOND_TIMEOUT - ); - }); }); diff --git a/src/plugins/data/public/search/collectors/create_usage_collector.ts b/src/plugins/data/public/search/collectors/create_usage_collector.ts index 321b2c5b9904..187ed90652bb 100644 --- a/src/plugins/data/public/search/collectors/create_usage_collector.ts +++ b/src/plugins/data/public/search/collectors/create_usage_collector.ts @@ -48,29 +48,5 @@ export const createUsageCollector = ( SEARCH_EVENT_TYPE.QUERIES_CANCELLED ); }, - trackLongQueryPopupShown: async () => { - const currentApp = await getCurrentApp(); - return usageCollection?.reportUiStats( - currentApp!, - METRIC_TYPE.LOADED, - SEARCH_EVENT_TYPE.LONG_QUERY_POPUP_SHOWN - ); - }, - trackLongQueryDialogDismissed: async () => { - const currentApp = await getCurrentApp(); - return usageCollection?.reportUiStats( - currentApp!, - METRIC_TYPE.CLICK, - SEARCH_EVENT_TYPE.LONG_QUERY_DIALOG_DISMISSED - ); - }, - trackLongQueryRunBeyondTimeout: async () => { - const currentApp = await getCurrentApp(); - return usageCollection?.reportUiStats( - currentApp!, - METRIC_TYPE.CLICK, - SEARCH_EVENT_TYPE.LONG_QUERY_RUN_BEYOND_TIMEOUT - ); - }, }; }; diff --git a/src/plugins/data/public/search/collectors/types.ts b/src/plugins/data/public/search/collectors/types.ts index 3e98f901eb0c..bb7fa1e6ae4a 100644 --- a/src/plugins/data/public/search/collectors/types.ts +++ b/src/plugins/data/public/search/collectors/types.ts @@ -20,15 +20,9 @@ export enum SEARCH_EVENT_TYPE { QUERY_TIMED_OUT = 'queryTimedOut', QUERIES_CANCELLED = 'queriesCancelled', - LONG_QUERY_POPUP_SHOWN = 'longQueryPopupShown', - LONG_QUERY_DIALOG_DISMISSED = 'longQueryDialogDismissed', - LONG_QUERY_RUN_BEYOND_TIMEOUT = 'longQueryRunBeyondTimeout', } export interface SearchUsageCollector { trackQueryTimedOut: () => Promise; trackQueriesCancelled: () => Promise; - trackLongQueryPopupShown: () => Promise; - trackLongQueryDialogDismissed: () => Promise; - trackLongQueryRunBeyondTimeout: () => Promise; } diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index a6a1736ac91d..c1af9699acbb 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -19,7 +19,14 @@ export * from './expressions'; -export { ISearch, ISearchGeneric, ISearchSetup, ISearchStart, SearchEnhancements } from './types'; +export { + ISearch, + ISearchGeneric, + ISearchSetup, + ISearchStart, + ISearchStartSearchSource, + SearchEnhancements, +} from './types'; export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search'; diff --git a/src/plugins/data/public/search/long_query_notification.tsx b/src/plugins/data/public/search/long_query_notification.tsx deleted file mode 100644 index 1db298618fae..000000000000 --- a/src/plugins/data/public/search/long_query_notification.tsx +++ /dev/null @@ -1,59 +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 { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import { ApplicationStart } from 'kibana/public'; -import { toMountPoint } from '../../../kibana_react/public'; - -interface Props { - application: ApplicationStart; -} - -export function getLongQueryNotification(props: Props) { - return toMountPoint(); -} - -export function LongQueryNotification(props: Props) { - return ( -
- - - - - { - await props.application.navigateToApp('management/stack/license_management'); - }} - > - - - - -
- ); -} diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index 84db69a83a00..7bfa6f0ab1bc 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -95,6 +95,39 @@ describe('SearchInterceptor', () => { await flushPromises(); }); + test('Should not timeout if requestTimeout is undefined', async () => { + searchInterceptor = new SearchInterceptor({ + startServices: mockCoreSetup.getStartServices(), + uiSettings: mockCoreSetup.uiSettings, + http: mockCoreSetup.http, + toasts: mockCoreSetup.notifications.toasts, + }); + mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { + return new Promise((resolve, reject) => { + options.signal.addEventListener('abort', () => { + reject(new AbortError()); + }); + + setTimeout(resolve, 5000); + }); + }); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + + expect.assertions(1); + const next = jest.fn(); + const complete = () => { + expect(next).toBeCalled(); + }; + response.subscribe({ next, complete }); + + jest.advanceTimersByTime(5000); + + await flushPromises(); + }); + test('Observable should fail if user aborts (test merged signal)', async () => { const abortController = new AbortController(); mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { @@ -125,7 +158,7 @@ describe('SearchInterceptor', () => { await flushPromises(); }); - test('Immediatelly aborts if passed an aborted abort signal', async (done) => { + test('Immediately aborts if passed an aborted abort signal', async (done) => { const abort = new AbortController(); const mockRequest: IEsSearchRequest = { params: {}, @@ -141,44 +174,4 @@ describe('SearchInterceptor', () => { response.subscribe({ error }); }); }); - - describe('getPendingCount$', () => { - test('should observe the number of pending requests', () => { - const pendingCount$ = searchInterceptor.getPendingCount$(); - const pendingNext = jest.fn(); - pendingCount$.subscribe(pendingNext); - - const mockResponse: any = { result: 200 }; - mockCoreSetup.http.fetch.mockResolvedValue(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); - - response.subscribe({ - complete: () => { - expect(pendingNext.mock.calls).toEqual([[0], [1], [0]]); - }, - }); - }); - - test('should observe the number of pending requests on error', () => { - const pendingCount$ = searchInterceptor.getPendingCount$(); - const pendingNext = jest.fn(); - pendingCount$.subscribe(pendingNext); - - const mockResponse: any = { result: 500 }; - mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); - - response.subscribe({ - complete: () => { - expect(pendingNext.mock.calls).toEqual([[0], [1], [0]]); - }, - }); - }); - }); }); diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 0a6d60afed2f..888e12a4285b 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,7 +17,7 @@ * under the License. */ -import { trimEnd } from 'lodash'; +import { trimEnd, debounce } from 'lodash'; import { BehaviorSubject, throwError, @@ -28,25 +28,24 @@ import { Observable, NEVER, } from 'rxjs'; -import { finalize, filter } from 'rxjs/operators'; -import { Toast, CoreStart, ToastsSetup, CoreSetup } from 'kibana/public'; -import { getCombinedSignal, AbortError } from '../../common/utils'; +import { catchError, finalize } from 'rxjs/operators'; +import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; import { + getCombinedSignal, + AbortError, IEsSearchRequest, IEsSearchResponse, ISearchOptions, ES_SEARCH_STRATEGY, -} from '../../common/search'; -import { getLongQueryNotification } from './long_query_notification'; +} from '../../common'; import { SearchUsageCollector } from './collectors'; -const LONG_QUERY_NOTIFICATION_DELAY = 10000; - export interface SearchInterceptorDeps { - toasts: ToastsSetup; http: CoreSetup['http']; uiSettings: CoreSetup['uiSettings']; startServices: Promise<[CoreStart, any, unknown]>; + toasts: ToastsSetup; usageCollector?: SearchUsageCollector; } @@ -69,12 +68,6 @@ export class SearchInterceptor { */ protected timeoutSubscriptions: Subscription = new Subscription(); - /** - * The current long-running toast (if there is one). - * @internal - */ - protected longRunningToast?: Toast; - /** * @internal */ @@ -89,19 +82,6 @@ export class SearchInterceptor { this.deps.startServices.then(([coreStart]) => { this.application = coreStart.application; }); - - // When search requests go out, a notification is scheduled allowing users to continue the - // request past the timeout. When all search requests complete, we remove the notification. - this.getPendingCount$() - .pipe(filter((count) => count === 0)) - .subscribe(this.hideToast); - } - /** - * Returns an `Observable` over the current number of pending searches. This could mean that one - * of the search requests is still in flight, or that it has only received partial responses. - */ - public getPendingCount$() { - return this.pendingCount$.asObservable(); } /** @@ -146,6 +126,12 @@ export class SearchInterceptor { this.pendingCount$.next(this.pendingCount$.getValue() + 1); return this.runSearch(request, combinedSignal, options?.strategy).pipe( + catchError((e: any) => { + if (e.body?.attributes?.error === 'Request timed out') { + this.showTimeoutError(e); + } + return throwError(e); + }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); cleanup(); @@ -170,12 +156,10 @@ export class SearchInterceptor { const timeout$ = timeout ? timer(timeout) : NEVER; const subscription = timeout$.subscribe(() => { timeoutController.abort(); + this.showTimeoutError(new AbortError()); }); this.timeoutSubscriptions.add(subscription); - // Schedule the notification to allow users to cancel or wait beyond the timeout - const notificationSubscription = timer(LONG_QUERY_NOTIFICATION_DELAY).subscribe(this.showToast); - // Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs: // 1. The user manually aborts (via `cancelPending`) // 2. The request times out @@ -189,7 +173,6 @@ export class SearchInterceptor { const combinedSignal = getCombinedSignal(signals); const cleanup = () => { this.timeoutSubscriptions.remove(subscription); - notificationSubscription.unsubscribe(); }; combinedSignal.addEventListener('abort', cleanup); @@ -200,36 +183,23 @@ export class SearchInterceptor { }; } - /** - * @internal - */ - protected showToast = () => { - if (this.longRunningToast) return; - this.longRunningToast = this.deps.toasts.addInfo( - { - title: 'Your query is taking a while', - text: getLongQueryNotification({ - application: this.application, + // Right now we are debouncing but we will hook this up with background sessions to show only one + // error notification per session. + protected showTimeoutError = debounce( + (e: Error) => { + this.deps.toasts.addError(e, { + title: 'Timed out', + toastMessage: i18n.translate('data.search.upgradeLicense', { + defaultMessage: + 'One or more queries timed out. With our free Basic tier, your queries never time out.', }), - }, - { - toastLifeTimeMs: 1000000, - } - ); - }; - - /** - * @internal - */ - protected hideToast = () => { - if (this.longRunningToast) { - this.deps.toasts.remove(this.longRunningToast); - delete this.longRunningToast; - if (this.deps.usageCollector) { - this.deps.usageCollector.trackLongQueryDialogDismissed(); - } + }); + }, + 60000, + { + leading: true, } - }; + ); } export type ISearchInterceptor = PublicMethodsOf; diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index f8f4acbe43df..6b73761c5a43 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -103,7 +103,13 @@ export class SearchService implements Plugin { aggs: this.aggsService.start({ fieldFormats, uiSettings }), search, searchSource: { + /** + * creates searchsource based on serialized search source fields + */ create: createSearchSource(indexPatterns, searchSourceDependencies), + /** + * creates an enpty search source + */ createEmpty: () => { return new SearchSource({}, searchSourceDependencies); }, diff --git a/src/plugins/data/public/search/search_source/create_search_source.ts b/src/plugins/data/public/search/search_source/create_search_source.ts index 4c44f4d62d46..242fbd73fe42 100644 --- a/src/plugins/data/public/search/search_source/create_search_source.ts +++ b/src/plugins/data/public/search/search_source/create_search_source.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { migrateLegacyQuery } from '../../../../kibana_legacy/common'; +import { migrateLegacyQuery } from './migrate_legacy_query'; import { SearchSource, SearchSourceDependencies } from './search_source'; import { IndexPatternsContract } from '../../index_patterns/index_patterns'; import { SearchSourceFields } from './types'; diff --git a/src/plugins/data/public/search/search_source/migrate_legacy_query.ts b/src/plugins/data/public/search/search_source/migrate_legacy_query.ts new file mode 100644 index 000000000000..8d9b50d5a66b --- /dev/null +++ b/src/plugins/data/public/search/search_source/migrate_legacy_query.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { has } from 'lodash'; +import { Query } from 'src/plugins/data/public'; + +/** + * Creates a standardized query object from old queries that were either strings or pure ES query DSL + * + * @param query - a legacy query, what used to be stored in SearchSource's query property + * @return Object + */ + +export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { + // Lucene was the only option before, so language-less queries are all lucene + if (!has(query, 'language')) { + return { query, language: 'lucene' }; + } + + return query as Query; +} diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index 68c7b663b362..a39898e6a9f5 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -143,15 +143,19 @@ export class SearchSource { * PUBLIC API *****/ + /** + * internal, dont use + * @param searchStrategyId + */ setPreferredSearchStrategyId(searchStrategyId: string) { this.searchStrategyId = searchStrategyId; } - setFields(newFields: SearchSourceFields) { - this.fields = newFields; - return this; - } - + /** + * sets value to a single search source feild + * @param field: field name + * @param value: value for the field + */ setField(field: K, value: SearchSourceFields[K]) { if (value == null) { delete this.fields[field]; @@ -161,16 +165,33 @@ export class SearchSource { return this; } + /** + * Internal, do not use. Overrides all search source fields with the new field array. + * + * @private + * @param newFields New field array. + */ + setFields(newFields: SearchSourceFields) { + this.fields = newFields; + return this; + } + + /** + * returns search source id + */ getId() { return this.id; } + /** + * returns all search source fields + */ getFields() { return { ...this.fields }; } /** - * Get fields from the fields + * Gets a single field from the fields */ getField(field: K, recurse = true): SearchSourceFields[K] { if (!recurse || this.fields[field] !== void 0) { @@ -187,10 +208,16 @@ export class SearchSource { return this.getField(field, false); } + /** + * @deprecated Don't use. + */ create() { return new SearchSource({}, this.dependencies); } + /** + * creates a copy of this search source (without its children) + */ createCopy() { const newSearchSource = new SearchSource({}, this.dependencies); newSearchSource.setFields({ ...this.fields }); @@ -201,6 +228,10 @@ export class SearchSource { return newSearchSource; } + /** + * creates a new child search source + * @param options + */ createChild(options = {}) { const childSearchSource = new SearchSource({}, this.dependencies); childSearchSource.setParent(this, options); @@ -227,42 +258,6 @@ export class SearchSource { return this.parent; } - /** - * Run a search using the search service - * @return {Observable>} - */ - private fetch$(searchRequest: SearchRequest, options: ISearchOptions) { - const { search, getConfig } = this.dependencies; - - const params = getSearchParamsFromRequest(searchRequest, { - getConfig, - }); - - return search({ params, indexType: searchRequest.indexType }, options).pipe( - map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)) - ); - } - - /** - * Run a search using the search service - * @return {Promise>} - */ - private async legacyFetch(searchRequest: SearchRequest, options: ISearchOptions) { - const { http, getConfig, loadingCount$ } = this.dependencies; - - return await fetchSoon( - searchRequest, - { - ...(this.searchStrategyId && { searchStrategyId: this.searchStrategyId }), - ...options, - }, - { - http, - config: { get: getConfig }, - loadingCount$, - } - ); - } /** * Fetch this source and reject the returned Promise on error * @@ -301,6 +296,9 @@ export class SearchSource { this.requestStartHandlers.push(handler); } + /** + * Returns body contents of the search request, often referred as query DSL. + */ async getSearchRequestBody() { const searchRequest = await this.flatten(); return searchRequest.body; @@ -318,6 +316,43 @@ export class SearchSource { * PRIVATE APIS ******/ + /** + * Run a search using the search service + * @return {Observable>} + */ + private fetch$(searchRequest: SearchRequest, options: ISearchOptions) { + const { search, getConfig } = this.dependencies; + + const params = getSearchParamsFromRequest(searchRequest, { + getConfig, + }); + + return search({ params, indexType: searchRequest.indexType }, options).pipe( + map(({ rawResponse }) => handleResponse(searchRequest, rawResponse)) + ); + } + + /** + * Run a search using the search service + * @return {Promise>} + */ + private async legacyFetch(searchRequest: SearchRequest, options: ISearchOptions) { + const { http, getConfig, loadingCount$ } = this.dependencies; + + return await fetchSoon( + searchRequest, + { + ...(this.searchStrategyId && { searchStrategyId: this.searchStrategyId }), + ...options, + }, + { + http, + config: { get: getConfig }, + loadingCount$, + } + ); + } + /** * Called by requests of this search source when they are started * @param options @@ -480,6 +515,9 @@ export class SearchSource { return searchRequest; } + /** + * serializes search source fields (which can later be passed to {@link ISearchStartSearchSource}) + */ public getSerializedFields() { const { filter: originalFilters, ...searchSourceFields } = omit(this.getFields(), [ 'sort', @@ -531,5 +569,8 @@ export class SearchSource { } } -/** @public **/ +/** + * search source interface + * @public + */ export type ISearchSource = Pick; diff --git a/src/plugins/data/public/search/search_source/types.ts b/src/plugins/data/public/search/search_source/types.ts index c2f8701a64fa..0882aa9a2cee 100644 --- a/src/plugins/data/public/search/search_source/types.ts +++ b/src/plugins/data/public/search/search_source/types.ts @@ -34,19 +34,37 @@ export interface SortDirectionNumeric { export type EsQuerySortValue = Record; +/** + * search source fields + */ export interface SearchSourceFields { type?: string; + /** + * {@link Query} + */ query?: Query; + /** + * {@link Filter} + */ filter?: Filter[] | Filter | (() => Filter[] | Filter | undefined); + /** + * {@link EsQuerySortValue} + */ sort?: EsQuerySortValue | EsQuerySortValue[]; highlight?: any; highlightAll?: boolean; + /** + * {@link AggConfigs} + */ aggs?: any; from?: number; size?: number; source?: NameList; version?: boolean; fields?: NameList; + /** + * {@link IndexPatternService} + */ index?: IndexPattern; searchAfter?: EsQuerySearchAfter; timeout?: string; diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index cec5c63294e9..83a542269046 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -62,13 +62,42 @@ export interface ISearchSetup { __enhance: (enhancements: SearchEnhancements) => void; } +/** + * high level search service + * @public + */ +export interface ISearchStartSearchSource { + /** + * creates {@link SearchSource} based on provided serialized {@link SearchSourceFields} + * @param fields + */ + create: (fields?: SearchSourceFields) => Promise; + /** + * creates empty {@link SearchSource} + */ + createEmpty: () => ISearchSource; +} +/** + * search service + * @public + */ export interface ISearchStart { + /** + * agg config sub service + * {@link AggsStart} + * + */ aggs: AggsStart; + /** + * low level search + * {@link ISearchGeneric} + */ search: ISearchGeneric; - searchSource: { - create: (fields?: SearchSourceFields) => Promise; - createEmpty: () => ISearchSource; - }; + /** + * high level search + * {@link ISearchStartSearchSource} + */ + searchSource: ISearchStartSearchSource; } export { SEARCH_EVENT_TYPE } from './collectors'; diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index bffc10642eb4..7b5d79aff24e 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -46,6 +46,9 @@ export interface DataStartDependencies { uiActions: UiActionsStart; } +/** + * Data plugin public Setup contract + */ export interface DataPublicPluginSetup { autocomplete: AutocompleteSetup; search: ISearchSetup; @@ -57,20 +60,61 @@ export interface DataPublicPluginSetup { __enhance: (enhancements: DataPublicPluginEnhancements) => void; } +/** + * Data plugin prewired UI components + */ +export interface DataPublicPluginStartUi { + IndexPatternSelect: React.ComponentType; + SearchBar: React.ComponentType; +} + +/** + * utilities to generate filters from action context + */ +export interface DataPublicPluginStartActions { + createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; + createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; +} + +/** + * Data plugin public Start contract + */ export interface DataPublicPluginStart { - actions: { - createFiltersFromValueClickAction: typeof createFiltersFromValueClickAction; - createFiltersFromRangeSelectAction: typeof createFiltersFromRangeSelectAction; - }; + /** + * filter creation utilities + * {@link DataPublicPluginStartActions} + */ + actions: DataPublicPluginStartActions; + /** + * autocomplete service + * {@link AutocompleteStart} + */ autocomplete: AutocompleteStart; + /** + * index patterns service + * {@link IndexPatternsContract} + */ indexPatterns: IndexPatternsContract; + /** + * search service + * {@link ISearchStart} + */ search: ISearchStart; + /** + * field formats service + * {@link FieldFormatsStart} + */ fieldFormats: FieldFormatsStart; + /** + * query service + * {@link QueryStart} + */ query: QueryStart; - ui: { - IndexPatternSelect: React.ComponentType; - SearchBar: React.ComponentType; - }; + /** + * prewired UI components + * {@link DataPublicPluginStartUi} + */ + ui: DataPublicPluginStartUi; } export interface IDataPluginServices extends Partial { diff --git a/src/plugins/data/public/ui/query_string_input/_query_bar.scss b/src/plugins/data/public/ui/query_string_input/_query_bar.scss index 00895ec49003..1ff24c61954e 100644 --- a/src/plugins/data/public/ui/query_string_input/_query_bar.scss +++ b/src/plugins/data/public/ui/query_string_input/_query_bar.scss @@ -8,30 +8,37 @@ border-right: none !important; } +.kbnQueryBar__textareaWrap { + overflow: visible !important; // Override EUI form control + display: flex; + flex: 1 1 100%; + position: relative; +} + .kbnQueryBar__textarea { z-index: $euiZContentMenu; resize: none !important; // When in the group, it will autosize - height: $euiSizeXXL; + height: $euiFormControlHeight; // Unlike most inputs within layout control groups, the text area still needs a border. // These adjusts help it sit above the control groups shadow to line up correctly. - padding-top: $euiSizeS + 3px !important; - transform: translateY(-2px); - padding: $euiSizeS - 1px; + padding: $euiSizeS; + padding-top: $euiSizeS + 3px; + transform: translateY(-1px) translateX(-1px); - &:not(:focus) { + &:not(:focus):not(:invalid) { @include euiYScrollWithShadows; + } + + &:not(:focus) { white-space: nowrap; overflow-y: hidden; overflow-x: hidden; - border: none; - box-shadow: none; } // When focused, let it scroll &:focus { overflow-x: auto; overflow-y: auto; - width: calc(100% + 1px); // To overtake the group's fake border white-space: normal; } } diff --git a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx index 0bfac2a07a7e..f159cac664a9 100644 --- a/src/plugins/data/public/ui/query_string_input/query_string_input.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_string_input.tsx @@ -19,6 +19,7 @@ import React, { Component, RefObject, createRef } from 'react'; import { i18n } from '@kbn/i18n'; + import classNames from 'classnames'; import { EuiTextArea, @@ -63,6 +64,7 @@ interface Props { dataTestSubj?: string; size?: SuggestionsListSize; className?: string; + isInvalid?: boolean; } interface State { @@ -591,6 +593,7 @@ export class QueryStringInputUI extends Component { 'euiFormControlLayout euiFormControlLayout--group kbnQueryBar__wrap', this.props.className ); + return (
{this.props.prepend} @@ -607,7 +610,7 @@ export class QueryStringInputUI extends Component { >
{ } role="textbox" data-test-subj={this.props.dataTestSubj || 'queryInput'} + isInvalid={this.props.isInvalid} > {this.getQueryString()} diff --git a/src/plugins/data/public/ui/typeahead/constants.ts b/src/plugins/data/public/ui/typeahead/constants.ts index 08f9bd23e16f..0e28891a1453 100644 --- a/src/plugins/data/public/ui/typeahead/constants.ts +++ b/src/plugins/data/public/ui/typeahead/constants.ts @@ -33,4 +33,4 @@ export const SUGGESTIONS_LIST_REQUIRED_BOTTOM_SPACE = 250; * A distance in px to display suggestions list right under the query input without a gap * @public */ -export const SUGGESTIONS_LIST_REQUIRED_TOP_OFFSET = 2; +export const SUGGESTIONS_LIST_REQUIRED_TOP_OFFSET = 1; diff --git a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx index dc7c55374f1d..50ed9e9542d3 100644 --- a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx +++ b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx @@ -154,6 +154,7 @@ export class SuggestionsComponent extends Component { const StyledSuggestionsListDiv = styled.div` ${(props: { queryBarRect: DOMRect; verticalListPosition: string }) => ` position: absolute; + z-index: 4001; left: ${props.queryBarRect.left}px; width: ${props.queryBarRect.width}px; ${props.verticalListPosition}`} diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 106f974ed345..e2ed500689cf 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -52,10 +52,11 @@ export const esSearchStrategyProvider = ( }); try { - const esResponse = (await context.core.elasticsearch.client.asCurrentUser.search( - params - )) as ApiResponse>; - const rawResponse = esResponse.body; + // Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 + const promise = context.core.elasticsearch.client.asCurrentUser.search(params); + if (options?.abortSignal) + options.abortSignal.addEventListener('abort', () => promise.abort()); + const { body: rawResponse } = (await promise) as ApiResponse>; if (usage) usage.trackSuccess(rawResponse.took); diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index b2b958454de4..aefdac2ab639 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -20,7 +20,7 @@ import { RequestHandlerContext } from '../../../../core/server'; import { ISearchOptions } from '../../common/search'; import { AggsSetup, AggsStart } from './aggs'; -import { SearchUsage } from './collectors/usage'; +import { SearchUsage } from './collectors'; import { IEsSearchRequest, IEsSearchResponse } from './es_search'; export interface SearchEnhancements { diff --git a/src/plugins/dev_tools/kibana.json b/src/plugins/dev_tools/kibana.json index d83cabd0f081..f1c6c9ecf87e 100644 --- a/src/plugins/dev_tools/kibana.json +++ b/src/plugins/dev_tools/kibana.json @@ -3,5 +3,5 @@ "version": "kibana", "server": false, "ui": true, - "requiredPlugins": ["kibanaLegacy"] + "requiredPlugins": ["urlForwarding"] } diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts index 45fa3634bc87..fcc6a57361a9 100644 --- a/src/plugins/dev_tools/public/plugin.ts +++ b/src/plugins/dev_tools/public/plugin.ts @@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n'; import { sortBy } from 'lodash'; import { AppNavLinkStatus, DEFAULT_APP_CATEGORIES } from '../../../core/public'; -import { KibanaLegacySetup } from '../../kibana_legacy/public'; +import { UrlForwardingSetup } from '../../url_forwarding/public'; import { CreateDevToolArgs, DevToolApp, createDevToolApp } from './dev_tool'; import './index.scss'; @@ -51,7 +51,7 @@ export class DevToolsPlugin implements Plugin { return sortBy([...this.devTools.values()], 'order'); } - public setup(coreSetup: CoreSetup, { kibanaLegacy }: { kibanaLegacy: KibanaLegacySetup }) { + public setup(coreSetup: CoreSetup, { urlForwarding }: { urlForwarding: UrlForwardingSetup }) { const { application: applicationSetup, getStartServices } = coreSetup; applicationSetup.register({ @@ -75,7 +75,7 @@ export class DevToolsPlugin implements Plugin { }, }); - kibanaLegacy.forwardApp('dev_tools', 'dev_tools'); + urlForwarding.forwardApp('dev_tools', 'dev_tools'); return { register: (devToolArgs: CreateDevToolArgs) => { diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 041f362bf062..1a23f6deb5fa 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -9,6 +9,7 @@ "embeddable", "inspector", "kibanaLegacy", + "urlForwarding", "navigation", "uiActions", "visualizations" diff --git a/src/plugins/discover/public/application/angular/discover_state.ts b/src/plugins/discover/public/application/angular/discover_state.ts index ff8fb9f80a72..ac0dc054485f 100644 --- a/src/plugins/discover/public/application/angular/discover_state.ts +++ b/src/plugins/discover/public/application/angular/discover_state.ts @@ -28,7 +28,7 @@ import { withNotifyOnErrors, } from '../../../../kibana_utils/public'; import { esFilters, Filter, Query } from '../../../../data/public'; -import { migrateLegacyQuery } from '../../../../kibana_legacy/public'; +import { migrateLegacyQuery } from '../helpers/migrate_legacy_query'; export interface AppState { /** diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts index a32af8fe43dc..4db1d2b175d0 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.test.ts @@ -58,6 +58,11 @@ describe('docTable', function () { expect(getSort([['foo', 'bar']], indexPattern)).toEqual([]); expect(getSort([{ foo: 'bar' }], indexPattern)).toEqual([]); }); + + test('should convert a legacy sort to an array of objects', function () { + expect(getSort(['foo', 'desc'], indexPattern)).toEqual([{ foo: 'desc' }]); + expect(getSort(['foo', 'asc'], indexPattern)).toEqual([{ foo: 'asc' }]); + }); }); describe('getSortArray function', function () { diff --git a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.ts b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.ts index c28519692318..73ae691529e2 100644 --- a/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.ts +++ b/src/plugins/discover/public/application/angular/doc_table/lib/get_sort.ts @@ -46,6 +46,12 @@ function createSortObject( } } +export function isLegacySort(sort: SortPair[] | SortPair): sort is SortPair { + return ( + sort.length === 2 && typeof sort[0] === 'string' && (sort[1] === 'desc' || sort[1] === 'asc') + ); +} + /** * Take a sorting array and make it into an object * @param {array} sort two dimensional array [[fieldToSort, directionToSort]] @@ -53,8 +59,12 @@ function createSortObject( * @param {object} indexPattern used for determining default sort * @returns Array<{object}> an array of sort objects */ -export function getSort(sort: SortPair[], indexPattern: IndexPattern): SortPairObj[] { +export function getSort(sort: SortPair[] | SortPair, indexPattern: IndexPattern): SortPairObj[] { if (Array.isArray(sort)) { + if (isLegacySort(sort)) { + // To stay compatible with legacy sort, which just supported a single sort field + return [{ [sort[0]]: sort[1] }]; + } return sort .map((sortPair: SortPair) => createSortObject(sortPair, indexPattern)) .filter((sortPairObj) => typeof sortPairObj === 'object') as SortPairObj[]; diff --git a/src/plugins/discover/public/application/angular/redirect.ts b/src/plugins/discover/public/application/angular/redirect.ts index bfa2f07f852e..d3fb47f329d4 100644 --- a/src/plugins/discover/public/application/angular/redirect.ts +++ b/src/plugins/discover/public/application/angular/redirect.ts @@ -24,10 +24,10 @@ getAngularModule().config(($routeProvider: any) => { const path = window.location.hash.substr(1); getUrlTracker().restorePreviousUrl(); $rootScope.$applyAsync(() => { - const { kibanaLegacy } = getServices(); - const { navigated } = kibanaLegacy.navigateToLegacyKibanaUrl(path); + const { urlForwarding } = getServices(); + const { navigated } = urlForwarding.navigateToLegacyKibanaUrl(path); if (!navigated) { - kibanaLegacy.navigateToDefaultApp(); + urlForwarding.navigateToDefaultApp(); } }); // prevent angular from completing the navigation diff --git a/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts b/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts new file mode 100644 index 000000000000..8d9b50d5a66b --- /dev/null +++ b/src/plugins/discover/public/application/helpers/migrate_legacy_query.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { has } from 'lodash'; +import { Query } from 'src/plugins/data/public'; + +/** + * Creates a standardized query object from old queries that were either strings or pure ES query DSL + * + * @param query - a legacy query, what used to be stored in SearchSource's query property + * @return Object + */ + +export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { + // Lucene was the only option before, so language-less queries are all lucene + if (!has(query, 'language')) { + return { query, language: 'lucene' }; + } + + return query as Query; +} diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index 75c83e30d80a..12562d8571a2 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -43,6 +43,7 @@ import { DiscoverStartPlugins } from './plugin'; import { createSavedSearchesLoader, SavedSearch } from './saved_searches'; import { getHistory } from './kibana_services'; import { KibanaLegacyStart } from '../../kibana_legacy/public'; +import { UrlForwardingStart } from '../../url_forwarding/public'; export interface DiscoverServices { addBasePath: (path: string) => string; @@ -59,6 +60,7 @@ export interface DiscoverServices { metadata: { branch: string }; share?: SharePluginStart; kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; timefilter: TimefilterContract; toastNotifications: ToastsStart; getSavedSearchById: (id: string) => Promise; @@ -100,6 +102,7 @@ export async function buildServices( }, share: plugins.share, kibanaLegacy: plugins.kibanaLegacy, + urlForwarding: plugins.urlForwarding, timefilter: plugins.data.query.timefilter.timefilter, toastNotifications: core.notifications.toasts, uiSettings: core.uiSettings, diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts index 015f4267646c..b6960c8a20ab 100644 --- a/src/plugins/discover/public/plugin.ts +++ b/src/plugins/discover/public/plugin.ts @@ -37,6 +37,7 @@ import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navi import { SharePluginStart, SharePluginSetup, UrlGeneratorContract } from 'src/plugins/share/public'; import { VisualizationsStart, VisualizationsSetup } from 'src/plugins/visualizations/public'; import { KibanaLegacySetup, KibanaLegacyStart } from 'src/plugins/kibana_legacy/public'; +import { UrlForwardingSetup, UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import { HomePublicPluginSetup } from 'src/plugins/home/public'; import { Start as InspectorPublicPluginStart } from 'src/plugins/inspector/public'; import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; @@ -119,6 +120,7 @@ export interface DiscoverSetupPlugins { uiActions: UiActionsSetup; embeddable: EmbeddableSetup; kibanaLegacy: KibanaLegacySetup; + urlForwarding: UrlForwardingSetup; home?: HomePublicPluginSetup; visualizations: VisualizationsSetup; data: DataPublicPluginSetup; @@ -135,6 +137,7 @@ export interface DiscoverStartPlugins { data: DataPublicPluginStart; share?: SharePluginStart; kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; inspector: InspectorPublicPluginStart; visualizations: VisualizationsStart; } @@ -267,13 +270,13 @@ export class DiscoverPlugin }, }); - plugins.kibanaLegacy.forwardApp('doc', 'discover', (path) => { + plugins.urlForwarding.forwardApp('doc', 'discover', (path) => { return `#${path}`; }); - plugins.kibanaLegacy.forwardApp('context', 'discover', (path) => { + plugins.urlForwarding.forwardApp('context', 'discover', (path) => { return `#${path}`; }); - plugins.kibanaLegacy.forwardApp('discover', 'discover', (path) => { + plugins.urlForwarding.forwardApp('discover', 'discover', (path) => { const [, id, tail] = /discover\/([^\?]+)(.*)/.exec(path) || []; if (!id) { return `#${path.replace('/discover', '') || '/'}`; diff --git a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts index 1cdb5af00e74..3460203aac29 100644 --- a/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts +++ b/src/plugins/embeddable/public/lib/actions/apply_filter_action.ts @@ -42,6 +42,7 @@ export function createFilterAction(): ActionByType { return createAction({ type: ACTION_APPLY_FILTER, id: ACTION_APPLY_FILTER, + order: 100, getIconType: () => 'filter', getDisplayName: () => { return i18n.translate('embeddableApi.actions.applyFilterActionTitle', { diff --git a/src/plugins/es_ui_shared/public/request/use_request.test.helpers.tsx b/src/plugins/es_ui_shared/public/request/use_request.test.helpers.tsx index 0d6fd122ad22..7a42ed7fad42 100644 --- a/src/plugins/es_ui_shared/public/request/use_request.test.helpers.tsx +++ b/src/plugins/es_ui_shared/public/request/use_request.test.helpers.tsx @@ -106,7 +106,7 @@ export const createUseRequestHelpers = (): UseRequestHelpers => { }; const TestComponent = ({ requestConfig }: { requestConfig: UseRequestConfig }) => { - const { isInitialRequest, isLoading, error, data, sendRequest } = useRequest( + const { isInitialRequest, isLoading, error, data, resendRequest } = useRequest( httpClient as HttpSetup, requestConfig ); @@ -115,7 +115,7 @@ export const createUseRequestHelpers = (): UseRequestHelpers => { hookResult.isLoading = isLoading; hookResult.error = error; hookResult.data = data; - hookResult.sendRequest = sendRequest; + hookResult.resendRequest = resendRequest; return null; }; diff --git a/src/plugins/es_ui_shared/public/request/use_request.test.ts b/src/plugins/es_ui_shared/public/request/use_request.test.ts index f7902218d931..2a639f93b47b 100644 --- a/src/plugins/es_ui_shared/public/request/use_request.test.ts +++ b/src/plugins/es_ui_shared/public/request/use_request.test.ts @@ -102,7 +102,7 @@ describe('useRequest hook', () => { setupSuccessRequest(); expect(hookResult.isInitialRequest).toBe(true); - hookResult.sendRequest(); + hookResult.resendRequest(); await completeRequest(); expect(hookResult.isInitialRequest).toBe(false); }); @@ -148,7 +148,7 @@ describe('useRequest hook', () => { expect(hookResult.error).toBe(getErrorResponse().error); act(() => { - hookResult.sendRequest(); + hookResult.resendRequest(); }); expect(hookResult.isLoading).toBe(true); expect(hookResult.error).toBe(getErrorResponse().error); @@ -183,7 +183,7 @@ describe('useRequest hook', () => { expect(hookResult.data).toBe(getSuccessResponse().data); act(() => { - hookResult.sendRequest(); + hookResult.resendRequest(); }); expect(hookResult.isLoading).toBe(true); expect(hookResult.data).toBe(getSuccessResponse().data); @@ -215,7 +215,7 @@ describe('useRequest hook', () => { }); describe('callbacks', () => { - describe('sendRequest', () => { + describe('resendRequest', () => { it('sends the request', async () => { const { setupSuccessRequest, completeRequest, hookResult, getSendRequestSpy } = helpers; setupSuccessRequest(); @@ -224,7 +224,7 @@ describe('useRequest hook', () => { expect(getSendRequestSpy().callCount).toBe(1); await act(async () => { - hookResult.sendRequest(); + hookResult.resendRequest(); await completeRequest(); }); expect(getSendRequestSpy().callCount).toBe(2); @@ -239,17 +239,17 @@ describe('useRequest hook', () => { await advanceTime(REQUEST_TIME); expect(getSendRequestSpy().callCount).toBe(1); act(() => { - hookResult.sendRequest(); + hookResult.resendRequest(); }); // The manual request resolves, and we'll send yet another one... await advanceTime(REQUEST_TIME); expect(getSendRequestSpy().callCount).toBe(2); act(() => { - hookResult.sendRequest(); + hookResult.resendRequest(); }); - // At this point, we've moved forward 3s. The poll is set at 2s. If sendRequest didn't + // At this point, we've moved forward 3s. The poll is set at 2s. If resendRequest didn't // reset the poll, the request call count would be 4, not 3. await advanceTime(REQUEST_TIME); expect(getSendRequestSpy().callCount).toBe(3); @@ -291,14 +291,14 @@ describe('useRequest hook', () => { const HALF_REQUEST_TIME = REQUEST_TIME * 0.5; setupSuccessRequest({ pollIntervalMs: REQUEST_TIME }); - // Before the original request resolves, we make a manual sendRequest call. + // Before the original request resolves, we make a manual resendRequest call. await advanceTime(HALF_REQUEST_TIME); expect(getSendRequestSpy().callCount).toBe(0); act(() => { - hookResult.sendRequest(); + hookResult.resendRequest(); }); - // The original quest resolves but it's been marked as outdated by the the manual sendRequest + // The original quest resolves but it's been marked as outdated by the the manual resendRequest // call "interrupts", so data is left undefined. await advanceTime(HALF_REQUEST_TIME); expect(getSendRequestSpy().callCount).toBe(1); diff --git a/src/plugins/es_ui_shared/public/request/use_request.ts b/src/plugins/es_ui_shared/public/request/use_request.ts index 481843bf40e8..e04f84a67b8a 100644 --- a/src/plugins/es_ui_shared/public/request/use_request.ts +++ b/src/plugins/es_ui_shared/public/request/use_request.ts @@ -20,11 +20,7 @@ import { useEffect, useCallback, useState, useRef, useMemo } from 'react'; import { HttpSetup } from '../../../../../src/core/public'; -import { - sendRequest as sendStatelessRequest, - SendRequestConfig, - SendRequestResponse, -} from './send_request'; +import { sendRequest, SendRequestConfig } from './send_request'; export interface UseRequestConfig extends SendRequestConfig { pollIntervalMs?: number; @@ -37,7 +33,7 @@ export interface UseRequestResponse { isLoading: boolean; error: E | null; data?: D | null; - sendRequest: () => Promise>; + resendRequest: () => void; } export const useRequest = ( @@ -80,7 +76,7 @@ export const useRequest = ( /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [path, method, queryStringified, bodyStringified]); - const sendRequest = useCallback(async () => { + const resendRequest = useCallback(async () => { // If we're on an interval, this allows us to reset it if the user has manually requested the // data, to avoid doubled-up requests. clearPollInterval(); @@ -91,7 +87,7 @@ export const useRequest = ( // "old" error/data or loading state when a new request is in-flight. setIsLoading(true); - const response = await sendStatelessRequest(httpClient, requestBody); + const response = await sendRequest(httpClient, requestBody); const { data: serializedResponseData, error: responseError } = response; const isOutdatedRequest = requestId !== requestCountRef.current; @@ -99,7 +95,7 @@ export const useRequest = ( // Ignore outdated or irrelevant data. if (isOutdatedRequest || isUnmounted) { - return { data: null, error: null }; + return; } setError(responseError); @@ -112,8 +108,6 @@ export const useRequest = ( } // Setting isLoading to false also acts as a signal for scheduling the next poll request. setIsLoading(false); - - return { data: serializedResponseData, error: responseError }; }, [requestBody, httpClient, deserializer, clearPollInterval]); const scheduleRequest = useCallback(() => { @@ -121,19 +115,19 @@ export const useRequest = ( clearPollInterval(); if (pollIntervalMs) { - pollIntervalIdRef.current = setTimeout(sendRequest, pollIntervalMs); + pollIntervalIdRef.current = setTimeout(resendRequest, pollIntervalMs); } - }, [pollIntervalMs, sendRequest, clearPollInterval]); + }, [pollIntervalMs, resendRequest, clearPollInterval]); - // Send the request on component mount and whenever the dependencies of sendRequest() change. + // Send the request on component mount and whenever the dependencies of resendRequest() change. useEffect(() => { - sendRequest(); - }, [sendRequest]); + resendRequest(); + }, [resendRequest]); // Schedule the next poll request when the previous one completes. useEffect(() => { // When a request completes, attempt to schedule the next one. Note that we aren't re-scheduling - // a request whenever sendRequest's dependencies change. isLoading isn't set to false until the + // a request whenever resendRequest's dependencies change. isLoading isn't set to false until the // initial request has completed, so we won't schedule a request on mount. if (!isLoading) { scheduleRequest(); @@ -156,6 +150,6 @@ export const useRequest = ( isLoading, error, data, - sendRequest, // Gives the user the ability to manually request data + resendRequest, // Gives the user the ability to manually request data }; }; diff --git a/src/plugins/expressions/common/expression_types/specs/boolean.ts b/src/plugins/expressions/common/expression_types/specs/boolean.ts index adbdeafc34fd..73b0b98eaaf0 100644 --- a/src/plugins/expressions/common/expression_types/specs/boolean.ts +++ b/src/plugins/expressions/common/expression_types/specs/boolean.ts @@ -41,7 +41,6 @@ export const boolean: ExpressionTypeDefinition<'boolean', boolean> = { }, datatable: (value): Datatable => ({ type: 'datatable', - meta: {}, columns: [{ id: 'value', name: 'value', meta: { type: name } }], rows: [{ value }], }), diff --git a/src/plugins/expressions/common/expression_types/specs/datatable.ts b/src/plugins/expressions/common/expression_types/specs/datatable.ts index dd3c653878de..c201e99faeb0 100644 --- a/src/plugins/expressions/common/expression_types/specs/datatable.ts +++ b/src/plugins/expressions/common/expression_types/specs/datatable.ts @@ -52,7 +52,10 @@ export type DatatableRow = Record; export interface DatatableColumnMeta { type: DatatableColumnType; field?: string; + index?: string; params?: SerializableState; + source?: string; + sourceParams?: SerializableState; } /** * This type represents the shape of a column in a `Datatable`. @@ -63,17 +66,11 @@ export interface DatatableColumn { meta: DatatableColumnMeta; } -export interface DatatableMeta { - type?: string; - source?: string; -} - /** * A `Datatable` in Canvas is a unique structure that represents tabulated data. */ export interface Datatable { type: typeof name; - meta?: DatatableMeta; columns: DatatableColumn[]; rows: DatatableRow[]; } diff --git a/src/plugins/expressions/common/expression_types/specs/num.ts b/src/plugins/expressions/common/expression_types/specs/num.ts index 041747f39740..d208a9dcf73c 100644 --- a/src/plugins/expressions/common/expression_types/specs/num.ts +++ b/src/plugins/expressions/common/expression_types/specs/num.ts @@ -73,7 +73,6 @@ export const num: ExpressionTypeDefinition<'num', ExpressionValueNum> = { }, datatable: ({ value }): Datatable => ({ type: 'datatable', - meta: {}, columns: [{ id: 'value', name: 'value', meta: { type: 'number' } }], rows: [{ value }], }), diff --git a/src/plugins/expressions/common/expression_types/specs/number.ts b/src/plugins/expressions/common/expression_types/specs/number.ts index c5fdacf3408a..c30d3fe943d4 100644 --- a/src/plugins/expressions/common/expression_types/specs/number.ts +++ b/src/plugins/expressions/common/expression_types/specs/number.ts @@ -55,7 +55,6 @@ export const number: ExpressionTypeDefinition = { }, datatable: (value): Datatable => ({ type: 'datatable', - meta: {}, columns: [{ id: 'value', name: 'value', meta: { type: 'number' } }], rows: [{ value }], }), diff --git a/src/plugins/expressions/common/expression_types/specs/string.ts b/src/plugins/expressions/common/expression_types/specs/string.ts index 3d52707279bf..0869e21e455f 100644 --- a/src/plugins/expressions/common/expression_types/specs/string.ts +++ b/src/plugins/expressions/common/expression_types/specs/string.ts @@ -40,7 +40,6 @@ export const string: ExpressionTypeDefinition = { }, datatable: (value): Datatable => ({ type: 'datatable', - meta: {}, columns: [{ id: 'value', name: 'value', meta: { type: 'string' } }], rows: [{ value }], }), diff --git a/src/plugins/home/kibana.json b/src/plugins/home/kibana.json index 74bd3625ca96..81bfc57a0036 100644 --- a/src/plugins/home/kibana.json +++ b/src/plugins/home/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "kibanaLegacy"], + "requiredPlugins": ["data", "urlForwarding"], "optionalPlugins": ["usageCollection", "telemetry"], "requiredBundles": [ "kibanaReact" diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index 90e549c87343..69cd68d553d0 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -32,8 +32,8 @@ import { useMount } from 'react-use'; const RedirectToDefaultApp = () => { useMount(() => { - const { kibanaLegacy } = getServices(); - kibanaLegacy.navigateToDefaultApp(); + const { urlForwarding } = getServices(); + urlForwarding.navigateToDefaultApp(); }); return null; }; diff --git a/src/plugins/home/public/application/kibana_services.ts b/src/plugins/home/public/application/kibana_services.ts index 8bd651d03812..74b2bf8d4f6a 100644 --- a/src/plugins/home/public/application/kibana_services.ts +++ b/src/plugins/home/public/application/kibana_services.ts @@ -29,7 +29,7 @@ import { } from 'kibana/public'; import { UiStatsMetricType } from '@kbn/analytics'; import { TelemetryPluginStart } from '../../../telemetry/public'; -import { KibanaLegacyStart } from '../../../kibana_legacy/public'; +import { UrlForwardingStart } from '../../../url_forwarding/public'; import { TutorialService } from '../services/tutorials'; import { FeatureCatalogueRegistry } from '../services/feature_catalogue'; import { EnvironmentService } from '../services/environment'; @@ -41,7 +41,7 @@ export interface HomeKibanaServices { chrome: ChromeStart; application: ApplicationStart; uiSettings: IUiSettingsClient; - kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; homeConfig: ConfigSchema; featureCatalogue: FeatureCatalogueRegistry; http: HttpStart; diff --git a/src/plugins/home/public/plugin.test.ts b/src/plugins/home/public/plugin.test.ts index 0ebba06e6bea..7b56c6ec89b7 100644 --- a/src/plugins/home/public/plugin.test.ts +++ b/src/plugins/home/public/plugin.test.ts @@ -20,7 +20,7 @@ import { registryMock, environmentMock, tutorialMock } from './plugin.test.mocks'; import { HomePublicPlugin } from './plugin'; import { coreMock } from '../../../core/public/mocks'; -import { kibanaLegacyPluginMock } from '../../kibana_legacy/public/mocks'; +import { urlForwardingPluginMock } from '../../url_forwarding/public/mocks'; const mockInitializerContext = coreMock.createPluginInitializerContext(); @@ -37,7 +37,7 @@ describe('HomePublicPlugin', () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, { - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), } ); expect(setup).toHaveProperty('featureCatalogue'); @@ -56,7 +56,7 @@ describe('HomePublicPlugin', () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, { - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), } ); expect(setup).toHaveProperty('featureCatalogue'); @@ -73,7 +73,7 @@ describe('HomePublicPlugin', () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, { - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), } ); expect(setup).toHaveProperty('featureCatalogue'); @@ -84,7 +84,7 @@ describe('HomePublicPlugin', () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, { - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), } ); expect(setup).toHaveProperty('environment'); @@ -95,7 +95,7 @@ describe('HomePublicPlugin', () => { const setup = await new HomePublicPlugin(mockInitializerContext).setup( coreMock.createSetup() as any, { - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), } ); expect(setup).toHaveProperty('tutorials'); diff --git a/src/plugins/home/public/plugin.ts b/src/plugins/home/public/plugin.ts index ba2f537e7c5d..b62ceae3d0d3 100644 --- a/src/plugins/home/public/plugin.ts +++ b/src/plugins/home/public/plugin.ts @@ -41,19 +41,19 @@ import { setServices } from './application/kibana_services'; import { DataPublicPluginStart } from '../../data/public'; import { TelemetryPluginStart } from '../../telemetry/public'; import { UsageCollectionSetup } from '../../usage_collection/public'; -import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public'; +import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; import { AppNavLinkStatus } from '../../../core/public'; import { PLUGIN_ID, HOME_APP_BASE_PATH } from '../common/constants'; export interface HomePluginStartDependencies { data: DataPublicPluginStart; telemetry?: TelemetryPluginStart; - kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; } export interface HomePluginSetupDependencies { usageCollection?: UsageCollectionSetup; - kibanaLegacy: KibanaLegacySetup; + urlForwarding: UrlForwardingSetup; } export class HomePublicPlugin @@ -67,7 +67,7 @@ export class HomePublicPlugin public setup( core: CoreSetup, - { kibanaLegacy, usageCollection }: HomePluginSetupDependencies + { urlForwarding, usageCollection }: HomePluginSetupDependencies ): HomePublicPluginSetup { core.application.register({ id: PLUGIN_ID, @@ -79,7 +79,7 @@ export class HomePublicPlugin : () => {}; const [ coreStart, - { telemetry, data, kibanaLegacy: kibanaLegacyStart }, + { telemetry, data, urlForwarding: urlForwardingStart }, ] = await core.getStartServices(); setServices({ trackUiMetric, @@ -97,7 +97,7 @@ export class HomePublicPlugin getBasePath: core.http.basePath.get, indexPatternService: data.indexPatterns, environmentService: this.environmentService, - kibanaLegacy: kibanaLegacyStart, + urlForwarding: urlForwardingStart, homeConfig: this.initializerContext.config.get(), tutorialService: this.tutorialService, featureCatalogue: this.featuresCatalogueRegistry, @@ -109,7 +109,7 @@ export class HomePublicPlugin return await renderApp(params.element, coreStart, params.history); }, }); - kibanaLegacy.forwardApp('home', 'home'); + urlForwarding.forwardApp('home', 'home'); const featureCatalogue = { ...this.featuresCatalogueRegistry.setup() }; @@ -170,7 +170,7 @@ export class HomePublicPlugin public start( { application: { capabilities, currentAppId$ }, http }: CoreStart, - { kibanaLegacy }: HomePluginStartDependencies + { urlForwarding }: HomePluginStartDependencies ) { this.featuresCatalogueRegistry.start({ capabilities }); @@ -184,7 +184,7 @@ export class HomePublicPlugin if (appId === 'home') { // ...navigate to default app set by `kibana.defaultAppId`. // This doesn't do anything as along as the default settings are kept. - kibanaLegacy.navigateToDefaultApp({ overwriteHash: false }); + urlForwarding.navigateToDefaultApp({ overwriteHash: false }); } }); } diff --git a/src/plugins/index_pattern_management/kibana.json b/src/plugins/index_pattern_management/kibana.json index d0ad6a96065c..6c3025485bbd 100644 --- a/src/plugins/index_pattern_management/kibana.json +++ b/src/plugins/index_pattern_management/kibana.json @@ -3,6 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["management", "data", "kibanaLegacy"], + "requiredPlugins": ["management", "data", "urlForwarding"], "requiredBundles": ["kibanaReact", "kibanaUtils"] } diff --git a/src/plugins/index_pattern_management/public/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts index 6a9ef23e3732..24aea961764a 100644 --- a/src/plugins/index_pattern_management/public/mocks.ts +++ b/src/plugins/index_pattern_management/public/mocks.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext } from 'src/core/public'; import { coreMock } from '../../../core/public/mocks'; import { managementPluginMock } from '../../management/public/mocks'; -import { kibanaLegacyPluginMock } from '../../kibana_legacy/public/mocks'; +import { urlForwardingPluginMock } from '../../url_forwarding/public/mocks'; import { dataPluginMock } from '../../data/public/mocks'; import { IndexPatternManagementSetup, @@ -65,7 +65,7 @@ const createInstance = async () => { const setup = plugin.setup(coreMock.createSetup(), { management: managementPluginMock.createSetupContract(), - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), + urlForwarding: urlForwardingPluginMock.createSetupContract(), }); const doStart = () => plugin.start(coreMock.createStart(), { diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts index ee1e00fcafd9..cfe0a23eb14d 100644 --- a/src/plugins/index_pattern_management/public/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { KibanaLegacySetup } from '../../kibana_legacy/public'; +import { UrlForwardingSetup } from '../../url_forwarding/public'; import { IndexPatternManagementService, IndexPatternManagementServiceSetup, @@ -31,7 +31,7 @@ import { ManagementSetup } from '../../management/public'; export interface IndexPatternManagementSetupDependencies { management: ManagementSetup; - kibanaLegacy: KibanaLegacySetup; + urlForwarding: UrlForwardingSetup; } export interface IndexPatternManagementStartDependencies { @@ -62,7 +62,7 @@ export class IndexPatternManagementPlugin public setup( core: CoreSetup, - { management, kibanaLegacy }: IndexPatternManagementSetupDependencies + { management, urlForwarding }: IndexPatternManagementSetupDependencies ) { const kibanaSection = management.sections.section.kibana; @@ -73,8 +73,8 @@ export class IndexPatternManagementPlugin const newAppPath = `management/kibana/${IPM_APP_ID}`; const legacyPatternsPath = 'management/kibana/index_patterns'; - kibanaLegacy.forwardApp('management/kibana/index_pattern', newAppPath, (path) => '/create'); - kibanaLegacy.forwardApp(legacyPatternsPath, newAppPath, (path) => { + urlForwarding.forwardApp('management/kibana/index_pattern', newAppPath, (path) => '/create'); + urlForwarding.forwardApp(legacyPatternsPath, newAppPath, (path) => { const pathInApp = path.substr(legacyPatternsPath.length + 1); return pathInApp && `/patterns${pathInApp}`; }); diff --git a/src/plugins/input_control_vis/public/vis_controller.tsx b/src/plugins/input_control_vis/public/vis_controller.tsx index e4310960851c..faea98b79229 100644 --- a/src/plugins/input_control_vis/public/vis_controller.tsx +++ b/src/plugins/input_control_vis/public/vis_controller.tsx @@ -18,8 +18,10 @@ */ import React from 'react'; +import { isEqual } from 'lodash'; import { render, unmountComponentAtNode } from 'react-dom'; +import { Subscription } from 'rxjs'; import { I18nStart } from 'kibana/public'; import { InputControlVis } from './components/vis/input_control_vis'; import { getControlFactory } from './control/control_factory'; @@ -34,11 +36,13 @@ import { VisParams, Vis } from '../../visualizations/public'; export const createInputControlVisController = (deps: InputControlVisDependencies) => { return class InputControlVisController { private I18nContext?: I18nStart['Context']; + private isLoaded = false; controls: Array; queryBarUpdateHandler: () => void; filterManager: FilterManager; updateSubsciption: any; + timeFilterSubscription: Subscription; visParams?: VisParams; constructor(public el: Element, public vis: Vis) { @@ -50,19 +54,32 @@ export const createInputControlVisController = (deps: InputControlVisDependencie this.updateSubsciption = this.filterManager .getUpdates$() .subscribe(this.queryBarUpdateHandler); + this.timeFilterSubscription = deps.data.query.timefilter.timefilter + .getTimeUpdate$() + .subscribe(() => { + if (this.visParams?.useTimeFilter) { + this.isLoaded = false; + } + }); } async render(visData: any, visParams: VisParams) { - this.visParams = visParams; - this.controls = []; - this.controls = await this.initControls(); - const [{ i18n }] = await deps.core.getStartServices(); - this.I18nContext = i18n.Context; + if (!this.I18nContext) { + const [{ i18n }] = await deps.core.getStartServices(); + this.I18nContext = i18n.Context; + } + if (!this.isLoaded || !isEqual(visParams, this.visParams)) { + this.visParams = visParams; + this.controls = []; + this.controls = await this.initControls(); + this.isLoaded = true; + } this.drawVis(); } destroy() { this.updateSubsciption.unsubscribe(); + this.timeFilterSubscription.unsubscribe(); unmountComponentAtNode(this.el); this.controls.forEach((control) => control.destroy()); } diff --git a/src/plugins/kibana_legacy/README.md b/src/plugins/kibana_legacy/README.md index 82bf3270589d..d66938cca6d1 100644 --- a/src/plugins/kibana_legacy/README.md +++ b/src/plugins/kibana_legacy/README.md @@ -1,6 +1,7 @@ # kibana-legacy -This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. +This plugin contains several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform. -Currently, the only service offered is the ability to register apps which are rendered in the legacy "kibana" plugin. +This plugin will be removed once all parts of legacy Kibana are removed from other plugins. +All of this plugin should be considered deprecated. New code should never integrate with the services provided from this plugin. \ No newline at end of file diff --git a/src/plugins/kibana_legacy/kibana.json b/src/plugins/kibana_legacy/kibana.json index 79264d95dcc2..e96b4859a36d 100644 --- a/src/plugins/kibana_legacy/kibana.json +++ b/src/plugins/kibana_legacy/kibana.json @@ -2,6 +2,5 @@ "id": "kibanaLegacy", "version": "kibana", "server": true, - "ui": true, - "extraPublicDirs": ["common", "common/kbn_base_url"] + "ui": true } diff --git a/src/plugins/kibana_legacy/public/index.ts b/src/plugins/kibana_legacy/public/index.ts index 27b940b0a456..030dfd585fef 100644 --- a/src/plugins/kibana_legacy/public/index.ts +++ b/src/plugins/kibana_legacy/public/index.ts @@ -24,7 +24,6 @@ export const plugin = (initializerContext: PluginInitializerContext) => new KibanaLegacyPlugin(initializerContext); export * from './plugin'; -export { kbnBaseUrl, migrateLegacyQuery } from '../common'; export { initAngularBootstrap } from './angular_bootstrap'; export { PaginateDirectiveProvider, PaginateControlsDirectiveProvider } from './paginate/paginate'; diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index a3cdb2106523..f3aa015b6000 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -22,12 +22,9 @@ import { KibanaLegacyPlugin } from './plugin'; export type Setup = jest.Mocked>; export type Start = jest.Mocked>; -const createSetupContract = (): Setup => ({ - forwardApp: jest.fn(), -}); +const createSetupContract = (): Setup => ({}); const createStartContract = (): Start => ({ - getForwards: jest.fn(), config: { defaultAppId: 'home', }, @@ -35,8 +32,6 @@ const createStartContract = (): Start => ({ turnHideWriteControlsOn: jest.fn(), getHideWriteControls: jest.fn(), }, - navigateToDefaultApp: jest.fn(), - navigateToLegacyKibanaUrl: jest.fn(), loadFontAwesome: jest.fn(), }); diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index 59ce88c07f4f..8e62411fc34e 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -18,78 +18,18 @@ */ import { PluginInitializerContext, CoreStart, CoreSetup } from 'kibana/public'; -import { Subscription } from 'rxjs'; import { ConfigSchema } from '../config'; import { getDashboardConfig } from './dashboard_config'; -import { navigateToDefaultApp } from './navigate_to_default_app'; -import { createLegacyUrlForwardApp } from './forward_app'; import { injectHeaderStyle } from './utils/inject_header_style'; -import { navigateToLegacyKibanaUrl } from './forward_app/navigate_to_legacy_kibana_url'; - -export interface ForwardDefinition { - legacyAppId: string; - newAppId: string; - rewritePath: (legacyPath: string) => string; -} export class KibanaLegacyPlugin { - private forwardDefinitions: ForwardDefinition[] = []; - private currentAppId: string | undefined; - private currentAppIdSubscription: Subscription | undefined; - constructor(private readonly initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup<{}, KibanaLegacyStart>) { - core.application.register(createLegacyUrlForwardApp(core, this.forwardDefinitions)); - return { - /** - * Forwards URLs within the legacy `kibana` app to a new platform application. - * - * @param legacyAppId The name of the old app to forward URLs from - * @param newAppId The name of the new app that handles the URLs now - * @param rewritePath Function to rewrite the legacy sub path of the app to the new path in the core app. - * If none is provided, it will just strip the prefix of the legacyAppId away - * - * path into the new path - * - * Example usage: - * ``` - * kibanaLegacy.forwardApp( - * 'old', - * 'new', - * path => { - * const [, id] = /old/item\/(.*)$/.exec(path) || []; - * if (!id) { - * return '#/home'; - * } - * return '#/items/${id}'; - * } - * ); - * ``` - * This will cause the following redirects: - * - * * app/kibana#/old/ -> app/new#/home - * * app/kibana#/old/item/123 -> app/new#/items/123 - * - */ - forwardApp: ( - legacyAppId: string, - newAppId: string, - rewritePath?: (legacyPath: string) => string - ) => { - this.forwardDefinitions.push({ - legacyAppId, - newAppId, - rewritePath: rewritePath || ((path) => `#${path.replace(`/${legacyAppId}`, '') || '/'}`), - }); - }, - }; + return {}; } public start({ application, http: { basePath }, uiSettings }: CoreStart) { - this.currentAppIdSubscription = application.currentAppId$.subscribe((currentAppId) => { - this.currentAppId = currentAppId; - }); injectHeaderStyle(uiSettings); return { /** @@ -97,31 +37,6 @@ export class KibanaLegacyPlugin { * @deprecated */ dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls), - /** - * Navigates to the app defined as kibana.defaultAppId. - * This takes redirects into account and uses the right mechanism to navigate. - */ - navigateToDefaultApp: ( - { overwriteHash }: { overwriteHash: boolean } = { overwriteHash: true } - ) => { - navigateToDefaultApp( - this.initializerContext.config.get().defaultAppId, - this.forwardDefinitions, - application, - basePath, - this.currentAppId, - overwriteHash - ); - }, - /** - * Resolves the provided hash using the registered forwards and navigates to the target app. - * If a navigation happened, `{ navigated: true }` will be returned. - * If no matching forward is found, `{ navigated: false }` will be returned. - * @param hash - */ - navigateToLegacyKibanaUrl: (hash: string) => { - return navigateToLegacyKibanaUrl(hash, this.forwardDefinitions, basePath, application); - }, /** * Loads the font-awesome icon font. Should be removed once the last consumer has migrated to EUI * @deprecated @@ -129,11 +44,6 @@ export class KibanaLegacyPlugin { loadFontAwesome: async () => { await import('./font_awesome'); }, - /** - * @deprecated - * Just exported for wiring up with legacy platform, should not be used. - */ - getForwards: () => this.forwardDefinitions, /** * @deprecated * Just exported for wiring up with dashboard mode, should not be used. @@ -141,12 +51,6 @@ export class KibanaLegacyPlugin { config: this.initializerContext.config.get(), }; } - - public stop() { - if (this.currentAppIdSubscription) { - this.currentAppIdSubscription.unsubscribe(); - } - } } export type KibanaLegacySetup = ReturnType; diff --git a/src/plugins/kibana_legacy/public/utils/index.ts b/src/plugins/kibana_legacy/public/utils/index.ts index a32cd5e40a04..590a75ffeed9 100644 --- a/src/plugins/kibana_legacy/public/utils/index.ts +++ b/src/plugins/kibana_legacy/public/utils/index.ts @@ -18,7 +18,6 @@ */ export * from './system_api'; -export * from './normalize_path'; // @ts-ignore export { KbnAccessibleClickProvider } from './kbn_accessible_click'; // @ts-ignore diff --git a/src/plugins/kibana_legacy/server/index.ts b/src/plugins/kibana_legacy/server/index.ts index 3ddcac1517f7..c447f44c16a8 100644 --- a/src/plugins/kibana_legacy/server/index.ts +++ b/src/plugins/kibana_legacy/server/index.ts @@ -50,8 +50,6 @@ export const config: PluginConfigDescriptor = { ], }; -export { kbnBaseUrl, migrateLegacyQuery } from '../common'; - class Plugin { public setup(core: CoreSetup) {} diff --git a/src/plugins/management/kibana.json b/src/plugins/management/kibana.json index 1a9e6be46bd5..6c8574f02422 100644 --- a/src/plugins/management/kibana.json +++ b/src/plugins/management/kibana.json @@ -3,7 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["kibanaLegacy"], "optionalPlugins": ["home"], "requiredBundles": ["kibanaReact", "kibanaUtils", "home"] } diff --git a/src/plugins/management/public/plugin.ts b/src/plugins/management/public/plugin.ts index cd65b7adfead..794bbc0d0613 100644 --- a/src/plugins/management/public/plugin.ts +++ b/src/plugins/management/public/plugin.ts @@ -18,6 +18,7 @@ */ import { i18n } from '@kbn/i18n'; +import { BehaviorSubject } from 'rxjs'; import { ManagementSetup, ManagementStart } from './types'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; import { @@ -27,6 +28,9 @@ import { DEFAULT_APP_CATEGORIES, PluginInitializerContext, AppMountParameters, + AppUpdater, + AppStatus, + AppNavLinkStatus, } from '../../../core/public'; import { @@ -41,6 +45,8 @@ interface ManagementSetupDependencies { export class ManagementPlugin implements Plugin { private readonly managementSections = new ManagementSectionsService(); + private readonly appUpdater = new BehaviorSubject(() => ({})); + constructor(private initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup, { home }: ManagementSetupDependencies) { @@ -70,6 +76,7 @@ export class ManagementPlugin implements Plugin section.getAppsEnabled().length > 0); + + if (!hasAnyEnabledApps) { + this.appUpdater.next(() => { + return { + status: AppStatus.inaccessible, + navLinkStatus: AppNavLinkStatus.hidden, + }; + }); + } + return {}; } } diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts new file mode 100644 index 000000000000..a513bb3c95f2 --- /dev/null +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.test.ts @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { buildContextMenuForActions } from './build_eui_context_menu_panels'; +import { Action, createAction } from '../actions'; + +const createTestAction = ({ + type, + dispayName, + order, +}: { + type: string; + dispayName: string; + order: number; +}) => + createAction({ + type: type as any, // mapping doesn't matter for this test + getDisplayName: () => dispayName, + order, + execute: async () => {}, + }); + +test('contextMenu actions sorting: order, type, displayName', async () => { + const actions: Action[] = [ + createTestAction({ + order: 100, + type: '1', + dispayName: 'a', + }), + createTestAction({ + order: 100, + type: '1', + dispayName: 'b', + }), + createTestAction({ + order: 0, + type: '2', + dispayName: 'c', + }), + createTestAction({ + order: 0, + type: '2', + dispayName: 'd', + }), + createTestAction({ + order: 0, + type: '3', + dispayName: 'aa', + }), + ].sort(() => 0.5 - Math.random()); + + const result = await buildContextMenuForActions({ + actions: actions.map((action) => ({ action, context: {}, trigger: '' as any })), + }); + + expect(result.items?.map((item) => item.name as string)).toMatchInlineSnapshot(` + Array [ + "a", + "b", + "c", + "d", + "aa", + ] + `); +}); diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index b44a07273f4a..3be1ec781cef 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; import _ from 'lodash'; +import sortBy from 'lodash/sortBy'; import { i18n } from '@kbn/i18n'; import { uiToReactComponent } from '../../../kibana_react/public'; import { Action } from '../actions'; @@ -46,11 +47,11 @@ interface ActionWithContext { export async function buildContextMenuForActions({ actions, title = defaultTitle, - closeMenu, + closeMenu = () => {}, }: { actions: ActionWithContext[]; title?: string; - closeMenu: () => void; + closeMenu?: () => void; }): Promise { const menuItems = await buildEuiContextMenuPanelItems({ actions, @@ -74,6 +75,13 @@ async function buildEuiContextMenuPanelItems({ actions: ActionWithContext[]; closeMenu: () => void; }) { + actions = sortBy( + actions, + (a) => -1 * (a.action.order ?? 0), + (a) => a.action.type, + (a) => a.action.getDisplayName({ ...a.context, trigger: a.trigger }) + ); + const items: EuiContextMenuPanelItemDescriptor[] = new Array(actions.length); const promises = actions.map(async ({ action, context, trigger }, index) => { const isCompatible = await action.isCompatible({ diff --git a/src/plugins/url_forwarding/README.md b/src/plugins/url_forwarding/README.md new file mode 100644 index 000000000000..5c5501cc019f --- /dev/null +++ b/src/plugins/url_forwarding/README.md @@ -0,0 +1,3 @@ +# url-forwarding + +This plugins contains helpers to redirect legacy URLs. It can be used to forward old URLs to their new counterparts. diff --git a/src/plugins/url_forwarding/kibana.json b/src/plugins/url_forwarding/kibana.json new file mode 100644 index 000000000000..4f534c1219b3 --- /dev/null +++ b/src/plugins/url_forwarding/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "urlForwarding", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["kibanaLegacy"] +} diff --git a/src/plugins/kibana_legacy/public/forward_app/forward_app.ts b/src/plugins/url_forwarding/public/forward_app/forward_app.ts similarity index 94% rename from src/plugins/kibana_legacy/public/forward_app/forward_app.ts rename to src/plugins/url_forwarding/public/forward_app/forward_app.ts index b425091dfbcd..967b18769ebc 100644 --- a/src/plugins/kibana_legacy/public/forward_app/forward_app.ts +++ b/src/plugins/url_forwarding/public/forward_app/forward_app.ts @@ -20,10 +20,10 @@ import { App, AppMountParameters, CoreSetup } from 'kibana/public'; import { AppNavLinkStatus } from '../../../../core/public'; import { navigateToLegacyKibanaUrl } from './navigate_to_legacy_kibana_url'; -import { ForwardDefinition, KibanaLegacyStart } from '../plugin'; +import { ForwardDefinition, UrlForwardingStart } from '../plugin'; export const createLegacyUrlForwardApp = ( - core: CoreSetup<{}, KibanaLegacyStart>, + core: CoreSetup<{}, UrlForwardingStart>, forwards: ForwardDefinition[] ): App => ({ id: 'kibana', diff --git a/src/plugins/kibana_legacy/public/forward_app/index.ts b/src/plugins/url_forwarding/public/forward_app/index.ts similarity index 100% rename from src/plugins/kibana_legacy/public/forward_app/index.ts rename to src/plugins/url_forwarding/public/forward_app/index.ts diff --git a/src/plugins/kibana_legacy/public/forward_app/navigate_to_legacy_kibana_url.test.ts b/src/plugins/url_forwarding/public/forward_app/navigate_to_legacy_kibana_url.test.ts similarity index 100% rename from src/plugins/kibana_legacy/public/forward_app/navigate_to_legacy_kibana_url.test.ts rename to src/plugins/url_forwarding/public/forward_app/navigate_to_legacy_kibana_url.test.ts diff --git a/src/plugins/kibana_legacy/public/forward_app/navigate_to_legacy_kibana_url.ts b/src/plugins/url_forwarding/public/forward_app/navigate_to_legacy_kibana_url.ts similarity index 96% rename from src/plugins/kibana_legacy/public/forward_app/navigate_to_legacy_kibana_url.ts rename to src/plugins/url_forwarding/public/forward_app/navigate_to_legacy_kibana_url.ts index 1df991f66747..1677b01e7aa4 100644 --- a/src/plugins/kibana_legacy/public/forward_app/navigate_to_legacy_kibana_url.ts +++ b/src/plugins/url_forwarding/public/forward_app/navigate_to_legacy_kibana_url.ts @@ -19,7 +19,7 @@ import { ApplicationStart, IBasePath } from 'kibana/public'; import { ForwardDefinition } from '../index'; -import { normalizePath } from '../utils/normalize_path'; +import { normalizePath } from './normalize_path'; export const navigateToLegacyKibanaUrl = ( path: string, diff --git a/src/plugins/kibana_legacy/public/utils/normalize_path.ts b/src/plugins/url_forwarding/public/forward_app/normalize_path.ts similarity index 100% rename from src/plugins/kibana_legacy/public/utils/normalize_path.ts rename to src/plugins/url_forwarding/public/forward_app/normalize_path.ts diff --git a/src/plugins/kibana_legacy/common/index.ts b/src/plugins/url_forwarding/public/index.ts similarity index 85% rename from src/plugins/kibana_legacy/common/index.ts rename to src/plugins/url_forwarding/public/index.ts index 9c16d7b27386..5fc3f0bea4d3 100644 --- a/src/plugins/kibana_legacy/common/index.ts +++ b/src/plugins/url_forwarding/public/index.ts @@ -17,5 +17,8 @@ * under the License. */ -export * from './kbn_base_url'; -export * from './migrate_legacy_query'; +import { UrlForwardingPlugin } from './plugin'; + +export const plugin = () => new UrlForwardingPlugin(); + +export * from './plugin'; diff --git a/src/plugins/url_forwarding/public/mocks.ts b/src/plugins/url_forwarding/public/mocks.ts new file mode 100644 index 000000000000..5e32d9b1896b --- /dev/null +++ b/src/plugins/url_forwarding/public/mocks.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { UrlForwardingPlugin } from './plugin'; + +export type Setup = jest.Mocked>; +export type Start = jest.Mocked>; + +const createSetupContract = (): Setup => ({ + forwardApp: jest.fn(), +}); + +const createStartContract = (): Start => ({ + getForwards: jest.fn(), + navigateToDefaultApp: jest.fn(), + navigateToLegacyKibanaUrl: jest.fn(), +}); + +export const urlForwardingPluginMock = { + createSetupContract, + createStartContract, +}; diff --git a/src/plugins/kibana_legacy/public/navigate_to_default_app.ts b/src/plugins/url_forwarding/public/navigate_to_default_app.ts similarity index 100% rename from src/plugins/kibana_legacy/public/navigate_to_default_app.ts rename to src/plugins/url_forwarding/public/navigate_to_default_app.ts diff --git a/src/plugins/url_forwarding/public/plugin.ts b/src/plugins/url_forwarding/public/plugin.ts new file mode 100644 index 000000000000..8ef23fb2c840 --- /dev/null +++ b/src/plugins/url_forwarding/public/plugin.ts @@ -0,0 +1,134 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 { CoreStart, CoreSetup } from 'kibana/public'; +import { KibanaLegacyStart } from 'src/plugins/kibana_legacy/public'; +import { Subscription } from 'rxjs'; +import { navigateToDefaultApp } from './navigate_to_default_app'; +import { createLegacyUrlForwardApp } from './forward_app'; +import { navigateToLegacyKibanaUrl } from './forward_app/navigate_to_legacy_kibana_url'; + +export interface ForwardDefinition { + legacyAppId: string; + newAppId: string; + rewritePath: (legacyPath: string) => string; +} + +export class UrlForwardingPlugin { + private forwardDefinitions: ForwardDefinition[] = []; + private currentAppId: string | undefined; + private currentAppIdSubscription: Subscription | undefined; + + public setup(core: CoreSetup<{}, UrlForwardingStart>) { + core.application.register(createLegacyUrlForwardApp(core, this.forwardDefinitions)); + return { + /** + * Forwards URLs within the legacy `kibana` app to a new platform application. + * + * @param legacyAppId The name of the old app to forward URLs from + * @param newAppId The name of the new app that handles the URLs now + * @param rewritePath Function to rewrite the legacy sub path of the app to the new path in the core app. + * If none is provided, it will just strip the prefix of the legacyAppId away + * + * path into the new path + * + * Example usage: + * ``` + * urlForwarding.forwardApp( + * 'old', + * 'new', + * path => { + * const [, id] = /old/item\/(.*)$/.exec(path) || []; + * if (!id) { + * return '#/home'; + * } + * return '#/items/${id}'; + * } + * ); + * ``` + * This will cause the following redirects: + * + * * app/kibana#/old/ -> app/new#/home + * * app/kibana#/old/item/123 -> app/new#/items/123 + * + */ + forwardApp: ( + legacyAppId: string, + newAppId: string, + rewritePath?: (legacyPath: string) => string + ) => { + this.forwardDefinitions.push({ + legacyAppId, + newAppId, + rewritePath: rewritePath || ((path) => `#${path.replace(`/${legacyAppId}`, '') || '/'}`), + }); + }, + }; + } + + public start( + { application, http: { basePath }, uiSettings }: CoreStart, + { kibanaLegacy }: { kibanaLegacy: KibanaLegacyStart } + ) { + this.currentAppIdSubscription = application.currentAppId$.subscribe((currentAppId) => { + this.currentAppId = currentAppId; + }); + return { + /** + * Navigates to the app defined as kibana.defaultAppId. + * This takes redirects into account and uses the right mechanism to navigate. + */ + navigateToDefaultApp: ( + { overwriteHash }: { overwriteHash: boolean } = { overwriteHash: true } + ) => { + navigateToDefaultApp( + kibanaLegacy.config.defaultAppId, + this.forwardDefinitions, + application, + basePath, + this.currentAppId, + overwriteHash + ); + }, + /** + * Resolves the provided hash using the registered forwards and navigates to the target app. + * If a navigation happened, `{ navigated: true }` will be returned. + * If no matching forward is found, `{ navigated: false }` will be returned. + * @param hash + */ + navigateToLegacyKibanaUrl: (hash: string) => { + return navigateToLegacyKibanaUrl(hash, this.forwardDefinitions, basePath, application); + }, + /** + * @deprecated + * Just exported for wiring up with legacy platform, should not be used. + */ + getForwards: () => this.forwardDefinitions, + }; + } + + public stop() { + if (this.currentAppIdSubscription) { + this.currentAppIdSubscription.unsubscribe(); + } + } +} + +export type UrlForwardingSetup = ReturnType; +export type UrlForwardingStart = ReturnType; diff --git a/src/plugins/visualizations/server/plugin.ts b/src/plugins/visualizations/server/plugin.ts index 993612d22ebf..7502968a3365 100644 --- a/src/plugins/visualizations/server/plugin.ts +++ b/src/plugins/visualizations/server/plugin.ts @@ -19,6 +19,8 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; +import { Observable } from 'rxjs'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { PluginInitializerContext, CoreSetup, @@ -32,16 +34,19 @@ import { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; import { visualizationSavedObjectType } from './saved_objects'; import { VisualizationsPluginSetup, VisualizationsPluginStart } from './types'; +import { registerVisualizationsCollector } from './usage_collector'; export class VisualizationsPlugin implements Plugin { private readonly logger: Logger; + private readonly config: Observable<{ kibana: { index: string } }>; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.config = initializerContext.config.legacy.globalConfig$; } - public setup(core: CoreSetup) { + public setup(core: CoreSetup, plugins: { usageCollection?: UsageCollectionSetup }) { this.logger.debug('visualizations: Setup'); core.savedObjects.registerType(visualizationSavedObjectType); @@ -61,6 +66,10 @@ export class VisualizationsPlugin }, }); + if (plugins.usageCollection) { + registerVisualizationsCollector(plugins.usageCollection, this.config); + } + return {}; } diff --git a/src/plugins/visualizations/server/usage_collector/get_past_days.test.ts b/src/plugins/visualizations/server/usage_collector/get_past_days.test.ts new file mode 100644 index 000000000000..7ef3009de9e5 --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/get_past_days.test.ts @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import moment from 'moment'; +import { getPastDays } from './get_past_days'; + +describe('getPastDays', () => { + test('Returns 2 days that have passed from the current date', () => { + const pastDate = moment().subtract(2, 'days').startOf('day').toString(); + + expect(getPastDays(pastDate)).toEqual(2); + }); + + test('Returns 30 days that have passed from the current date', () => { + const pastDate = moment().subtract(30, 'days').startOf('day').toString(); + + expect(getPastDays(pastDate)).toEqual(30); + }); +}); diff --git a/src/plugins/visualizations/server/usage_collector/get_past_days.ts b/src/plugins/visualizations/server/usage_collector/get_past_days.ts new file mode 100644 index 000000000000..5fa68d80de11 --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/get_past_days.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const getPastDays = (dateString: string): number => { + const date = new Date(dateString); + const today = new Date(); + const diff = Math.abs(date.getTime() - today.getTime()); + return Math.trunc(diff / (1000 * 60 * 60 * 24)); +}; diff --git a/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts b/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts new file mode 100644 index 000000000000..4a8e4b70ae07 --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/get_usage_collector.test.ts @@ -0,0 +1,195 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import moment from 'moment'; +import { of } from 'rxjs'; + +import { LegacyAPICaller } from 'src/core/server'; +import { getUsageCollector } from './get_usage_collector'; + +const defaultMockSavedObjects = [ + { + _id: 'visualization:coolviz-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "shell_beads"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), + }, + }, +]; + +const enlargedMockSavedObjects = [ + // default space + { + _id: 'visualization:coolviz-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), + }, + }, + { + _id: 'visualization:coolviz-456', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "printing_press"}' }, + updated_at: moment().subtract(20, 'days').startOf('day').toString(), + }, + }, + { + _id: 'meat:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(2, 'months').startOf('day').toString(), + }, + }, + // meat space + { + _id: 'meat:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(89, 'days').startOf('day').toString(), + }, + }, + { + _id: 'meat:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cuneiform"}' }, + updated_at: moment().subtract(5, 'months').startOf('day').toString(), + }, + }, + { + _id: 'meat:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cuneiform"}' }, + updated_at: moment().subtract(2, 'days').startOf('day').toString(), + }, + }, + { + _id: 'meat:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(7, 'days').startOf('day').toString(), + }, + }, + // cyber space + { + _id: 'cyber:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(7, 'months').startOf('day').toString(), + }, + }, + { + _id: 'cyber:visualization:coolviz-789', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "floppy_disk"}' }, + updated_at: moment().subtract(3, 'days').startOf('day').toString(), + }, + }, + { + _id: 'cyber:visualization:coolviz-123', + _source: { + type: 'visualization', + visualization: { visState: '{"type": "cave_painting"}' }, + updated_at: moment().subtract(15, 'days').startOf('day').toString(), + }, + }, +]; + +describe('Visualizations usage collector', () => { + const configMock = of({ kibana: { index: '' } }); + const usageCollector = getUsageCollector(configMock); + const getMockCallCluster = (hits: unknown[]) => + (() => Promise.resolve({ hits: { hits } }) as unknown) as LegacyAPICaller; + + test('Should fit the shape', () => { + expect(usageCollector.type).toBe('visualization_types'); + expect(usageCollector.isReady()).toBe(true); + expect(usageCollector.fetch).toEqual(expect.any(Function)); + }); + + test('Summarizes visualizations response data', async () => { + const result = await usageCollector.fetch(getMockCallCluster(defaultMockSavedObjects)); + + expect(result).toMatchObject({ + shell_beads: { + spaces_avg: 1, + spaces_max: 1, + spaces_min: 1, + total: 1, + saved_7_days_total: 1, + saved_30_days_total: 1, + saved_90_days_total: 1, + }, + }); + }); + + test('Summarizes visualizations response data per Space', async () => { + const expectedStats = { + cave_painting: { + total: 3, + spaces_min: 1, + spaces_max: 1, + spaces_avg: 1, + saved_7_days_total: 1, + saved_30_days_total: 2, + saved_90_days_total: 3, + }, + printing_press: { + total: 1, + spaces_min: 1, + spaces_max: 1, + spaces_avg: 1, + saved_7_days_total: 0, + saved_30_days_total: 1, + saved_90_days_total: 1, + }, + cuneiform: { + total: 2, + spaces_min: 2, + spaces_max: 2, + spaces_avg: 2, + saved_7_days_total: 1, + saved_30_days_total: 1, + saved_90_days_total: 1, + }, + floppy_disk: { + total: 4, + spaces_min: 2, + spaces_max: 2, + spaces_avg: 2, + saved_7_days_total: 2, + saved_30_days_total: 2, + saved_90_days_total: 3, + }, + }; + + const result = await usageCollector.fetch(getMockCallCluster(enlargedMockSavedObjects)); + + expect(result).toMatchObject(expectedStats); + }); +}); diff --git a/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts b/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts new file mode 100644 index 000000000000..165c3ee64986 --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/get_usage_collector.ts @@ -0,0 +1,107 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; +import { countBy, get, groupBy, mapValues, max, min, values } from 'lodash'; +import { first } from 'rxjs/operators'; +import { SearchResponse } from 'elasticsearch'; + +import { LegacyAPICaller } from 'src/core/server'; +import { getPastDays } from './get_past_days'; + +const VIS_USAGE_TYPE = 'visualization_types'; + +type ESResponse = SearchResponse<{ visualization: { visState: string } }>; + +interface VisSummary { + type: string; + space: string; + past_days: number; +} + +/* + * Parse the response data into telemetry payload + */ +async function getStats(callCluster: LegacyAPICaller, index: string) { + const searchParams = { + size: 10000, // elasticsearch index.max_result_window default value + index, + ignoreUnavailable: true, + filterPath: [ + 'hits.hits._id', + 'hits.hits._source.visualization', + 'hits.hits._source.updated_at', + ], + body: { + query: { + bool: { filter: { term: { type: 'visualization' } } }, + }, + }, + }; + const esResponse: ESResponse = await callCluster('search', searchParams); + const size = get(esResponse, 'hits.hits.length'); + if (size < 1) { + return; + } + + // `map` to get the raw types + const visSummaries: VisSummary[] = esResponse.hits.hits.map((hit) => { + const spacePhrases = hit._id.split(':'); + const lastUpdated: string = get(hit, '_source.updated_at'); + const space = spacePhrases.length === 3 ? spacePhrases[0] : 'default'; // if in a custom space, the format of a saved object ID is space:type:id + const visualization = get(hit, '_source.visualization', { visState: '{}' }); + const visState: { type?: string } = JSON.parse(visualization.visState); + return { + type: visState.type || '_na_', + space, + past_days: getPastDays(lastUpdated), + }; + }); + + // organize stats per type + const visTypes = groupBy(visSummaries, 'type'); + + // get the final result + return mapValues(visTypes, (curr) => { + const total = curr.length; + const spacesBreakdown = countBy(curr, 'space'); + const spaceCounts: number[] = values(spacesBreakdown); + + return { + total, + spaces_min: min(spaceCounts), + spaces_max: max(spaceCounts), + spaces_avg: total / spaceCounts.length, + saved_7_days_total: curr.filter((c) => c.past_days <= 7).length, + saved_30_days_total: curr.filter((c) => c.past_days <= 30).length, + saved_90_days_total: curr.filter((c) => c.past_days <= 90).length, + }; + }); +} + +export function getUsageCollector(config: Observable<{ kibana: { index: string } }>) { + return { + type: VIS_USAGE_TYPE, + isReady: () => true, + fetch: async (callCluster: LegacyAPICaller) => { + const index = (await config.pipe(first()).toPromise()).kibana.index; + return await getStats(callCluster, index); + }, + }; +} diff --git a/src/plugins/visualizations/server/usage_collector/index.ts b/src/plugins/visualizations/server/usage_collector/index.ts new file mode 100644 index 000000000000..90ee65bb6ad2 --- /dev/null +++ b/src/plugins/visualizations/server/usage_collector/index.ts @@ -0,0 +1,31 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Observable } from 'rxjs'; + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { getUsageCollector } from './get_usage_collector'; + +export function registerVisualizationsCollector( + collectorSet: UsageCollectionSetup, + config: Observable<{ kibana: { index: string } }> +): void { + const collector = collectorSet.makeUsageCollector(getUsageCollector(config)); + collectorSet.registerCollector(collector); +} diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index 29fcd30184cb..318a1562efdf 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -5,7 +5,7 @@ "ui": true, "requiredPlugins": [ "data", - "kibanaLegacy", + "urlForwarding", "navigation", "savedObjects", "visualizations", diff --git a/src/plugins/visualize/public/application/components/visualize_no_match.tsx b/src/plugins/visualize/public/application/components/visualize_no_match.tsx index 7776c5e8ce48..98f22f25c666 100644 --- a/src/plugins/visualize/public/application/components/visualize_no_match.tsx +++ b/src/plugins/visualize/public/application/components/visualize_no_match.tsx @@ -34,7 +34,7 @@ export const VisualizeNoMatch = () => { useEffect(() => { services.restorePreviousUrl(); - const { navigated } = services.kibanaLegacy.navigateToLegacyKibanaUrl( + const { navigated } = services.urlForwarding.navigateToLegacyKibanaUrl( services.history.location.pathname ); diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 0a12dbc22a74..4bdd19113ddd 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -43,7 +43,7 @@ import { import { SharePluginStart } from 'src/plugins/share/public'; import { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/public'; import { EmbeddableStart } from 'src/plugins/embeddable/public'; -import { KibanaLegacyStart } from 'src/plugins/kibana_legacy/public'; +import { UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import { DashboardStart } from '../../../dashboard/public'; export type PureVisState = SavedVisState; @@ -95,7 +95,7 @@ export interface VisualizeServices extends CoreStart { embeddable: EmbeddableStart; history: History; kbnUrlStateStorage: IKbnUrlStateStorage; - kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; pluginInitializerContext: PluginInitializerContext; chrome: ChromeStart; data: DataPublicPluginStart; diff --git a/src/plugins/visualize/public/application/utils/migrate_legacy_query.ts b/src/plugins/visualize/public/application/utils/migrate_legacy_query.ts new file mode 100644 index 000000000000..8d9b50d5a66b --- /dev/null +++ b/src/plugins/visualize/public/application/utils/migrate_legacy_query.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { has } from 'lodash'; +import { Query } from 'src/plugins/data/public'; + +/** + * Creates a standardized query object from old queries that were either strings or pure ES query DSL + * + * @param query - a legacy query, what used to be stored in SearchSource's query property + * @return Object + */ + +export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { + // Lucene was the only option before, so language-less queries are all lucene + if (!has(query, 'language')) { + return { query, language: 'lucene' }; + } + + return query as Query; +} diff --git a/src/plugins/visualize/public/application/utils/use/use_visualize_app_state.tsx b/src/plugins/visualize/public/application/utils/use/use_visualize_app_state.tsx index 935d4b26c98c..24381ecfc9e2 100644 --- a/src/plugins/visualize/public/application/utils/use/use_visualize_app_state.tsx +++ b/src/plugins/visualize/public/application/utils/use/use_visualize_app_state.tsx @@ -24,7 +24,7 @@ import { EventEmitter } from 'events'; import { i18n } from '@kbn/i18n'; import { MarkdownSimple, toMountPoint } from '../../../../../kibana_react/public'; -import { migrateLegacyQuery } from '../../../../../kibana_legacy/public'; +import { migrateLegacyQuery } from '../migrate_legacy_query'; import { esFilters, connectToQueryState } from '../../../../../data/public'; import { VisualizeServices, diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 7e5cafd3ceec..95d5343d5d69 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -40,7 +40,7 @@ import { import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; import { SharePluginStart, SharePluginSetup } from '../../share/public'; -import { KibanaLegacySetup, KibanaLegacyStart } from '../../kibana_legacy/public'; +import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; import { VisualizationsStart } from '../../visualizations/public'; import { VisualizeConstants } from './application/visualize_constants'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; @@ -66,7 +66,7 @@ export interface VisualizePluginStartDependencies { share?: SharePluginStart; visualizations: VisualizationsStart; embeddable: EmbeddableStart; - kibanaLegacy: KibanaLegacyStart; + urlForwarding: UrlForwardingStart; savedObjects: SavedObjectsStart; dashboard: DashboardStart; uiActions: UiActionsStart; @@ -74,7 +74,7 @@ export interface VisualizePluginStartDependencies { export interface VisualizePluginSetupDependencies { home?: HomePublicPluginSetup; - kibanaLegacy: KibanaLegacySetup; + urlForwarding: UrlForwardingSetup; data: DataPublicPluginSetup; share?: SharePluginSetup; } @@ -90,7 +90,7 @@ export class VisualizePlugin public async setup( core: CoreSetup, - { home, kibanaLegacy, data, share }: VisualizePluginSetupDependencies + { home, urlForwarding, data, share }: VisualizePluginSetupDependencies ) { const { appMounted, @@ -177,7 +177,7 @@ export class VisualizePlugin useHash: coreStart.uiSettings.get('state:storeInSessionStorage'), ...withNotifyOnErrors(coreStart.notifications.toasts), }), - kibanaLegacy: pluginsStart.kibanaLegacy, + urlForwarding: pluginsStart.urlForwarding, pluginInitializerContext: this.initializerContext, chrome: coreStart.chrome, data: pluginsStart.data, @@ -209,7 +209,7 @@ export class VisualizePlugin }, }); - kibanaLegacy.forwardApp('visualize', 'visualize'); + urlForwarding.forwardApp('visualize', 'visualize'); if (home) { home.featureCatalogue.register({ diff --git a/tasks/config/run.js b/tasks/config/run.js index 132b51765b3e..148be6ea8afa 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -154,12 +154,6 @@ module.exports = function () { args: ['scripts/test_hardening.js'], }), - test_package_safer_lodash_set: scriptWithGithubChecks({ - title: '@elastic/safer-lodash-set tests', - cmd: YARN, - args: ['--cwd', 'packages/elastic-safer-lodash-set', 'test'], - }), - apiIntegrationTests: scriptWithGithubChecks({ title: 'API integration tests', cmd: NODE, diff --git a/tasks/jenkins.js b/tasks/jenkins.js index adfb6f0f4686..90efadf41c43 100644 --- a/tasks/jenkins.js +++ b/tasks/jenkins.js @@ -38,7 +38,6 @@ module.exports = function (grunt) { 'run:test_jest_integration', 'run:test_projects', 'run:test_hardening', - 'run:test_package_safer_lodash_set', 'run:apiIntegrationTests', ]); }; diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts index 104094f5b6fb..83eac78621a5 100644 --- a/test/common/services/security/test_user.ts +++ b/test/common/services/security/test_user.ts @@ -65,9 +65,9 @@ export async function createTestUserService( } return new (class TestUser { - async restoreDefaults() { + async restoreDefaults(shouldRefreshBrowser: boolean = true) { if (isEnabled()) { - await this.setRoles(config.get('security.defaultRoles')); + await this.setRoles(config.get('security.defaultRoles'), shouldRefreshBrowser); } } diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 4c97d672bae2..057ae0bd13b6 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -42,6 +42,7 @@ import { FilterBarProvider } from './filter_bar'; import { FlyoutProvider } from './flyout'; import { GlobalNavProvider } from './global_nav'; import { InspectorProvider } from './inspector'; +import { ManagementMenuProvider } from './management'; import { QueryBarProvider } from './query_bar'; import { RemoteProvider } from './remote'; import { RenderableProvider } from './renderable'; @@ -91,4 +92,5 @@ export const services = { savedQueryManagementComponent: SavedQueryManagementComponentProvider, elasticChart: ElasticChartProvider, supertest: KibanaSupertestProvider, + managementMenu: ManagementMenuProvider, }; diff --git a/src/plugins/kibana_legacy/common/kbn_base_url.ts b/test/functional/services/management/index.ts similarity index 93% rename from src/plugins/kibana_legacy/common/kbn_base_url.ts rename to test/functional/services/management/index.ts index 69711626750e..54cd229a8e85 100644 --- a/src/plugins/kibana_legacy/common/kbn_base_url.ts +++ b/test/functional/services/management/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export const kbnBaseUrl = '/app/kibana'; +export { ManagementMenuProvider } from './management_menu'; diff --git a/test/functional/services/management/management_menu.ts b/test/functional/services/management/management_menu.ts new file mode 100644 index 000000000000..9aed490bc699 --- /dev/null +++ b/test/functional/services/management/management_menu.ts @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from 'test/functional/ftr_provider_context'; + +export function ManagementMenuProvider({ getService }: FtrProviderContext) { + const find = getService('find'); + + class ManagementMenu { + public async getSections() { + const sectionsElements = await find.allByCssSelector( + '.mgtSideBarNav > .euiSideNav__content > .euiSideNavItem' + ); + + const sections = []; + + for (const el of sectionsElements) { + const sectionId = await (await el.findByClassName('euiSideNavItemButton')).getAttribute( + 'data-test-subj' + ); + const sectionLinks = await Promise.all( + (await el.findAllByCssSelector('.euiSideNavItem > a.euiSideNavItemButton')).map((item) => + item.getAttribute('data-test-subj') + ) + ); + + sections.push({ sectionId, sectionLinks }); + } + + return sections; + } + } + + return new ManagementMenu(); +} diff --git a/test/scripts/test/safer_lodash_set.sh b/test/scripts/test/safer_lodash_set.sh deleted file mode 100755 index 4d7f9c28210d..000000000000 --- a/test/scripts/test/safer_lodash_set.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -yarn run grunt run:test_package_safer_lodash_set diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 52641ce31f0b..edd2c0aa4740 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -34,7 +34,6 @@ def test() { kibanaPipeline.scriptTask('Jest Unit Tests', 'test/scripts/test/jest_unit.sh'), kibanaPipeline.scriptTask('API Integration Tests', 'test/scripts/test/api_integration.sh'), - kibanaPipeline.scriptTask('@elastic/safer-lodash-set Tests', 'test/scripts/test/safer_lodash_set.sh'), kibanaPipeline.scriptTask('X-Pack SIEM cyclic dependency', 'test/scripts/test/xpack_siem_cyclic_dependency.sh'), kibanaPipeline.scriptTask('X-Pack List cyclic dependency', 'test/scripts/test/xpack_list_cyclic_dependency.sh'), kibanaPipeline.scriptTask('X-Pack Jest Unit Tests', 'test/scripts/test/xpack_jest_unit.sh'), diff --git a/x-pack/legacy/plugins/xpack_main/server/xpack_main.d.ts b/x-pack/legacy/plugins/xpack_main/server/xpack_main.d.ts index f4363a8e57b3..c2ec5662ad12 100644 --- a/x-pack/legacy/plugins/xpack_main/server/xpack_main.d.ts +++ b/x-pack/legacy/plugins/xpack_main/server/xpack_main.d.ts @@ -5,7 +5,7 @@ */ import KbnServer from 'src/legacy/server/kbn_server'; -import { Feature, FeatureConfig } from '../../../../plugins/features/server'; +import { KibanaFeature } from '../../../../plugins/features/server'; import { XPackInfo, XPackInfoOptions } from './lib/xpack_info'; export { XPackFeature } from './lib/xpack_info'; diff --git a/x-pack/package.json b/x-pack/package.json index 3a074ba1f1d7..1e2fa4d7ee55 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -207,7 +207,7 @@ "mocha": "^7.1.1", "mocha-junit-reporter": "^1.23.1", "mochawesome": "^4.1.0", - "mochawesome-merge": "^2.0.1", + "mochawesome-merge": "^4.1.0", "mustache": "^2.3.0", "mutation-observer": "^1.0.3", "node-fetch": "^2.6.0", @@ -268,7 +268,7 @@ "vinyl-fs": "^3.0.3", "whatwg-fetch": "^3.0.0", "xml-crypto": "^1.4.0", - "yargs": "4.8.1" + "yargs": "^15.4.1" }, "dependencies": { "@babel/core": "^7.11.1", diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 3bc8acead6c1..c55b21b2f902 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -331,15 +331,17 @@ const result = await actionsClient.execute({ Kibana ships with a set of built-in action types: -| Type | Id | Description | -| ------------------------- | ------------- | ------------------------------------------------------------------ | -| [Server log](#server-log) | `.server-log` | Logs messages to the Kibana log using Kibana's logger | -| [Email](#email) | `.email` | Sends an email using SMTP | -| [Slack](#slack) | `.slack` | Posts a message to a slack channel | -| [Index](#index) | `.index` | Indexes document(s) into Elasticsearch | -| [Webhook](#webhook) | `.webhook` | Send a payload to a web service using HTTP POST or PUT | -| [PagerDuty](#pagerduty) | `.pagerduty` | Trigger, resolve, or acknowlege an incident to a PagerDuty service | -| [ServiceNow](#servicenow) | `.servicenow` | Create or update an incident to a ServiceNow instance | +| Type | Id | Description | +| ------------------------------- | ------------- | ------------------------------------------------------------------ | +| [Server log](#server-log) | `.server-log` | Logs messages to the Kibana log using Kibana's logger | +| [Email](#email) | `.email` | Sends an email using SMTP | +| [Slack](#slack) | `.slack` | Posts a message to a slack channel | +| [Index](#index) | `.index` | Indexes document(s) into Elasticsearch | +| [Webhook](#webhook) | `.webhook` | Send a payload to a web service using HTTP POST or PUT | +| [PagerDuty](#pagerduty) | `.pagerduty` | Trigger, resolve, or acknowlege an incident to a PagerDuty service | +| [ServiceNow](#servicenow) | `.servicenow` | Create or update an incident to a ServiceNow instance | +| [Jira](#jira) | `.jira` | Create or update an issue to a Jira instance | +| [IBM Resilient](#ibm-resilient) | `.resilient` | Create or update an incident to a IBM Resilient instance | --- @@ -561,8 +563,8 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a | Property | Description | Type | | ------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------- | | savedObjectId | The id of the saved object. | string | -| title | The title of the case. | string _(optional)_ | -| description | The description of the case. | string _(optional)_ | +| title | The title of the incident. | string _(optional)_ | +| description | The description of the incident. | string _(optional)_ | | comment | A comment. | string _(optional)_ | | comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }`. | object[] _(optional)_ | | externalId | The id of the incident in ServiceNow. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | @@ -601,16 +603,16 @@ The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/pla #### `subActionParams (pushToService)` -| Property | Description | Type | -| ------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------- | -| savedObjectId | The id of the saved object | string | -| title | The title of the case | string _(optional)_ | -| description | The description of the case | string _(optional)_ | -| externalId | The id of the incident in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | -| issueType | The id of the issue type in Jira. | string _(optional)_ | -| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | -| labels | An array of labels. | string[] _(optional)_ | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | +| Property | Description | Type | +| ------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------- | +| savedObjectId | The id of the saved object | string | +| title | The title of the issue | string _(optional)_ | +| description | The description of the issue | string _(optional)_ | +| externalId | The id of the issue in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | +| issueType | The id of the issue type in Jira. | string _(optional)_ | +| priority | The name of the priority in Jira. Example: `Medium`. | string _(optional)_ | +| labels | An array of labels. | string[] _(optional)_ | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | #### `subActionParams (issueTypes)` @@ -628,10 +630,10 @@ ID: `.resilient` ### `config` -| Property | Description | Type | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ | -| apiUrl | IBM Resilient instance URL. | string | -| incidentConfiguration | Case configuration object. The object should contain an attribute called `mapping`. A `mapping` is an array of objects. Each mapping object should be of the form `{ source: string, target: string, actionType: string }`. `source` is the Case field. `target` is the Jira field where `source` will be mapped to. `actionType` can be one of `nothing`, `overwrite` or `append`. For example the `{ source: 'title', target: 'summary', actionType: 'overwrite' }` record, inside mapping array, means that the title of a case will be mapped to the short description of an incident in IBM Resilient and will be overwrite on each update. | object | +| Property | Description | Type | +| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| apiUrl | IBM Resilient instance URL. | string | +| incidentConfiguration | Optional property and specific to **Cases only**. If defined, the object should contain an attribute called `mapping`. A `mapping` is an array of objects. Each mapping object should be of the form `{ source: string, target: string, actionType: string }`. `source` is the Case field. `target` is the Jira field where `source` will be mapped to. `actionType` can be one of `nothing`, `overwrite` or `append`. For example the `{ source: 'title', target: 'summary', actionType: 'overwrite' }` record, inside mapping array, means that the title of a case will be mapped to the short description of an incident in IBM Resilient and will be overwrite on each update. | object | ### `secrets` @@ -652,10 +654,12 @@ ID: `.resilient` | Property | Description | Type | | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------- | | savedObjectId | The id of the saved object | string | -| title | The title of the case | string _(optional)_ | -| description | The description of the case | string _(optional)_ | -| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | +| title | The title of the incident | string _(optional)_ | +| description | The description of the incident | string _(optional)_ | +| comments | The comments of the incident. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | | externalId | The id of the incident in IBM Resilient. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | +| incidentTypes | An array with the ids of IBM Resilient incident types. | number[] _(optional)_ | +| severityCode | IBM Resilient id of the severity code. | number _(optional)_ | # Command Line Utility diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts index a48124cdbcb6..14573161b8d5 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -85,7 +85,9 @@ describe('ensureAuthorized', () => { await actionsAuthorization.ensureAuthorized('create', 'myType'); expect(authorization.actions.savedObject.get).toHaveBeenCalledWith('action', 'create'); - expect(checkPrivileges).toHaveBeenCalledWith(mockAuthorizationAction('action', 'create')); + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: mockAuthorizationAction('action', 'create'), + }); expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled(); @@ -131,10 +133,12 @@ describe('ensureAuthorized', () => { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create' ); - expect(checkPrivileges).toHaveBeenCalledWith([ - mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), - mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), - ]); + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [ + mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), + mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), + ], + }); expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index da5a5a1cdc3e..3ba798ddf171 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -42,11 +42,11 @@ export class ActionsAuthorization { const { authorization } = this; if (authorization?.mode?.useRbacForRequest(this.request)) { const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); - const { hasAllRequested, username } = await checkPrivileges( - operationAlias[operation] + const { hasAllRequested, username } = await checkPrivileges({ + kibana: operationAlias[operation] ? operationAlias[operation](authorization) - : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation) - ); + : authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation), + }); if (hasAllRequested) { this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId); } else { diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/api.ts b/x-pack/plugins/actions/server/builtin_action_types/case/api.ts deleted file mode 100644 index de4b7edaed3d..000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/case/api.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 { - ExternalServiceApi, - ExternalServiceParams, - PushToServiceResponse, - GetIncidentApiHandlerArgs, - HandshakeApiHandlerArgs, - PushToServiceApiHandlerArgs, -} from './types'; -import { prepareFieldsForTransformation, transformFields, transformComments } from './utils'; - -const handshakeHandler = async ({ - externalService, - mapping, - params, -}: HandshakeApiHandlerArgs) => {}; -const getIncidentHandler = async ({ - externalService, - mapping, - params, -}: GetIncidentApiHandlerArgs) => {}; - -const pushToServiceHandler = async ({ - externalService, - mapping, - params, -}: PushToServiceApiHandlerArgs): Promise => { - const { externalId, comments } = params; - const updateIncident = externalId ? true : false; - const defaultPipes = updateIncident ? ['informationUpdated'] : ['informationCreated']; - let currentIncident: ExternalServiceParams | undefined; - let res: PushToServiceResponse; - - if (externalId) { - currentIncident = await externalService.getIncident(externalId); - } - - const fields = prepareFieldsForTransformation({ - externalCase: params.externalCase, - mapping, - defaultPipes, - }); - - const incident = transformFields({ - params, - fields, - currentIncident, - }); - - if (updateIncident) { - res = await externalService.updateIncident({ incidentId: externalId, incident }); - } else { - res = await externalService.createIncident({ incident }); - } - - if ( - comments && - Array.isArray(comments) && - comments.length > 0 && - mapping.get('comments')?.actionType !== 'nothing' - ) { - const commentsTransformed = transformComments(comments, ['informationAdded']); - - res.comments = []; - for (const currentComment of commentsTransformed) { - const comment = await externalService.createComment({ - incidentId: res.id, - comment: currentComment, - field: mapping.get('comments')?.target ?? 'comments', - }); - res.comments = [ - ...(res.comments ?? []), - { - commentId: comment.commentId, - pushedDate: comment.pushedDate, - }, - ]; - } - } - - return res; -}; - -export const api: ExternalServiceApi = { - handshake: handshakeHandler, - pushToService: pushToServiceHandler, - getIncident: getIncidentHandler, -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/common_schema.ts b/x-pack/plugins/actions/server/builtin_action_types/case/common_schema.ts deleted file mode 100644 index 5a23eb89339e..000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/case/common_schema.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { schema } from '@kbn/config-schema'; - -export const MappingActionType = schema.oneOf([ - schema.literal('nothing'), - schema.literal('overwrite'), - schema.literal('append'), -]); - -export const MapRecordSchema = schema.object({ - source: schema.string(), - target: schema.string(), - actionType: MappingActionType, -}); - -export const IncidentConfigurationSchema = schema.object({ - mapping: schema.arrayOf(MapRecordSchema), -}); - -export const UserSchema = schema.object({ - fullName: schema.nullable(schema.string()), - username: schema.nullable(schema.string()), -}); - -export const EntityInformation = { - createdAt: schema.nullable(schema.string()), - createdBy: schema.nullable(UserSchema), - updatedAt: schema.nullable(schema.string()), - updatedBy: schema.nullable(UserSchema), -}; - -export const EntityInformationSchema = schema.object(EntityInformation); - -export const CommentSchema = schema.object({ - commentId: schema.string(), - comment: schema.string(), - ...EntityInformation, -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/common_types.ts b/x-pack/plugins/actions/server/builtin_action_types/case/common_types.ts deleted file mode 100644 index cca83fb88ca9..000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/case/common_types.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TypeOf } from '@kbn/config-schema'; -import { - IncidentConfigurationSchema, - MapRecordSchema, - CommentSchema, - EntityInformationSchema, -} from './common_schema'; - -export interface CreateCommentRequest { - [key: string]: string; -} - -export type IncidentConfiguration = TypeOf; -export type MapRecord = TypeOf; -export type Comment = TypeOf; -export type EntityInformation = TypeOf; - -export interface ExternalServiceCommentResponse { - commentId: string; - pushedDate: string; - externalCommentId?: string; -} - -export interface PipedField { - key: string; - value: string; - actionType: string; - pipes: string[]; -} - -export interface TransformFieldsArgs { - params: P; - fields: PipedField[]; - currentIncident?: S; -} - -export interface TransformerArgs { - value: string; - date?: string; - user?: string; - previousValue?: string; -} diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts index f47686c911ff..5a23eb89339e 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts @@ -18,36 +18,18 @@ export const MapRecordSchema = schema.object({ actionType: MappingActionType, }); -export const CaseConfigurationSchema = schema.object({ +export const IncidentConfigurationSchema = schema.object({ mapping: schema.arrayOf(MapRecordSchema), }); -export const ExternalIncidentServiceConfiguration = { - apiUrl: schema.string(), - casesConfiguration: CaseConfigurationSchema, -}; - -export const ExternalIncidentServiceConfigurationSchema = schema.object( - ExternalIncidentServiceConfiguration -); - -export const ExternalIncidentServiceSecretConfiguration = { - password: schema.string(), - username: schema.string(), -}; - -export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( - ExternalIncidentServiceSecretConfiguration -); - export const UserSchema = schema.object({ fullName: schema.nullable(schema.string()), username: schema.nullable(schema.string()), }); -const EntityInformation = { - createdAt: schema.string(), - createdBy: UserSchema, +export const EntityInformation = { + createdAt: schema.nullable(schema.string()), + createdBy: schema.nullable(UserSchema), updatedAt: schema.nullable(schema.string()), updatedBy: schema.nullable(UserSchema), }; @@ -59,40 +41,3 @@ export const CommentSchema = schema.object({ comment: schema.string(), ...EntityInformation, }); - -export const ExecutorSubActionSchema = schema.oneOf([ - schema.literal('getIncident'), - schema.literal('pushToService'), - schema.literal('handshake'), -]); - -export const ExecutorSubActionPushParamsSchema = schema.object({ - savedObjectId: schema.string(), - title: schema.string(), - description: schema.nullable(schema.string()), - comments: schema.nullable(schema.arrayOf(CommentSchema)), - externalId: schema.nullable(schema.string()), - ...EntityInformation, -}); - -export const ExecutorSubActionGetIncidentParamsSchema = schema.object({ - externalId: schema.string(), -}); - -// Reserved for future implementation -export const ExecutorSubActionHandshakeParamsSchema = schema.object({}); - -export const ExecutorParamsSchema = schema.oneOf([ - schema.object({ - subAction: schema.literal('getIncident'), - subActionParams: ExecutorSubActionGetIncidentParamsSchema, - }), - schema.object({ - subAction: schema.literal('handshake'), - subActionParams: ExecutorSubActionHandshakeParamsSchema, - }), - schema.object({ - subAction: schema.literal('pushToService'), - subActionParams: ExecutorSubActionPushParamsSchema, - }), -]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/types.ts b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts index 1030e3d9c5d8..73d8297c638d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/case/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts @@ -4,74 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -// This will have to remain `any` until we can extend connectors with generics -// and circular dependencies eliminated. -/* eslint-disable @typescript-eslint/no-explicit-any */ - import { TypeOf } from '@kbn/config-schema'; -import { Logger } from '../../../../../../src/core/server'; - import { - ExternalIncidentServiceConfigurationSchema, - ExternalIncidentServiceSecretConfigurationSchema, - ExecutorParamsSchema, - CaseConfigurationSchema, + IncidentConfigurationSchema, MapRecordSchema, CommentSchema, - ExecutorSubActionPushParamsSchema, - ExecutorSubActionGetIncidentParamsSchema, - ExecutorSubActionHandshakeParamsSchema, + EntityInformationSchema, } from './schema'; -import { LicenseType } from '../../../../../legacy/common/constants'; - -export interface AnyParams { - [index: string]: string | number | object | undefined | null; -} - -export type ExternalIncidentServiceConfiguration = TypeOf< - typeof ExternalIncidentServiceConfigurationSchema ->; -export type ExternalIncidentServiceSecretConfiguration = TypeOf< - typeof ExternalIncidentServiceSecretConfigurationSchema ->; - -export type ExecutorParams = TypeOf; -export type ExecutorSubActionPushParams = TypeOf; -export type ExecutorSubActionGetIncidentParams = TypeOf< - typeof ExecutorSubActionGetIncidentParamsSchema ->; - -export type ExecutorSubActionHandshakeParams = TypeOf< - typeof ExecutorSubActionHandshakeParamsSchema ->; - -export type CaseConfiguration = TypeOf; +export type IncidentConfiguration = TypeOf; export type MapRecord = TypeOf; export type Comment = TypeOf; - -export interface ExternalServiceConfiguration { - id: string; - name: string; - minimumLicenseRequired: LicenseType; -} - -export interface ExternalServiceCredentials { - config: Record; - secrets: Record; -} - -export interface ExternalServiceValidation { - config: (configurationUtilities: any, configObject: any) => void; - secrets: (configurationUtilities: any, secrets: any) => void; -} - -export interface ExternalServiceIncidentResponse { - id: string; - title: string; - url: string; - pushedDate: string; -} +export type EntityInformation = TypeOf; export interface ExternalServiceCommentResponse { commentId: string; @@ -79,69 +23,6 @@ export interface ExternalServiceCommentResponse { externalCommentId?: string; } -export interface ExternalServiceParams { - [index: string]: any; -} - -export interface ExternalService { - getIncident: (id: string) => Promise; - createIncident: (params: ExternalServiceParams) => Promise; - updateIncident: (params: ExternalServiceParams) => Promise; - createComment: (params: ExternalServiceParams) => Promise; -} - -export interface PushToServiceApiParams extends ExecutorSubActionPushParams { - externalCase: Record; -} - -export interface ExternalServiceApiHandlerArgs { - externalService: ExternalService; - mapping: Map; -} - -export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerArgs { - params: PushToServiceApiParams; -} - -export interface GetIncidentApiHandlerArgs extends ExternalServiceApiHandlerArgs { - params: ExecutorSubActionGetIncidentParams; -} - -export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs { - params: ExecutorSubActionHandshakeParams; -} - -export interface PushToServiceResponse extends ExternalServiceIncidentResponse { - comments?: ExternalServiceCommentResponse[]; -} - -export interface ExternalServiceApi { - handshake: (args: HandshakeApiHandlerArgs) => Promise; - pushToService: (args: PushToServiceApiHandlerArgs) => Promise; - getIncident: (args: GetIncidentApiHandlerArgs) => Promise; -} - -export interface CreateExternalServiceBasicArgs { - api: ExternalServiceApi; - createExternalService: ( - credentials: ExternalServiceCredentials, - logger: Logger, - proxySettings?: any - ) => ExternalService; - logger: Logger; -} - -export interface CreateExternalServiceArgs extends CreateExternalServiceBasicArgs { - config: ExternalServiceConfiguration; - validate: ExternalServiceValidation; - validationSchema: { config: any; secrets: any }; -} - -export interface CreateActionTypeArgs { - configurationUtilities: any; - executor?: any; -} - export interface PipedField { key: string; value: string; @@ -149,16 +30,10 @@ export interface PipedField { pipes: string[]; } -export interface PrepareFieldsForTransformArgs { - externalCase: Record; - mapping: Map; - defaultPipes?: string[]; -} - -export interface TransformFieldsArgs { - params: PushToServiceApiParams; +export interface TransformFieldsArgs { + params: P; fields: PipedField[]; - currentIncident?: ExternalServiceParams; + currentIncident?: S; } export interface TransformerArgs { @@ -167,3 +42,13 @@ export interface TransformerArgs { user?: string; previousValue?: string; } + +export interface AnyParams { + [index: string]: string | number | object | undefined | null; +} + +export interface PrepareFieldsForTransformArgs { + externalCase: Record; + mapping: Map; + defaultPipes?: string[]; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts b/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts index 2e3cee3946d6..600e18eb5daf 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { normalizeMapping, buildMap, @@ -14,7 +16,23 @@ import { } from './utils'; import { SUPPORTED_SOURCE_FIELDS } from './constants'; -import { Comment, MapRecord, PushToServiceApiParams } from './types'; +import { Comment, MapRecord } from './types'; + +interface Entity { + createdAt: string | null; + createdBy: { fullName: string; username: string } | null; + updatedAt: string | null; + updatedBy: { fullName: string; username: string } | null; +} + +interface PushToServiceApiParams extends Entity { + savedObjectId: string; + title: string; + description: string | null; + externalId: string | null; + externalObject: Record; + comments: Comment[]; +} const mapping: MapRecord[] = [ { source: 'title', target: 'short_description', actionType: 'overwrite' }, @@ -22,7 +40,6 @@ const mapping: MapRecord[] = [ { source: 'comments', target: 'comments', actionType: 'append' }, ]; -// eslint-disable-next-line @typescript-eslint/no-explicit-any const finalMapping: Map = new Map(); finalMapping.set('title', { @@ -61,7 +78,7 @@ const fullParams: PushToServiceApiParams = { updatedAt: null, updatedBy: null, externalId: null, - externalCase: { + externalObject: { short_description: 'a title', description: 'a description', }, @@ -154,7 +171,7 @@ describe('mapParams', () => { describe('prepareFieldsForTransformation', () => { test('prepare fields with defaults', () => { const res = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, }); expect(res).toEqual([ @@ -175,7 +192,7 @@ describe('prepareFieldsForTransformation', () => { test('prepare fields with default pipes', () => { const res = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, defaultPipes: ['myTestPipe'], }); @@ -199,11 +216,15 @@ describe('prepareFieldsForTransformation', () => { describe('transformFields', () => { test('transform fields for creation correctly', () => { const fields = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, }); - const res = transformFields({ + const res = transformFields< + PushToServiceApiParams, + {}, + { short_description: string; description: string } + >({ params: fullParams, fields, }); @@ -216,12 +237,16 @@ describe('transformFields', () => { test('transform fields for update correctly', () => { const fields = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, defaultPipes: ['informationUpdated'], }); - const res = transformFields({ + const res = transformFields< + PushToServiceApiParams, + {}, + { short_description: string; description: string } + >({ params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', @@ -245,12 +270,16 @@ describe('transformFields', () => { test('add newline character to description', () => { const fields = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, defaultPipes: ['informationUpdated'], }); - const res = transformFields({ + const res = transformFields< + PushToServiceApiParams, + {}, + { short_description: string; description: string } + >({ params: fullParams, fields, currentIncident: { @@ -263,11 +292,15 @@ describe('transformFields', () => { test('append username if fullname is undefined when create', () => { const fields = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, }); - const res = transformFields({ + const res = transformFields< + PushToServiceApiParams, + {}, + { short_description: string; description: string } + >({ params: { ...fullParams, createdBy: { fullName: '', username: 'elastic' }, @@ -283,12 +316,16 @@ describe('transformFields', () => { test('append username if fullname is undefined when update', () => { const fields = prepareFieldsForTransformation({ - externalCase: fullParams.externalCase, + externalCase: fullParams.externalObject, mapping: finalMapping, defaultPipes: ['informationUpdated'], }); - const res = transformFields({ + const res = transformFields< + PushToServiceApiParams, + {}, + { short_description: string; description: string } + >({ params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts index 701bbea14fde..3d51f5e82627 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts @@ -4,30 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { curry, flow, get } from 'lodash'; -import { schema } from '@kbn/config-schema'; - -import { ActionTypeExecutorOptions, ActionTypeExecutorResult, ActionType } from '../../types'; - -import { ExecutorParamsSchema } from './schema'; -import { - ExternalIncidentServiceConfiguration, - ExternalIncidentServiceSecretConfiguration, -} from './types'; +import { flow, get } from 'lodash'; import { - CreateExternalServiceArgs, - CreateActionTypeArgs, - ExecutorParams, MapRecord, - AnyParams, - CreateExternalServiceBasicArgs, - PrepareFieldsForTransformArgs, - PipedField, TransformFieldsArgs, Comment, - ExecutorSubActionPushParams, - PushToServiceResponse, + EntityInformation, + PipedField, + AnyParams, + PrepareFieldsForTransformArgs, } from './types'; import { transformers } from './transformers'; @@ -61,92 +47,6 @@ export const mapParams = (params: T, mapping: Map async ( - execOptions: ActionTypeExecutorOptions< - ExternalIncidentServiceConfiguration, - ExternalIncidentServiceSecretConfiguration, - ExecutorParams - > -): Promise> => { - const { actionId, config, params, secrets } = execOptions; - const { subAction, subActionParams } = params; - let data = {}; - - const res: ActionTypeExecutorResult = { - status: 'ok', - actionId, - }; - - const externalService = createExternalService( - { - config, - secrets, - }, - logger, - execOptions.proxySettings - ); - - if (!api[subAction]) { - throw new Error('[Action][ExternalService] Unsupported subAction type.'); - } - - if (subAction !== 'pushToService') { - throw new Error('[Action][ExternalService] subAction not implemented.'); - } - - if (subAction === 'pushToService') { - const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; - const { comments, externalId, ...restParams } = pushToServiceParams; - - const mapping = buildMap(config.casesConfiguration.mapping); - const externalCase = mapParams( - restParams as ExecutorSubActionPushParams, - mapping - ); - - data = await api.pushToService({ - externalService, - mapping, - params: { ...pushToServiceParams, externalCase }, - }); - } - - return { - ...res, - data, - }; -}; - -export const createConnector = ({ - api, - config, - validate, - createExternalService, - validationSchema, - logger, -}: CreateExternalServiceArgs) => { - return ({ - configurationUtilities, - executor = createConnectorExecutor({ api, createExternalService, logger }), - }: CreateActionTypeArgs): ActionType => ({ - ...config, - validate: { - config: schema.object(validationSchema.config, { - validate: curry(validate.config)(configurationUtilities), - }), - secrets: schema.object(validationSchema.secrets, { - validate: curry(validate.secrets)(configurationUtilities), - }), - params: ExecutorParamsSchema, - }, - executor, - }); -}; - export const prepareFieldsForTransformation = ({ externalCase, mapping, @@ -165,11 +65,15 @@ export const prepareFieldsForTransformation = ({ }); }; -export const transformFields = ({ +export const transformFields = < + P extends EntityInformation, + S extends Record, + R extends {} +>({ params, fields, currentIncident, -}: TransformFieldsArgs): Record => { +}: TransformFieldsArgs): R => { return fields.reduce((prev, cur) => { const transform = flow(...cur.pipes.map((p) => transformers[p])); return { @@ -177,18 +81,11 @@ export const transformFields = ({ [cur.key]: transform({ value: cur.value, date: params.updatedAt ?? params.createdAt, - user: - (params.updatedBy != null - ? params.updatedBy.fullName - ? params.updatedBy.fullName - : params.updatedBy.username - : params.createdBy.fullName - ? params.createdBy.fullName - : params.createdBy.username) ?? '', + user: getEntity(params), previousValue: currentIncident ? currentIncident[cur.key] : '', }).value, }; - }, {}); + }, {} as R); }; export const transformComments = (comments: Comment[], pipes: string[]): Comment[] => { @@ -197,18 +94,18 @@ export const transformComments = (comments: Comment[], pipes: string[]): Comment comment: flow(...pipes.map((p) => transformers[p]))({ value: c.comment, date: c.updatedAt ?? c.createdAt, - user: - (c.updatedBy != null - ? c.updatedBy.fullName - ? c.updatedBy.fullName - : c.updatedBy.username - : c.createdBy.fullName - ? c.createdBy.fullName - : c.createdBy.username) ?? '', + user: getEntity(c), }).value, })); }; -export const getErrorMessage = (connector: string, msg: string) => { - return `[Action][${connector}]: ${msg}`; -}; +export const getEntity = (entity: EntityInformation): string => + (entity.updatedBy != null + ? entity.updatedBy.fullName + ? entity.updatedBy.fullName + : entity.updatedBy.username + : entity.createdBy != null + ? entity.createdBy.fullName + ? entity.createdBy.fullName + : entity.createdBy.username + : '') ?? ''; diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/case/validators.ts deleted file mode 100644 index 08e8a8be6a3e..000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/case/validators.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 { isEmpty } from 'lodash'; - -import { ActionsConfigurationUtilities } from '../../actions_config'; -import { - ExternalIncidentServiceConfiguration, - ExternalIncidentServiceSecretConfiguration, -} from './types'; - -import * as i18n from './translations'; - -export const validateCommonConfig = ( - configurationUtilities: ActionsConfigurationUtilities, - configObject: ExternalIncidentServiceConfiguration -) => { - try { - if (isEmpty(configObject.casesConfiguration.mapping)) { - return i18n.MAPPING_EMPTY; - } - - configurationUtilities.ensureUriAllowed(configObject.apiUrl); - } catch (allowListError) { - return i18n.WHITE_LISTED_ERROR(allowListError.message); - } -}; - -export const validateCommonSecrets = ( - configurationUtilities: ActionsConfigurationUtilities, - secrets: ExternalIncidentServiceSecretConfiguration -) => {}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts index da47a4bfb839..a64eb7a2036c 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { flow } from 'lodash'; import { ExternalServiceParams, PushToServiceApiHandlerArgs, @@ -15,14 +14,11 @@ import { GetFieldsByIssueTypeHandlerArgs, GetIssueTypesHandlerArgs, PushToServiceApiParams, + PushToServiceResponse, } from './types'; // TODO: to remove, need to support Case -import { transformers } from '../case/transformers'; -import { TransformFieldsArgs, Comment, EntityInformation } from '../case/common_types'; - -import { PushToServiceResponse } from './types'; -import { prepareFieldsForTransformation } from '../case/utils'; +import { prepareFieldsForTransformation, transformFields, transformComments } from '../case/utils'; const handshakeHandler = async ({ externalService, @@ -81,7 +77,7 @@ const pushToServiceHandler = async ({ defaultPipes, }); - incident = transformFields({ + incident = transformFields({ params, fields, currentIncident, @@ -132,47 +128,6 @@ const pushToServiceHandler = async ({ return res; }; -export const transformFields = ({ - params, - fields, - currentIncident, -}: TransformFieldsArgs): Incident => { - return fields.reduce((prev, cur) => { - const transform = flow(...cur.pipes.map((p) => transformers[p])); - return { - ...prev, - [cur.key]: transform({ - value: cur.value, - date: params.updatedAt ?? params.createdAt, - user: getEntity(params), - previousValue: currentIncident ? currentIncident[cur.key] : '', - }).value, - }; - }, {} as Incident); -}; - -export const transformComments = (comments: Comment[], pipes: string[]): Comment[] => { - return comments.map((c) => ({ - ...c, - comment: flow(...pipes.map((p) => transformers[p]))({ - value: c.comment, - date: c.updatedAt ?? c.createdAt, - user: getEntity(c), - }).value, - })); -}; - -export const getEntity = (entity: EntityInformation): string => - (entity.updatedBy != null - ? entity.updatedBy.fullName - ? entity.updatedBy.fullName - : entity.updatedBy.username - : entity.createdBy != null - ? entity.createdBy.fullName - ? entity.createdBy.fullName - : entity.createdBy.username - : '') ?? ''; - export const api: ExternalServiceApi = { handshake: handshakeHandler, pushToService: pushToServiceHandler, diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts index e7841996fede..53f8d43ebc2d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts @@ -6,7 +6,7 @@ import { ExternalService, PushToServiceApiParams, ExecutorSubActionPushParams } from './types'; -import { MapRecord } from '../case/common_types'; +import { MapRecord } from '../case/types'; const createMock = (): jest.Mocked => { const service = { diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts index 07c8e22812b2..9fee465e72ef 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts @@ -5,11 +5,7 @@ */ import { schema } from '@kbn/config-schema'; -import { - CommentSchema, - EntityInformation, - IncidentConfigurationSchema, -} from '../case/common_schema'; +import { CommentSchema, EntityInformation, IncidentConfigurationSchema } from '../case/schema'; export const ExternalIncidentServiceConfiguration = { apiUrl: schema.string(), diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts index 5e97f5309f8e..6fe7c62976f2 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts @@ -19,8 +19,8 @@ import { ExecutorSubActionGetFieldsByIssueTypeParamsSchema, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; -import { IncidentConfigurationSchema } from '../case/common_schema'; -import { Comment } from '../case/common_types'; +import { IncidentConfigurationSchema } from '../case/schema'; +import { Comment } from '../case/types'; import { Logger } from '../../../../../../src/core/server'; export type JiraPublicConfigurationType = TypeOf; diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/api.test.ts index 734f6be38262..e974fedd0775 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/api.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/api.test.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { api } from '../case/api'; +import { Logger } from '../../../../../../src/core/server'; +import { api } from './api'; import { externalServiceMock, mapping, apiParams } from './mocks'; -import { ExternalService } from '../case/types'; +import { ExternalService } from './types'; + +let mockedLogger: jest.Mocked; describe('api', () => { let externalService: jest.Mocked; @@ -23,7 +26,12 @@ describe('api', () => { describe('create incident', () => { test('it creates an incident', async () => { const params = { ...apiParams, externalId: null }; - const res = await api.pushToService({ externalService, mapping, params }); + const res = await api.pushToService({ + externalService, + mapping, + params, + logger: mockedLogger, + }); expect(res).toEqual({ id: '1', @@ -45,7 +53,12 @@ describe('api', () => { test('it creates an incident without comments', async () => { const params = { ...apiParams, externalId: null, comments: [] }; - const res = await api.pushToService({ externalService, mapping, params }); + const res = await api.pushToService({ + externalService, + mapping, + params, + logger: mockedLogger, + }); expect(res).toEqual({ id: '1', @@ -57,7 +70,7 @@ describe('api', () => { test('it calls createIncident correctly', async () => { const params = { ...apiParams, externalId: null }; - await api.pushToService({ externalService, mapping, params }); + await api.pushToService({ externalService, mapping, params, logger: mockedLogger }); expect(externalService.createIncident).toHaveBeenCalledWith({ incident: { @@ -71,7 +84,7 @@ describe('api', () => { test('it calls createComment correctly', async () => { const params = { ...apiParams, externalId: null }; - await api.pushToService({ externalService, mapping, params }); + await api.pushToService({ externalService, mapping, params, logger: mockedLogger }); expect(externalService.createComment).toHaveBeenCalledTimes(2); expect(externalService.createComment).toHaveBeenNthCalledWith(1, { incidentId: '1', @@ -89,7 +102,6 @@ describe('api', () => { username: 'elastic', }, }, - field: 'comments', }); expect(externalService.createComment).toHaveBeenNthCalledWith(2, { @@ -108,14 +120,18 @@ describe('api', () => { username: 'elastic', }, }, - field: 'comments', }); }); }); describe('update incident', () => { test('it updates an incident', async () => { - const res = await api.pushToService({ externalService, mapping, params: apiParams }); + const res = await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(res).toEqual({ id: '1', @@ -137,7 +153,12 @@ describe('api', () => { test('it updates an incident without comments', async () => { const params = { ...apiParams, comments: [] }; - const res = await api.pushToService({ externalService, mapping, params }); + const res = await api.pushToService({ + externalService, + mapping, + params, + logger: mockedLogger, + }); expect(res).toEqual({ id: '1', @@ -149,7 +170,7 @@ describe('api', () => { test('it calls updateIncident correctly', async () => { const params = { ...apiParams }; - await api.pushToService({ externalService, mapping, params }); + await api.pushToService({ externalService, mapping, params, logger: mockedLogger }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', @@ -164,7 +185,7 @@ describe('api', () => { test('it calls createComment correctly', async () => { const params = { ...apiParams }; - await api.pushToService({ externalService, mapping, params }); + await api.pushToService({ externalService, mapping, params, logger: mockedLogger }); expect(externalService.createComment).toHaveBeenCalledTimes(2); expect(externalService.createComment).toHaveBeenNthCalledWith(1, { incidentId: '1', @@ -182,7 +203,6 @@ describe('api', () => { username: 'elastic', }, }, - field: 'comments', }); expect(externalService.createComment).toHaveBeenNthCalledWith(2, { @@ -201,11 +221,52 @@ describe('api', () => { username: 'elastic', }, }, - field: 'comments', }); }); }); + describe('incidentTypes', () => { + test('it returns the incident types correctly', async () => { + const res = await api.incidentTypes({ + externalService, + params: {}, + }); + expect(res).toEqual([ + { + id: 17, + name: 'Communication error (fax; email)', + }, + { + id: 1001, + name: 'Custom type', + }, + ]); + }); + }); + + describe('severity', () => { + test('it returns the severity correctly', async () => { + const res = await api.severity({ + externalService, + params: { id: '10006' }, + }); + expect(res).toEqual([ + { + id: 4, + name: 'Low', + }, + { + id: 5, + name: 'Medium', + }, + { + id: 6, + name: 'High', + }, + ]); + }); + }); + describe('mapping variations', () => { test('overwrite & append', async () => { mapping.set('title', { @@ -228,7 +289,12 @@ describe('api', () => { actionType: 'overwrite', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -260,7 +326,12 @@ describe('api', () => { actionType: 'nothing', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -291,7 +362,12 @@ describe('api', () => { actionType: 'append', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -324,7 +400,12 @@ describe('api', () => { actionType: 'nothing', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: {}, @@ -352,7 +433,12 @@ describe('api', () => { actionType: 'overwrite', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -382,7 +468,12 @@ describe('api', () => { actionType: 'overwrite', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -414,7 +505,12 @@ describe('api', () => { actionType: 'nothing', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -445,7 +541,12 @@ describe('api', () => { actionType: 'append', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -478,7 +579,12 @@ describe('api', () => { actionType: 'append', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.updateIncident).toHaveBeenCalledWith({ incidentId: 'incident-3', incident: { @@ -509,7 +615,12 @@ describe('api', () => { actionType: 'overwrite', }); - await api.pushToService({ externalService, mapping, params: apiParams }); + await api.pushToService({ + externalService, + mapping, + params: apiParams, + logger: mockedLogger, + }); expect(externalService.createComment).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/api.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/api.ts index 3db66e5884af..af3984bf5f0f 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/api.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/api.ts @@ -4,4 +4,129 @@ * you may not use this file except in compliance with the Elastic License. */ -export { api } from '../case/api'; +import { + ExternalServiceParams, + PushToServiceApiHandlerArgs, + HandshakeApiHandlerArgs, + GetIncidentApiHandlerArgs, + ExternalServiceApi, + Incident, + GetIncidentTypesHandlerArgs, + GetSeverityHandlerArgs, + PushToServiceApiParams, + PushToServiceResponse, +} from './types'; + +// TODO: to remove, need to support Case +import { transformFields, prepareFieldsForTransformation, transformComments } from '../case/utils'; + +const handshakeHandler = async ({ + externalService, + mapping, + params, +}: HandshakeApiHandlerArgs) => {}; + +const getIncidentHandler = async ({ + externalService, + mapping, + params, +}: GetIncidentApiHandlerArgs) => {}; + +const getIncidentTypesHandler = async ({ externalService }: GetIncidentTypesHandlerArgs) => { + const res = await externalService.getIncidentTypes(); + return res; +}; + +const getSeverityHandler = async ({ externalService }: GetSeverityHandlerArgs) => { + const res = await externalService.getSeverity(); + return res; +}; + +const pushToServiceHandler = async ({ + externalService, + mapping, + params, + logger, +}: PushToServiceApiHandlerArgs): Promise => { + const { externalId, comments } = params; + const updateIncident = externalId ? true : false; + const defaultPipes = updateIncident ? ['informationUpdated'] : ['informationCreated']; + let currentIncident: ExternalServiceParams | undefined; + let res: PushToServiceResponse; + + if (externalId) { + try { + currentIncident = await externalService.getIncident(externalId); + } catch (ex) { + logger.debug( + `Retrieving Incident by id ${externalId} from IBM Resilient was failed with exception: ${ex}` + ); + } + } + + let incident: Incident; + // TODO: should be removed later but currently keep it for the Case implementation support + if (mapping) { + const fields = prepareFieldsForTransformation({ + externalCase: params.externalObject, + mapping, + defaultPipes, + }); + + incident = transformFields({ + params, + fields, + currentIncident, + }); + } else { + const { title, description, incidentTypes, severityCode } = params; + incident = { name: title, description, incidentTypes, severityCode }; + } + + if (externalId != null) { + res = await externalService.updateIncident({ + incidentId: externalId, + incident, + }); + } else { + res = await externalService.createIncident({ + incident: { + ...incident, + }, + }); + } + + if (comments && Array.isArray(comments) && comments.length > 0) { + if (mapping && mapping.get('comments')?.actionType === 'nothing') { + return res; + } + const commentsTransformed = mapping + ? transformComments(comments, ['informationAdded']) + : comments; + + res.comments = []; + for (const currentComment of commentsTransformed) { + const comment = await externalService.createComment({ + incidentId: res.id, + comment: currentComment, + }); + res.comments = [ + ...(res.comments ?? []), + { + commentId: comment.commentId, + pushedDate: comment.pushedDate, + }, + ]; + } + } + + return res; +}; + +export const api: ExternalServiceApi = { + handshake: handshakeHandler, + pushToService: pushToServiceHandler, + getIncident: getIncidentHandler, + incidentTypes: getIncidentTypesHandler, + severity: getSeverityHandler, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/config.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/config.ts deleted file mode 100644 index 4ce9417bfa9a..000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/config.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ExternalServiceConfiguration } from '../case/types'; -import * as i18n from './translations'; - -export const config: ExternalServiceConfiguration = { - id: '.resilient', - name: i18n.NAME, - minimumLicenseRequired: 'platinum', -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts index 1e9cb1558970..53285a2a350a 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/index.ts @@ -4,33 +4,139 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger } from '../../../../../../src/core/server'; -import { createConnector } from '../case/utils'; +import { curry } from 'lodash'; +import { schema } from '@kbn/config-schema'; -import { api } from './api'; -import { config } from './config'; import { validate } from './validators'; -import { createExternalService } from './service'; -import { ResilientSecretConfiguration, ResilientPublicConfiguration } from './schema'; +import { + ExternalIncidentServiceConfiguration, + ExternalIncidentServiceSecretConfiguration, + ExecutorParamsSchema, +} from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; -import { ActionType } from '../../types'; +import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types'; +import { createExternalService } from './service'; +import { api } from './api'; +import { + ExecutorParams, + ExecutorSubActionPushParams, + ResilientPublicConfigurationType, + ResilientSecretConfigurationType, + ResilientExecutorResultData, + ExecutorSubActionGetIncidentTypesParams, + ExecutorSubActionGetSeverityParams, +} from './types'; +import * as i18n from './translations'; +import { Logger } from '../../../../../../src/core/server'; -export function getActionType({ - logger, - configurationUtilities, -}: { +// TODO: to remove, need to support Case +import { buildMap, mapParams } from '../case/utils'; + +interface GetActionTypeParams { logger: Logger; configurationUtilities: ActionsConfigurationUtilities; -}): ActionType { - return createConnector({ - api, - config, - validate, - createExternalService, - validationSchema: { - config: ResilientPublicConfiguration, - secrets: ResilientSecretConfiguration, +} + +const supportedSubActions: string[] = ['pushToService', 'incidentTypes', 'severity']; + +// action type definition +export function getActionType( + params: GetActionTypeParams +): ActionType< + ResilientPublicConfigurationType, + ResilientSecretConfigurationType, + ExecutorParams, + ResilientExecutorResultData | {} +> { + const { logger, configurationUtilities } = params; + return { + id: '.resilient', + minimumLicenseRequired: 'platinum', + name: i18n.NAME, + validate: { + config: schema.object(ExternalIncidentServiceConfiguration, { + validate: curry(validate.config)(configurationUtilities), + }), + secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { + validate: curry(validate.secrets)(configurationUtilities), + }), + params: ExecutorParamsSchema, + }, + executor: curry(executor)({ logger }), + }; +} + +// action executor +async function executor( + { logger }: { logger: Logger }, + execOptions: ActionTypeExecutorOptions< + ResilientPublicConfigurationType, + ResilientSecretConfigurationType, + ExecutorParams + > +): Promise> { + const { actionId, config, params, secrets } = execOptions; + const { subAction, subActionParams } = params as ExecutorParams; + let data: ResilientExecutorResultData | null = null; + + const externalService = createExternalService( + { + config, + secrets, }, logger, - })({ configurationUtilities }); + execOptions.proxySettings + ); + + if (!api[subAction]) { + const errorMessage = `[Action][ExternalService] Unsupported subAction type ${subAction}.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (!supportedSubActions.includes(subAction)) { + const errorMessage = `[Action][ExternalService] subAction ${subAction} not implemented.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (subAction === 'pushToService') { + const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; + + const { comments, externalId, ...restParams } = pushToServiceParams; + const mapping = config.incidentConfiguration + ? buildMap(config.incidentConfiguration.mapping) + : null; + const externalObject = + config.incidentConfiguration && mapping + ? mapParams(restParams as ExecutorSubActionPushParams, mapping) + : {}; + + data = await api.pushToService({ + externalService, + mapping, + params: { ...pushToServiceParams, externalObject }, + logger, + }); + + logger.debug(`response push to service for incident id: ${data.id}`); + } + + if (subAction === 'incidentTypes') { + const incidentTypesParams = subActionParams as ExecutorSubActionGetIncidentTypesParams; + data = await api.incidentTypes({ + externalService, + params: incidentTypesParams, + }); + } + + if (subAction === 'severity') { + const severityParams = subActionParams as ExecutorSubActionGetSeverityParams; + data = await api.severity({ + externalService, + params: severityParams, + }); + } + + return { status: 'ok', data: data ?? {}, actionId }; } diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/mocks.ts index bba9c58bf28c..2e841728159a 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/mocks.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/mocks.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - ExternalService, - PushToServiceApiParams, - ExecutorSubActionPushParams, - MapRecord, -} from '../case/types'; +import { ExternalService, PushToServiceApiParams, ExecutorSubActionPushParams } from './types'; + +import { MapRecord } from '../case/types'; const createMock = (): jest.Mocked => { const service = { @@ -40,6 +37,25 @@ const createMock = (): jest.Mocked => { }) ), createComment: jest.fn(), + findIncidents: jest.fn(), + getIncidentTypes: jest.fn().mockImplementation(() => [ + { id: 17, name: 'Communication error (fax; email)' }, + { id: 1001, name: 'Custom type' }, + ]), + getSeverity: jest.fn().mockImplementation(() => [ + { + id: 4, + name: 'Low', + }, + { + id: 5, + name: 'Medium', + }, + { + id: 6, + name: 'High', + }, + ]), }; service.createComment.mockImplementationOnce(() => @@ -96,6 +112,8 @@ const executorParams: ExecutorSubActionPushParams = { updatedBy: { fullName: 'Elastic User', username: 'elastic' }, title: 'Incident title', description: 'Incident description', + incidentTypes: [1001], + severityCode: 6, comments: [ { commentId: 'case-comment-1', @@ -118,7 +136,58 @@ const executorParams: ExecutorSubActionPushParams = { const apiParams: PushToServiceApiParams = { ...executorParams, - externalCase: { name: 'Incident title', description: 'Incident description' }, + externalObject: { name: 'Incident title', description: 'Incident description' }, }; -export { externalServiceMock, mapping, executorParams, apiParams }; +const incidentTypes = [ + { + value: 17, + label: 'Communication error (fax; email)', + enabled: true, + properties: null, + uuid: '4a8d22f7-d89e-4403-85c7-2bafe3b7f2ae', + hidden: false, + default: false, + }, + { + value: 1001, + label: 'Custom type', + enabled: true, + properties: null, + uuid: '3b51c8c2-9758-48f8-b013-bd141f1d2ec9', + hidden: false, + default: false, + }, +]; + +const severity = [ + { + value: 4, + label: 'Low', + enabled: true, + properties: null, + uuid: '97cae239-963d-4e36-be34-07e47ef2cc86', + hidden: false, + default: true, + }, + { + value: 5, + label: 'Medium', + enabled: true, + properties: null, + uuid: 'c2c354c9-6d1e-4a48-82e5-bd5dc5068339', + hidden: false, + default: false, + }, + { + value: 6, + label: 'High', + enabled: true, + properties: null, + uuid: '93e5c99c-563b-48b9-80a3-9572307622d8', + hidden: false, + default: false, + }, +]; + +export { externalServiceMock, mapping, executorParams, apiParams, incidentTypes, severity }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts index c13de2b27e2b..151f703dcc07 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/schema.ts @@ -5,18 +5,77 @@ */ import { schema } from '@kbn/config-schema'; -import { ExternalIncidentServiceConfiguration } from '../case/schema'; +import { CommentSchema, EntityInformation, IncidentConfigurationSchema } from '../case/schema'; -export const ResilientPublicConfiguration = { +export const ExternalIncidentServiceConfiguration = { + apiUrl: schema.string(), orgId: schema.string(), - ...ExternalIncidentServiceConfiguration, + // TODO: to remove - set it optional for the current stage to support Case implementation + incidentConfiguration: schema.nullable(IncidentConfigurationSchema), + isCaseOwned: schema.nullable(schema.boolean()), }; -export const ResilientPublicConfigurationSchema = schema.object(ResilientPublicConfiguration); +export const ExternalIncidentServiceConfigurationSchema = schema.object( + ExternalIncidentServiceConfiguration +); -export const ResilientSecretConfiguration = { +export const ExternalIncidentServiceSecretConfiguration = { apiKeyId: schema.string(), apiKeySecret: schema.string(), }; -export const ResilientSecretConfigurationSchema = schema.object(ResilientSecretConfiguration); +export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( + ExternalIncidentServiceSecretConfiguration +); + +export const ExecutorSubActionSchema = schema.oneOf([ + schema.literal('getIncident'), + schema.literal('pushToService'), + schema.literal('handshake'), + schema.literal('incidentTypes'), + schema.literal('severity'), +]); + +export const ExecutorSubActionPushParamsSchema = schema.object({ + savedObjectId: schema.string(), + title: schema.string(), + description: schema.nullable(schema.string()), + externalId: schema.nullable(schema.string()), + incidentTypes: schema.nullable(schema.arrayOf(schema.number())), + severityCode: schema.nullable(schema.number()), + // TODO: remove later - need for support Case push multiple comments + comments: schema.nullable(schema.arrayOf(CommentSchema)), + ...EntityInformation, +}); + +export const ExecutorSubActionGetIncidentParamsSchema = schema.object({ + externalId: schema.string(), +}); + +// Reserved for future implementation +export const ExecutorSubActionHandshakeParamsSchema = schema.object({}); +export const ExecutorSubActionGetIncidentTypesParamsSchema = schema.object({}); +export const ExecutorSubActionGetSeverityParamsSchema = schema.object({}); + +export const ExecutorParamsSchema = schema.oneOf([ + schema.object({ + subAction: schema.literal('getIncident'), + subActionParams: ExecutorSubActionGetIncidentParamsSchema, + }), + schema.object({ + subAction: schema.literal('handshake'), + subActionParams: ExecutorSubActionHandshakeParamsSchema, + }), + schema.object({ + subAction: schema.literal('pushToService'), + subActionParams: ExecutorSubActionPushParamsSchema, + }), + schema.object({ + subAction: schema.literal('incidentTypes'), + subActionParams: ExecutorSubActionGetIncidentTypesParamsSchema, + }), + schema.object({ + subAction: schema.literal('severity'), + subActionParams: ExecutorSubActionGetSeverityParamsSchema, + }), +]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts index a9271671f68b..86ea352625a5 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.test.ts @@ -8,9 +8,11 @@ import axios from 'axios'; import { createExternalService, getValueTextContent, formatUpdateRequest } from './service'; import * as utils from '../lib/axios_utils'; -import { ExternalService } from '../case/types'; +import { ExternalService } from './types'; import { Logger } from '../../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { incidentTypes, severity } from './mocks'; + const logger = loggingSystemMock.create().get() as jest.Mocked; jest.mock('axios'); @@ -41,6 +43,8 @@ const mockIncidentUpdate = (withUpdateError = false) => { format: 'html', content: 'description', }, + incident_type_ids: [1001, 16, 12], + severity_code: 6, }, })); @@ -246,7 +250,12 @@ describe('IBM Resilient service', () => { })); const res = await service.createIncident({ - incident: { name: 'title', description: 'desc' }, + incident: { + name: 'title', + description: 'desc', + incidentTypes: [1001], + severityCode: 6, + }, }); expect(res).toEqual({ @@ -269,12 +278,18 @@ describe('IBM Resilient service', () => { })); await service.createIncident({ - incident: { name: 'title', description: 'desc' }, + incident: { + name: 'title', + description: 'desc', + incidentTypes: [1001], + severityCode: 6, + }, }); expect(requestMock).toHaveBeenCalledWith({ axios, - url: 'https://resilient.elastic.co/rest/orgs/201/incidents', + url: + 'https://resilient.elastic.co/rest/orgs/201/incidents?text_content_output_format=objects_convert', logger, method: 'post', data: { @@ -284,6 +299,8 @@ describe('IBM Resilient service', () => { content: 'desc', }, discovered_date: TIMESTAMP, + incident_type_ids: [{ id: 1001 }], + severity_code: { id: 6 }, }, }); }); @@ -295,7 +312,12 @@ describe('IBM Resilient service', () => { expect( service.createIncident({ - incident: { name: 'title', description: 'desc' }, + incident: { + name: 'title', + description: 'desc', + incidentTypes: [1001], + severityCode: 6, + }, }) ).rejects.toThrow( '[Action][IBM Resilient]: Unable to create incident. Error: An error has occurred' @@ -308,7 +330,12 @@ describe('IBM Resilient service', () => { mockIncidentUpdate(); const res = await service.updateIncident({ incidentId: '1', - incident: { name: 'title_updated', description: 'desc_updated' }, + incident: { + name: 'title', + description: 'desc', + incidentTypes: [1001], + severityCode: 6, + }, }); expect(res).toEqual({ @@ -324,7 +351,12 @@ describe('IBM Resilient service', () => { await service.updateIncident({ incidentId: '1', - incident: { name: 'title_updated', description: 'desc_updated' }, + incident: { + name: 'title_updated', + description: 'desc_updated', + incidentTypes: [1001], + severityCode: 5, + }, }); // Incident update makes three calls to the API. @@ -356,6 +388,28 @@ describe('IBM Resilient service', () => { }, }, }, + { + field: { + name: 'incident_type_ids', + }, + old_value: { + ids: [1001, 16, 12], + }, + new_value: { + ids: [1001], + }, + }, + { + field: { + name: 'severity_code', + }, + old_value: { + id: 6, + }, + new_value: { + id: 5, + }, + }, ], }, }); @@ -367,7 +421,12 @@ describe('IBM Resilient service', () => { expect( service.updateIncident({ incidentId: '1', - incident: { name: 'title', description: 'desc' }, + incident: { + name: 'title', + description: 'desc', + incidentTypes: [1001], + severityCode: 5, + }, }) ).rejects.toThrow( '[Action][IBM Resilient]: Unable to update incident with id 1. Error: An error has occurred' @@ -386,8 +445,14 @@ describe('IBM Resilient service', () => { const res = await service.createComment({ incidentId: '1', - comment: { comment: 'comment', commentId: 'comment-1' }, - field: 'comments', + comment: { + comment: 'comment', + commentId: 'comment-1', + createdBy: null, + createdAt: null, + updatedAt: null, + updatedBy: null, + }, }); expect(res).toEqual({ @@ -407,8 +472,14 @@ describe('IBM Resilient service', () => { await service.createComment({ incidentId: '1', - comment: { comment: 'comment', commentId: 'comment-1' }, - field: 'my_field', + comment: { + comment: 'comment', + commentId: 'comment-1', + createdBy: null, + createdAt: null, + updatedAt: null, + updatedBy: null, + }, }); expect(requestMock).toHaveBeenCalledWith({ @@ -434,12 +505,82 @@ describe('IBM Resilient service', () => { expect( service.createComment({ incidentId: '1', - comment: { comment: 'comment', commentId: 'comment-1' }, - field: 'comments', + comment: { + comment: 'comment', + commentId: 'comment-1', + createdBy: null, + createdAt: null, + updatedAt: null, + updatedBy: null, + }, }) ).rejects.toThrow( '[Action][IBM Resilient]: Unable to create comment at incident with id 1. Error: An error has occurred' ); }); }); + + describe('getIncidentTypes', () => { + test('it creates the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { + values: incidentTypes, + }, + })); + + const res = await service.getIncidentTypes(); + + expect(res).toEqual([ + { id: 17, name: 'Communication error (fax; email)' }, + { id: 1001, name: 'Custom type' }, + ]); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect(service.getIncidentTypes()).rejects.toThrow( + '[Action][IBM Resilient]: Unable to get incident types. Error: An error has occurred.' + ); + }); + }); + + describe('getSeverity', () => { + test('it creates the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { + values: severity, + }, + })); + + const res = await service.getSeverity(); + + expect(res).toEqual([ + { + id: 4, + name: 'Low', + }, + { + id: 5, + name: 'Medium', + }, + { + id: 6, + name: 'High', + }, + ]); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect(service.getIncidentTypes()).rejects.toThrow( + '[Action][IBM Resilient]: Unable to get incident types. Error: An error has occurred.' + ); + }); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts index b2150081f2c8..4bf1453641e4 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/service.ts @@ -5,44 +5,56 @@ */ import axios from 'axios'; +import { omitBy, isNil } from 'lodash/fp'; import { Logger } from '../../../../../../src/core/server'; -import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from '../case/types'; import { + ExternalServiceCredentials, + ExternalService, + ExternalServiceParams, + CreateCommentParams, + UpdateIncidentParams, + CreateIncidentParams, + CreateIncidentData, ResilientPublicConfigurationType, ResilientSecretConfigurationType, - CreateIncidentRequest, UpdateIncidentRequest, - CreateCommentRequest, - UpdateFieldText, - UpdateFieldTextArea, + GetValueTextContentResponse, } from './types'; import * as i18n from './translations'; import { getErrorMessage, request } from '../lib/axios_utils'; import { ProxySettings } from '../../types'; -const BASE_URL = `rest`; -const INCIDENT_URL = `incidents`; -const COMMENT_URL = `comments`; - const VIEW_INCIDENT_URL = `#incidents`; export const getValueTextContent = ( field: string, - value: string -): UpdateFieldText | UpdateFieldTextArea => { + value: string | number | number[] +): GetValueTextContentResponse => { if (field === 'description') { return { textarea: { format: 'html', - content: value, + content: value as string, }, }; } + if (field === 'incidentTypes') { + return { + ids: value as number[], + }; + } + + if (field === 'severityCode') { + return { + id: value as number, + }; + } + return { - text: value, + text: value as string, }; }; @@ -51,11 +63,30 @@ export const formatUpdateRequest = ({ newIncident, }: ExternalServiceParams): UpdateIncidentRequest => { return { - changes: Object.keys(newIncident).map((key) => ({ - field: { name: key }, - old_value: getValueTextContent(key, oldIncident[key]), - new_value: getValueTextContent(key, newIncident[key]), - })), + changes: Object.keys(newIncident as Record).map((key) => { + let name = key; + + if (key === 'incidentTypes') { + name = 'incident_type_ids'; + } + + if (key === 'severityCode') { + name = 'severity_code'; + } + + return { + field: { name }, + // TODO: Fix ugly casting + old_value: getValueTextContent( + key, + (oldIncident as Record)[name] as string + ), + new_value: getValueTextContent( + key, + (newIncident as Record)[key] as string + ), + }; + }), }; }; @@ -72,8 +103,12 @@ export const createExternalService = ( } const urlWithoutTrailingSlash = url.endsWith('/') ? url.slice(0, -1) : url; - const incidentUrl = `${urlWithoutTrailingSlash}/${BASE_URL}/orgs/${orgId}/${INCIDENT_URL}`; - const commentUrl = `${incidentUrl}/{inc_id}/${COMMENT_URL}`; + const orgUrl = `${urlWithoutTrailingSlash}/rest/orgs/${orgId}`; + const incidentUrl = `${orgUrl}/incidents`; + const commentUrl = `${incidentUrl}/{inc_id}/comments`; + const incidentFieldsUrl = `${orgUrl}/types/incident/fields`; + const incidentTypesUrl = `${incidentFieldsUrl}/incident_type_ids`; + const severityUrl = `${incidentFieldsUrl}/severity_code`; const axiosInstance = axios.create({ auth: { username: apiKeyId, password: apiKeySecret }, }); @@ -101,26 +136,48 @@ export const createExternalService = ( return { ...res.data, description: res.data.description?.content ?? '' }; } catch (error) { throw new Error( - getErrorMessage(i18n.NAME, `Unable to get incident with id ${id}. Error: ${error.message}`) + getErrorMessage(i18n.NAME, `Unable to get incident with id ${id}. Error: ${error.message}.`) ); } }; - const createIncident = async ({ incident }: ExternalServiceParams) => { + const createIncident = async ({ incident }: CreateIncidentParams) => { + let data: CreateIncidentData = { + name: incident.name, + discovered_date: Date.now(), + }; + + if (incident.description) { + data = { + ...data, + description: { + format: 'html', + content: incident.description ?? '', + }, + }; + } + + if (incident.incidentTypes) { + data = { + ...data, + incident_type_ids: incident.incidentTypes.map((id) => ({ id })), + }; + } + + if (incident.severityCode) { + data = { + ...data, + severity_code: { id: incident.severityCode }, + }; + } + try { - const res = await request({ + const res = await request({ axios: axiosInstance, - url: `${incidentUrl}`, + url: `${incidentUrl}?text_content_output_format=objects_convert`, method: 'post', logger, - data: { - ...incident, - description: { - format: 'html', - content: incident.description ?? '', - }, - discovered_date: Date.now(), - }, + data, proxySettings, }); @@ -132,17 +189,20 @@ export const createExternalService = ( }; } catch (error) { throw new Error( - getErrorMessage(i18n.NAME, `Unable to create incident. Error: ${error.message}`) + getErrorMessage(i18n.NAME, `Unable to create incident. Error: ${error.message}.`) ); } }; - const updateIncident = async ({ incidentId, incident }: ExternalServiceParams) => { + const updateIncident = async ({ incidentId, incident }: UpdateIncidentParams) => { try { const latestIncident = await getIncident(incidentId); - const data = formatUpdateRequest({ oldIncident: latestIncident, newIncident: incident }); - const res = await request({ + // Remove null or undefined values. Allowing null values sets the field in IBM Resilient to empty. + const newIncident = omitBy(isNil, incident); + const data = formatUpdateRequest({ oldIncident: latestIncident, newIncident }); + + const res = await request({ axios: axiosInstance, method: 'patch', url: `${incidentUrl}/${incidentId}`, @@ -173,9 +233,9 @@ export const createExternalService = ( } }; - const createComment = async ({ incidentId, comment, field }: ExternalServiceParams) => { + const createComment = async ({ incidentId, comment }: CreateCommentParams) => { try { - const res = await request({ + const res = await request({ axios: axiosInstance, method: 'post', url: getCommentsURL(incidentId), @@ -193,16 +253,62 @@ export const createExternalService = ( throw new Error( getErrorMessage( i18n.NAME, - `Unable to create comment at incident with id ${incidentId}. Error: ${error.message}` + `Unable to create comment at incident with id ${incidentId}. Error: ${error.message}.` ) ); } }; + const getIncidentTypes = async () => { + try { + const res = await request({ + axios: axiosInstance, + method: 'get', + url: incidentTypesUrl, + logger, + proxySettings, + }); + + const incidentTypes = res.data?.values ?? []; + return incidentTypes.map((type: { value: string; label: string }) => ({ + id: type.value, + name: type.label, + })); + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to get incident types. Error: ${error.message}.`) + ); + } + }; + + const getSeverity = async () => { + try { + const res = await request({ + axios: axiosInstance, + method: 'get', + url: severityUrl, + logger, + proxySettings, + }); + + const incidentTypes = res.data?.values ?? []; + return incidentTypes.map((type: { value: string; label: string }) => ({ + id: type.value, + name: type.label, + })); + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to get severity. Error: ${error.message}.`) + ); + } + }; + return { getIncident, createIncident, updateIncident, createComment, + getIncidentTypes, + getSeverity, }; }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/translations.ts index d952838d5a2b..8c6ce9902da8 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/translations.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/translations.ts @@ -9,3 +9,19 @@ import { i18n } from '@kbn/i18n'; export const NAME = i18n.translate('xpack.actions.builtin.case.resilientTitle', { defaultMessage: 'IBM Resilient', }); + +export const ALLOWED_HOSTS_ERROR = (message: string) => + i18n.translate('xpack.actions.builtin.configuration.apiAllowedHostsError', { + defaultMessage: 'error configuring connector action: {message}', + values: { + message, + }, + }); + +// TODO: remove when Case mappings will be removed +export const MAPPING_EMPTY = i18n.translate( + 'xpack.actions.builtin.servicenow.configuration.emptyMapping', + { + defaultMessage: '[incidentConfiguration.mapping]: expected non-empty but got empty', + } +); diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/types.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/types.ts index 6869e2ff3a10..ed622ee473b6 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/types.ts @@ -4,29 +4,175 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { TypeOf } from '@kbn/config-schema'; -import { ResilientPublicConfigurationSchema, ResilientSecretConfigurationSchema } from './schema'; +import { + ExternalIncidentServiceConfigurationSchema, + ExternalIncidentServiceSecretConfigurationSchema, + ExecutorParamsSchema, + ExecutorSubActionPushParamsSchema, + ExecutorSubActionGetIncidentParamsSchema, + ExecutorSubActionHandshakeParamsSchema, + ExecutorSubActionGetIncidentTypesParamsSchema, + ExecutorSubActionGetSeverityParamsSchema, +} from './schema'; + +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { Logger } from '../../../../../../src/core/server'; + +import { IncidentConfigurationSchema } from '../case/schema'; +import { Comment } from '../case/types'; + +export type ResilientPublicConfigurationType = TypeOf< + typeof ExternalIncidentServiceConfigurationSchema +>; +export type ResilientSecretConfigurationType = TypeOf< + typeof ExternalIncidentServiceSecretConfigurationSchema +>; + +export type ExecutorParams = TypeOf; +export type ExecutorSubActionPushParams = TypeOf; + +export type IncidentConfiguration = TypeOf; + +export interface ExternalServiceCredentials { + config: Record; + secrets: Record; +} + +export interface ExternalServiceValidation { + config: (configurationUtilities: ActionsConfigurationUtilities, configObject: any) => void; + secrets: (configurationUtilities: ActionsConfigurationUtilities, secrets: any) => void; +} + +export interface ExternalServiceIncidentResponse { + id: string; + title: string; + url: string; + pushedDate: string; +} + +export interface ExternalServiceCommentResponse { + commentId: string; + pushedDate: string; + externalCommentId?: string; +} -export type ResilientPublicConfigurationType = TypeOf; -export type ResilientSecretConfigurationType = TypeOf; +export type ExternalServiceParams = Record; -interface CreateIncidentBasicRequestArgs { +export type Incident = Pick< + ExecutorSubActionPushParams, + 'description' | 'incidentTypes' | 'severityCode' +> & { name: string; - description: string; - discovered_date: number; +}; + +export interface CreateIncidentParams { + incident: Incident; +} + +export interface UpdateIncidentParams { + incidentId: string; + incident: Incident; +} + +export interface CreateCommentParams { + incidentId: string; + comment: Comment; +} + +export type GetIncidentTypesResponse = Array<{ id: string; name: string }>; +export type GetSeverityResponse = Array<{ id: string; name: string }>; + +export interface ExternalService { + getIncident: (id: string) => Promise; + createIncident: (params: CreateIncidentParams) => Promise; + updateIncident: (params: UpdateIncidentParams) => Promise; + createComment: (params: CreateCommentParams) => Promise; + getIncidentTypes: () => Promise; + getSeverity: () => Promise; } -interface Comment { - text: { format: string; content: string }; +export interface PushToServiceApiParams extends ExecutorSubActionPushParams { + externalObject: Record; } -interface CreateIncidentRequestArgs extends CreateIncidentBasicRequestArgs { - comments?: Comment[]; +export type ExecutorSubActionGetIncidentTypesParams = TypeOf< + typeof ExecutorSubActionGetIncidentTypesParamsSchema +>; + +export type ExecutorSubActionGetSeverityParams = TypeOf< + typeof ExecutorSubActionGetSeverityParamsSchema +>; + +export interface ExternalServiceApiHandlerArgs { + externalService: ExternalService; + mapping: Map | null; } +export type ExecutorSubActionGetIncidentParams = TypeOf< + typeof ExecutorSubActionGetIncidentParamsSchema +>; + +export type ExecutorSubActionHandshakeParams = TypeOf< + typeof ExecutorSubActionHandshakeParamsSchema +>; + +export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: PushToServiceApiParams; + logger: Logger; +} + +export interface GetIncidentApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionGetIncidentParams; +} + +export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionHandshakeParams; +} + +export interface GetIncidentTypesHandlerArgs { + externalService: ExternalService; + params: ExecutorSubActionGetIncidentTypesParams; +} + +export interface GetSeverityHandlerArgs { + externalService: ExternalService; + params: ExecutorSubActionGetSeverityParams; +} + +export interface PushToServiceResponse extends ExternalServiceIncidentResponse { + comments?: ExternalServiceCommentResponse[]; +} + +export interface ExternalServiceApi { + handshake: (args: HandshakeApiHandlerArgs) => Promise; + pushToService: (args: PushToServiceApiHandlerArgs) => Promise; + getIncident: (args: GetIncidentApiHandlerArgs) => Promise; + incidentTypes: (args: GetIncidentTypesHandlerArgs) => Promise; + severity: (args: GetSeverityHandlerArgs) => Promise; +} + +export type ResilientExecutorResultData = + | PushToServiceResponse + | GetIncidentTypesResponse + | GetSeverityResponse; + export interface UpdateFieldText { text: string; } +export interface UpdateFieldText { + text: string; +} + +export interface UpdateIdsField { + ids: number[]; +} + +export interface UpdateIdField { + id: number; +} export interface UpdateFieldTextArea { textarea: { format: 'html' | 'text'; content: string }; @@ -34,13 +180,24 @@ export interface UpdateFieldTextArea { interface UpdateField { field: { name: string }; - old_value: UpdateFieldText | UpdateFieldTextArea; - new_value: UpdateFieldText | UpdateFieldTextArea; + old_value: UpdateFieldText | UpdateFieldTextArea | UpdateIdsField | UpdateIdField; + new_value: UpdateFieldText | UpdateFieldTextArea | UpdateIdsField | UpdateIdField; } -export type CreateIncidentRequest = CreateIncidentRequestArgs; -export type CreateCommentRequest = Comment; - export interface UpdateIncidentRequest { changes: UpdateField[]; } + +export type GetValueTextContentResponse = + | UpdateFieldText + | UpdateFieldTextArea + | UpdateIdsField + | UpdateIdField; + +export interface CreateIncidentData { + name: string; + discovered_date: number; + description?: { format: string; content: string }; + incident_type_ids?: Array<{ id: number }>; + severity_code?: { id: number }; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/resilient/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/resilient/validators.ts index 7226071392bc..a50e868cdda3 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/resilient/validators.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/resilient/validators.ts @@ -4,8 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ -import { validateCommonConfig, validateCommonSecrets } from '../case/validators'; -import { ExternalServiceValidation } from '../case/types'; +import { isEmpty } from 'lodash'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { + ResilientPublicConfigurationType, + ResilientSecretConfigurationType, + ExternalServiceValidation, +} from './types'; + +import * as i18n from './translations'; + +export const validateCommonConfig = ( + configurationUtilities: ActionsConfigurationUtilities, + configObject: ResilientPublicConfigurationType +) => { + if ( + configObject.incidentConfiguration !== null && + isEmpty(configObject.incidentConfiguration.mapping) + ) { + return i18n.MAPPING_EMPTY; + } + + try { + configurationUtilities.ensureUriAllowed(configObject.apiUrl); + } catch (allowedListError) { + return i18n.ALLOWED_HOSTS_ERROR(allowedListError.message); + } +}; + +export const validateCommonSecrets = ( + configurationUtilities: ActionsConfigurationUtilities, + secrets: ResilientSecretConfigurationType +) => {}; export const validate: ExternalServiceValidation = { config: validateCommonConfig, diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts index c8e6147ecef4..455a71517fb4 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { flow } from 'lodash'; import { ExternalServiceParams, PushToServiceApiHandlerArgs, @@ -12,12 +11,11 @@ import { ExternalServiceApi, PushToServiceApiParams, PushToServiceResponse, + Incident, } from './types'; // TODO: to remove, need to support Case -import { transformers } from '../case/transformers'; -import { TransformFieldsArgs, Comment, EntityInformation } from '../case/common_types'; -import { prepareFieldsForTransformation } from '../case/utils'; +import { transformFields, transformComments, prepareFieldsForTransformation } from '../case/utils'; const handshakeHandler = async ({ externalService, @@ -62,7 +60,7 @@ const pushToServiceHandler = async ({ defaultPipes, }); - incident = transformFields({ + incident = transformFields({ params, fields, currentIncident, @@ -117,47 +115,6 @@ const pushToServiceHandler = async ({ return res; }; -export const transformFields = ({ - params, - fields, - currentIncident, -}: TransformFieldsArgs): Record => { - return fields.reduce((prev, cur) => { - const transform = flow(...cur.pipes.map((p) => transformers[p])); - return { - ...prev, - [cur.key]: transform({ - value: cur.value, - date: params.updatedAt ?? params.createdAt, - user: getEntity(params), - previousValue: currentIncident ? currentIncident[cur.key] : '', - }).value, - }; - }, {}); -}; - -export const transformComments = (comments: Comment[], pipes: string[]): Comment[] => { - return comments.map((c) => ({ - ...c, - comment: flow(...pipes.map((p) => transformers[p]))({ - value: c.comment, - date: c.updatedAt ?? c.createdAt, - user: getEntity(c), - }).value, - })); -}; - -export const getEntity = (entity: EntityInformation): string => - (entity.updatedBy != null - ? entity.updatedBy.fullName - ? entity.updatedBy.fullName - : entity.updatedBy.username - : entity.createdBy != null - ? entity.createdBy.fullName - ? entity.createdBy.fullName - : entity.createdBy.username - : '') ?? ''; - export const api: ExternalServiceApi = { handshake: handshakeHandler, pushToService: pushToServiceHandler, diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts index 55a14e4528ac..f34e9714b22c 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts @@ -5,7 +5,7 @@ */ import { ExternalService, PushToServiceApiParams, ExecutorSubActionPushParams } from './types'; -import { MapRecord } from '../case/common_types'; +import { MapRecord } from '../case/types'; const createMock = (): jest.Mocked => { const service = { diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts index 921de42adfca..9896d4175954 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts @@ -5,11 +5,7 @@ */ import { schema } from '@kbn/config-schema'; -import { - CommentSchema, - EntityInformation, - IncidentConfigurationSchema, -} from '../case/common_schema'; +import { CommentSchema, EntityInformation, IncidentConfigurationSchema } from '../case/schema'; export const ExternalIncidentServiceConfiguration = { apiUrl: schema.string(), diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts index e8fcfac45d78..a6a0ac946fe9 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts @@ -16,8 +16,8 @@ import { ExecutorSubActionHandshakeParamsSchema, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; -import { ExternalServiceCommentResponse } from '../case/common_types'; -import { IncidentConfigurationSchema } from '../case/common_schema'; +import { ExternalServiceCommentResponse } from '../case/types'; +import { IncidentConfigurationSchema } from '../case/schema'; import { Logger } from '../../../../../../src/core/server'; export type ServiceNowPublicConfigurationType = TypeOf< @@ -82,6 +82,13 @@ export type ExecutorSubActionHandshakeParams = TypeOf< typeof ExecutorSubActionHandshakeParamsSchema >; +export type Incident = Pick< + ExecutorSubActionPushParams, + 'description' | 'severity' | 'urgency' | 'impact' +> & { + short_description: string; +}; + export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerArgs { params: PushToServiceApiParams; secrets: Record; diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index a6c589928165..592ca93ef5a1 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -159,7 +159,7 @@ export class ActionsPlugin implements Plugin, Plugi ); } - plugins.features.registerFeature(ACTIONS_FEATURE); + plugins.features.registerKibanaFeature(ACTIONS_FEATURE); setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects); this.eventLogService = plugins.eventLog; diff --git a/x-pack/plugins/alerting_builtins/server/plugin.test.ts b/x-pack/plugins/alerting_builtins/server/plugin.test.ts index 15ad06652350..629c02d92307 100644 --- a/x-pack/plugins/alerting_builtins/server/plugin.test.ts +++ b/x-pack/plugins/alerting_builtins/server/plugin.test.ts @@ -43,7 +43,7 @@ describe('AlertingBuiltins Plugin', () => { "name": "Index threshold", } `); - expect(featuresSetup.registerFeature).toHaveBeenCalledWith(BUILT_IN_ALERTS_FEATURE); + expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith(BUILT_IN_ALERTS_FEATURE); }); it('should return a service in the expected shape', async () => { diff --git a/x-pack/plugins/alerting_builtins/server/plugin.ts b/x-pack/plugins/alerting_builtins/server/plugin.ts index 41871c01bfb5..48e5c41cbe63 100644 --- a/x-pack/plugins/alerting_builtins/server/plugin.ts +++ b/x-pack/plugins/alerting_builtins/server/plugin.ts @@ -27,7 +27,7 @@ export class AlertingBuiltinsPlugin implements Plugin { core: CoreSetup, { alerts, features }: AlertingBuiltinsDeps ): Promise { - features.registerFeature(BUILT_IN_ALERTS_FEATURE); + features.registerKibanaFeature(BUILT_IN_ALERTS_FEATURE); registerBuiltInAlertTypes({ service: this.service, diff --git a/x-pack/plugins/alerts/README.md b/x-pack/plugins/alerts/README.md index 6307e463af85..62058d47cbd4 100644 --- a/x-pack/plugins/alerts/README.md +++ b/x-pack/plugins/alerts/README.md @@ -306,7 +306,7 @@ In addition, when users are inside your feature you might want to grant them acc You can control all of these abilities by assigning privileges to the Alerting Framework from within your own feature, for example: ```typescript -features.registerFeature({ +features.registerKibanaFeature({ id: 'my-application-id', name: 'My Application', app: [], @@ -348,7 +348,7 @@ In this example we can see the following: It's important to note that any role can be granted a mix of `all` and `read` privileges accross multiple type, for example: ```typescript -features.registerFeature({ +features.registerKibanaFeature({ id: 'my-application-id', name: 'My Application', app: [], diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.test.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.test.ts index b164d27ded64..c2506381b9df 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.test.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.test.ts @@ -6,7 +6,10 @@ import { KibanaRequest } from 'kibana/server'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; import { securityMock } from '../../../../plugins/security/server/mocks'; -import { PluginStartContract as FeaturesStartContract, Feature } from '../../../features/server'; +import { + PluginStartContract as FeaturesStartContract, + KibanaFeature, +} from '../../../features/server'; import { featuresPluginMock } from '../../../features/server/mocks'; import { AlertsAuthorization, @@ -41,7 +44,7 @@ function mockSecurity() { } function mockFeature(appName: string, typeName?: string) { - return new Feature({ + return new KibanaFeature({ id: appName, name: appName, app: [], @@ -84,7 +87,7 @@ function mockFeature(appName: string, typeName?: string) { } function mockFeatureWithSubFeature(appName: string, typeName: string) { - return new Feature({ + return new KibanaFeature({ id: appName, name: appName, app: [], @@ -174,7 +177,7 @@ beforeEach(() => { async executor() {}, producer: 'myApp', })); - features.getFeatures.mockReturnValue([ + features.getKibanaFeatures.mockReturnValue([ myAppFeature, myOtherAppFeature, myAppWithSubFeature, @@ -255,7 +258,7 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: true, - privileges: [], + privileges: { kibana: [] }, }); await alertAuthorization.ensureAuthorized('myType', 'myApp', WriteOperations.Create); @@ -263,9 +266,9 @@ describe('AlertsAuthorization', () => { expect(alertTypeRegistry.get).toHaveBeenCalledWith('myType'); expect(authorization.actions.alerting.get).toHaveBeenCalledWith('myType', 'myApp', 'create'); - expect(checkPrivileges).toHaveBeenCalledWith([ - mockAuthorizationAction('myType', 'myApp', 'create'), - ]); + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [mockAuthorizationAction('myType', 'myApp', 'create')], + }); expect(auditLogger.alertsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(auditLogger.alertsAuthorizationFailure).not.toHaveBeenCalled(); @@ -298,7 +301,7 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: true, - privileges: [], + privileges: { kibana: [] }, }); await alertAuthorization.ensureAuthorized('myType', 'alerts', WriteOperations.Create); @@ -306,9 +309,9 @@ describe('AlertsAuthorization', () => { expect(alertTypeRegistry.get).toHaveBeenCalledWith('myType'); expect(authorization.actions.alerting.get).toHaveBeenCalledWith('myType', 'myApp', 'create'); - expect(checkPrivileges).toHaveBeenCalledWith([ - mockAuthorizationAction('myType', 'myApp', 'create'), - ]); + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [mockAuthorizationAction('myType', 'myApp', 'create')], + }); expect(auditLogger.alertsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(auditLogger.alertsAuthorizationFailure).not.toHaveBeenCalled(); @@ -332,7 +335,7 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: true, - privileges: [], + privileges: { kibana: [] }, }); const alertAuthorization = new AlertsAuthorization({ @@ -354,10 +357,12 @@ describe('AlertsAuthorization', () => { 'myOtherApp', 'create' ); - expect(checkPrivileges).toHaveBeenCalledWith([ - mockAuthorizationAction('myType', 'myOtherApp', 'create'), - mockAuthorizationAction('myType', 'myApp', 'create'), - ]); + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [ + mockAuthorizationAction('myType', 'myOtherApp', 'create'), + mockAuthorizationAction('myType', 'myApp', 'create'), + ], + }); expect(auditLogger.alertsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(auditLogger.alertsAuthorizationFailure).not.toHaveBeenCalled(); @@ -390,16 +395,18 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myType', 'myApp', 'create'), - authorized: true, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myType', 'myApp', 'create'), + authorized: true, + }, + ], + }, }); await expect( @@ -439,16 +446,18 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myType', 'myApp', 'create'), - authorized: false, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myType', 'myApp', 'create'), + authorized: false, + }, + ], + }, }); await expect( @@ -488,16 +497,18 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myType', 'myApp', 'create'), - authorized: false, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myType', 'myOtherApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myType', 'myApp', 'create'), + authorized: false, + }, + ], + }, }); await expect( @@ -592,7 +603,7 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: true, - privileges: [], + privileges: { kibana: [] }, }); const alertAuthorization = new AlertsAuthorization({ @@ -621,24 +632,26 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), - authorized: false, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), + authorized: false, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -680,24 +693,26 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), - authorized: true, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), + authorized: true, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -728,32 +743,34 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('mySecondAppAlertType', 'myApp', 'find'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('mySecondAppAlertType', 'myOtherApp', 'find'), - authorized: true, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'find'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('mySecondAppAlertType', 'myApp', 'find'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('mySecondAppAlertType', 'myOtherApp', 'find'), + authorized: true, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -903,24 +920,26 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), - authorized: true, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), + authorized: true, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -989,16 +1008,18 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), - authorized: false, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), + authorized: false, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -1048,40 +1069,42 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'get'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'get'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'get'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'get'), - authorized: true, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'get'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'get'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'get'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'get'), + authorized: true, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ @@ -1158,24 +1181,26 @@ describe('AlertsAuthorization', () => { checkPrivileges.mockResolvedValueOnce({ username: 'some-user', hasAllRequested: false, - privileges: [ - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), - authorized: true, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), - authorized: false, - }, - { - privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), - authorized: false, - }, - ], + privileges: { + kibana: [ + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myOtherAppAlertType', 'myOtherApp', 'create'), + authorized: true, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myApp', 'create'), + authorized: false, + }, + { + privilege: mockAuthorizationAction('myAppAlertType', 'myOtherApp', 'create'), + authorized: false, + }, + ], + }, }); const alertAuthorization = new AlertsAuthorization({ diff --git a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts index b362a50c9f10..9dda006c1eb8 100644 --- a/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts +++ b/x-pack/plugins/alerts/server/authorization/alerts_authorization.ts @@ -82,7 +82,7 @@ export class AlertsAuthorization { (disabledFeatures) => new Set( features - .getFeatures() + .getKibanaFeatures() .filter( ({ id, alerting }) => // ignore features which are disabled in the user's space @@ -133,20 +133,21 @@ export class AlertsAuthorization { const shouldAuthorizeConsumer = consumer !== ALERTS_FEATURE_ID; const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request); - const { hasAllRequested, username, privileges } = await checkPrivileges( - shouldAuthorizeConsumer && consumer !== alertType.producer - ? [ - // check for access at consumer level - requiredPrivilegesByScope.consumer, - // check for access at producer level - requiredPrivilegesByScope.producer, - ] - : [ - // skip consumer privilege checks under `alerts` as all alert types can - // be created under `alerts` if you have producer level privileges - requiredPrivilegesByScope.producer, - ] - ); + const { hasAllRequested, username, privileges } = await checkPrivileges({ + kibana: + shouldAuthorizeConsumer && consumer !== alertType.producer + ? [ + // check for access at consumer level + requiredPrivilegesByScope.consumer, + // check for access at producer level + requiredPrivilegesByScope.producer, + ] + : [ + // skip consumer privilege checks under `alerts` as all alert types can + // be created under `alerts` if you have producer level privileges + requiredPrivilegesByScope.producer, + ], + }); if (!isAvailableConsumer) { /** @@ -177,7 +178,7 @@ export class AlertsAuthorization { ); } else { const authorizedPrivileges = map( - privileges.filter((privilege) => privilege.authorized), + privileges.kibana.filter((privilege) => privilege.authorized), 'privilege' ); const unauthorizedScopes = mapValues( @@ -341,9 +342,9 @@ export class AlertsAuthorization { } } - const { username, hasAllRequested, privileges } = await checkPrivileges([ - ...privilegeToAlertType.keys(), - ]); + const { username, hasAllRequested, privileges } = await checkPrivileges({ + kibana: [...privilegeToAlertType.keys()], + }); return { username, @@ -352,7 +353,7 @@ export class AlertsAuthorization { ? // has access to all features this.augmentWithAuthorizedConsumers(alertTypes, await this.allPossibleConsumers) : // only has some of the required privileges - privileges.reduce((authorizedAlertTypes, { authorized, privilege }) => { + privileges.kibana.reduce((authorizedAlertTypes, { authorized, privilege }) => { if (authorized && privilegeToAlertType.has(privilege)) { const [ alertType, diff --git a/x-pack/plugins/alerts/server/plugin.test.ts b/x-pack/plugins/alerts/server/plugin.test.ts index e65d19529025..026aa0c5238d 100644 --- a/x-pack/plugins/alerts/server/plugin.test.ts +++ b/x-pack/plugins/alerts/server/plugin.test.ts @@ -12,7 +12,7 @@ import { taskManagerMock } from '../../task_manager/server/mocks'; import { eventLogServiceMock } from '../../event_log/server/event_log_service.mock'; import { KibanaRequest, CoreSetup } from 'kibana/server'; import { featuresPluginMock } from '../../features/server/mocks'; -import { Feature } from '../../features/server'; +import { KibanaFeature } from '../../features/server'; describe('Alerting Plugin', () => { describe('setup()', () => { @@ -159,8 +159,8 @@ describe('Alerting Plugin', () => { function mockFeatures() { const features = featuresPluginMock.createSetup(); - features.getFeatures.mockReturnValue([ - new Feature({ + features.getKibanaFeatures.mockReturnValue([ + new KibanaFeature({ id: 'appName', name: 'appName', app: [], diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.test.ts b/x-pack/plugins/apm/common/anomaly_detection.test.ts similarity index 74% rename from x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.test.ts rename to x-pack/plugins/apm/common/anomaly_detection.test.ts index 52b7d54236db..21963b5300f8 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.test.ts +++ b/x-pack/plugins/apm/common/anomaly_detection.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getSeverity, severity } from './getSeverity'; +import { getSeverity, Severity } from './anomaly_detection'; describe('getSeverity', () => { describe('when score is undefined', () => { @@ -15,25 +15,25 @@ describe('getSeverity', () => { describe('when score < 25', () => { it('returns warning', () => { - expect(getSeverity(10)).toEqual(severity.warning); + expect(getSeverity(10)).toEqual(Severity.warning); }); }); describe('when score is between 25 and 50', () => { it('returns minor', () => { - expect(getSeverity(40)).toEqual(severity.minor); + expect(getSeverity(40)).toEqual(Severity.minor); }); }); describe('when score is between 50 and 75', () => { it('returns major', () => { - expect(getSeverity(60)).toEqual(severity.major); + expect(getSeverity(60)).toEqual(Severity.major); }); }); describe('when score is 75 or more', () => { it('returns critical', () => { - expect(getSeverity(100)).toEqual(severity.critical); + expect(getSeverity(100)).toEqual(Severity.critical); }); }); }); diff --git a/x-pack/plugins/apm/common/anomaly_detection.ts b/x-pack/plugins/apm/common/anomaly_detection.ts index 07270b572a4b..5d80ee638126 100644 --- a/x-pack/plugins/apm/common/anomaly_detection.ts +++ b/x-pack/plugins/apm/common/anomaly_detection.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import { EuiTheme } from '../../../legacy/common/eui_styled_components'; export interface ServiceAnomalyStats { transactionType?: string; @@ -13,6 +14,82 @@ export interface ServiceAnomalyStats { jobId?: string; } +export enum Severity { + critical = 'critical', + major = 'major', + minor = 'minor', + warning = 'warning', +} + +// TODO: Replace with `getSeverity` from: +// https://github.com/elastic/kibana/blob/0f964f66916480f2de1f4b633e5afafc08cf62a0/x-pack/plugins/ml/common/util/anomaly_utils.ts#L129 +export function getSeverity(score?: number) { + if (typeof score !== 'number') { + return undefined; + } else if (score < 25) { + return Severity.warning; + } else if (score >= 25 && score < 50) { + return Severity.minor; + } else if (score >= 50 && score < 75) { + return Severity.major; + } else if (score >= 75) { + return Severity.critical; + } else { + return undefined; + } +} + +export function getSeverityColor(theme: EuiTheme, severity?: Severity) { + switch (severity) { + case Severity.warning: + return theme.eui.euiColorVis0; + case Severity.minor: + case Severity.major: + return theme.eui.euiColorVis5; + case Severity.critical: + return theme.eui.euiColorVis9; + default: + return; + } +} + +export function getSeverityLabel(severity?: Severity) { + switch (severity) { + case Severity.critical: + return i18n.translate( + 'xpack.apm.servicesTable.serviceHealthStatus.critical', + { + defaultMessage: 'Critical', + } + ); + + case Severity.major: + case Severity.minor: + return i18n.translate( + 'xpack.apm.servicesTable.serviceHealthStatus.warning', + { + defaultMessage: 'Warning', + } + ); + + case Severity.warning: + return i18n.translate( + 'xpack.apm.servicesTable.serviceHealthStatus.healthy', + { + defaultMessage: 'Healthy', + } + ); + + default: + return i18n.translate( + 'xpack.apm.servicesTable.serviceHealthStatus.unknown', + { + defaultMessage: 'Unknown', + } + ); + } +} + export const ML_ERRORS = { INVALID_LICENSE: i18n.translate( 'xpack.apm.anomaly_detection.error.invalid_license', diff --git a/x-pack/plugins/apm/common/service_map.test.ts b/x-pack/plugins/apm/common/service_map.test.ts index 346403efc46a..31f439a7aaec 100644 --- a/x-pack/plugins/apm/common/service_map.test.ts +++ b/x-pack/plugins/apm/common/service_map.test.ts @@ -8,7 +8,7 @@ import { License } from '../../licensing/common/license'; import * as serviceMap from './service_map'; describe('service map helpers', () => { - describe('isValidPlatinumLicense', () => { + describe('isActivePlatinumLicense', () => { describe('with an expired license', () => { it('returns false', () => { const license = new License({ @@ -22,7 +22,7 @@ describe('service map helpers', () => { signature: 'test signature', }); - expect(serviceMap.isValidPlatinumLicense(license)).toEqual(false); + expect(serviceMap.isActivePlatinumLicense(license)).toEqual(false); }); }); @@ -39,7 +39,7 @@ describe('service map helpers', () => { signature: 'test signature', }); - expect(serviceMap.isValidPlatinumLicense(license)).toEqual(false); + expect(serviceMap.isActivePlatinumLicense(license)).toEqual(false); }); }); @@ -56,7 +56,7 @@ describe('service map helpers', () => { signature: 'test signature', }); - expect(serviceMap.isValidPlatinumLicense(license)).toEqual(true); + expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true); }); }); @@ -73,7 +73,7 @@ describe('service map helpers', () => { signature: 'test signature', }); - expect(serviceMap.isValidPlatinumLicense(license)).toEqual(true); + expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true); }); }); @@ -90,7 +90,7 @@ describe('service map helpers', () => { signature: 'test signature', }); - expect(serviceMap.isValidPlatinumLicense(license)).toEqual(true); + expect(serviceMap.isActivePlatinumLicense(license)).toEqual(true); }); }); }); diff --git a/x-pack/plugins/apm/common/service_map.ts b/x-pack/plugins/apm/common/service_map.ts index 7f46fc685d9c..1dc4d598cd2e 100644 --- a/x-pack/plugins/apm/common/service_map.ts +++ b/x-pack/plugins/apm/common/service_map.ts @@ -46,7 +46,7 @@ export interface ServiceNodeStats { avgErrorRate: number | null; } -export function isValidPlatinumLicense(license: ILicense) { +export function isActivePlatinumLicense(license: ILicense) { return license.isActive && license.hasAtLeast('platinum'); } diff --git a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature index 285615108266..82d896c5ba17 100644 --- a/x-pack/plugins/apm/e2e/cypress/integration/apm.feature +++ b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature @@ -1,4 +1,4 @@ -Feature: APM +KibanaFeature: APM Scenario: Transaction duration charts Given a user browses the APM UI application diff --git a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts index c1402bbd035f..66d604a663fb 100644 --- a/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts +++ b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts @@ -26,7 +26,7 @@ When(`the user inspects the opbeans-node service`, () => { }); Then(`should redirect to correct path with correct params`, () => { - cy.url().should('contain', `/app/apm#/services/opbeans-node/transactions`); + cy.url().should('contain', `/app/apm/services/opbeans-node/transactions`); cy.url().should('contain', `transactionType=request`); }); diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 6cc3bb2a2c7e..8aa441758033 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -7,7 +7,8 @@ "apmOss", "data", "licensing", - "triggers_actions_ui" + "triggers_actions_ui", + "embeddable" ], "optionalPlugins": [ "cloud", @@ -22,17 +23,13 @@ ], "server": true, "ui": true, - "configPath": [ - "xpack", - "apm" - ], - "extraPublicDirs": [ - "public/style/variables" - ], + "configPath": ["xpack", "apm"], + "extraPublicDirs": ["public/style/variables"], "requiredBundles": [ "kibanaReact", "kibanaUtils", "observability", - "home" + "home", + "maps" ] } diff --git a/x-pack/plugins/apm/public/application/csmApp.tsx b/x-pack/plugins/apm/public/application/csmApp.tsx index cdfe42bd628c..c63ec3700c87 100644 --- a/x-pack/plugins/apm/public/application/csmApp.tsx +++ b/x-pack/plugins/apm/public/application/csmApp.tsx @@ -26,7 +26,7 @@ import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; import { UrlParamsProvider } from '../context/UrlParamsContext'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { ConfigSchema } from '../index'; -import { ApmPluginSetupDeps } from '../plugin'; +import { ApmPluginSetupDeps, ApmPluginStartDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { px, units } from '../style/variables'; @@ -70,11 +70,13 @@ export function CsmAppRoot({ deps, history, config, + corePlugins: { embeddable }, }: { core: CoreStart; deps: ApmPluginSetupDeps; history: AppMountParameters['history']; config: ConfigSchema; + corePlugins: ApmPluginStartDeps; }) { const i18nCore = core.i18n; const plugins = deps; @@ -86,7 +88,7 @@ export function CsmAppRoot({ return ( - + @@ -110,12 +112,19 @@ export const renderApp = ( core: CoreStart, deps: ApmPluginSetupDeps, { element, history }: AppMountParameters, - config: ConfigSchema + config: ConfigSchema, + corePlugins: ApmPluginStartDeps ) => { createCallApmApi(core.http); ReactDOM.render( - , + , element ); return () => { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/CoreVitalItem.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/CoreVitalItem.tsx index a4cbebf20b54..22d50ca0d5c4 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/CoreVitalItem.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/CoreVitals/CoreVitalItem.tsx @@ -118,7 +118,6 @@ export function CoreVitalItem({ setInFocusInd(ind); }} /> - ); } diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx index f05c07e8512a..48c0f6cc60d8 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx @@ -18,6 +18,7 @@ import { PageLoadDistribution } from './PageLoadDistribution'; import { I18LABELS } from './translations'; import { VisitorBreakdown } from './VisitorBreakdown'; import { CoreVitals } from './CoreVitals'; +import { VisitorBreakdownMap } from './VisitorBreakdownMap'; export function RumDashboard() { return ( @@ -67,6 +68,9 @@ export function RumDashboard() { + + + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx new file mode 100644 index 000000000000..93608a0ccd82 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/EmbeddedMap.tsx @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState, useRef } from 'react'; +import uuid from 'uuid'; +import styled from 'styled-components'; + +import { + MapEmbeddable, + MapEmbeddableInput, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../maps/public/embeddable'; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../../maps/common/constants'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { + ErrorEmbeddable, + ViewMode, + isErrorEmbeddable, +} from '../../../../../../../../src/plugins/embeddable/public'; +import { getLayerList } from './LayerList'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { RenderTooltipContentParams } from '../../../../../../maps/public'; +import { MapToolTip } from './MapToolTip'; +import { useMapFilters } from './useMapFilters'; +import { EmbeddableStart } from '../../../../../../../../src/plugins/embeddable/public'; + +const EmbeddedPanel = styled.div` + z-index: auto; + flex: 1; + display: flex; + flex-direction: column; + height: 100%; + position: relative; + .embPanel__content { + display: flex; + flex: 1 1 100%; + z-index: 1; + min-height: 0; // Absolute must for Firefox to scroll contents + } + &&& .mapboxgl-canvas { + animation: none !important; + } +`; + +interface KibanaDeps { + embeddable: EmbeddableStart; +} +export function EmbeddedMapComponent() { + const { urlParams } = useUrlParams(); + + const { start, end, serviceName } = urlParams; + + const mapFilters = useMapFilters(); + + const [embeddable, setEmbeddable] = useState< + MapEmbeddable | ErrorEmbeddable | undefined + >(); + + const embeddableRoot: React.RefObject = useRef< + HTMLDivElement + >(null); + + const { + services: { embeddable: embeddablePlugin }, + } = useKibana(); + + if (!embeddablePlugin) { + throw new Error('Embeddable start plugin not found'); + } + const factory: any = embeddablePlugin.getEmbeddableFactory( + MAP_SAVED_OBJECT_TYPE + ); + + const input: MapEmbeddableInput = { + id: uuid.v4(), + filters: mapFilters, + refreshConfig: { + value: 0, + pause: false, + }, + viewMode: ViewMode.VIEW, + isLayerTOCOpen: false, + query: { + query: 'transaction.type : "page-load"', + language: 'kuery', + }, + ...(start && { + timeRange: { + from: new Date(start!).toISOString(), + to: new Date(end!).toISOString(), + }, + }), + hideFilterActions: true, + }; + + function renderTooltipContent({ + addFilters, + closeTooltip, + features, + isLocked, + getLayerName, + loadFeatureProperties, + loadFeatureGeometry, + }: RenderTooltipContentParams) { + const props = { + addFilters, + closeTooltip, + isLocked, + getLayerName, + loadFeatureProperties, + loadFeatureGeometry, + }; + + return ; + } + + useEffect(() => { + if (embeddable != null && serviceName) { + embeddable.updateInput({ filters: mapFilters }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mapFilters]); + + // DateRange updated useEffect + useEffect(() => { + if (embeddable != null && start != null && end != null) { + const timeRange = { + from: new Date(start).toISOString(), + to: new Date(end).toISOString(), + }; + embeddable.updateInput({ timeRange }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [start, end]); + + useEffect(() => { + async function setupEmbeddable() { + if (!factory) { + throw new Error('Map embeddable not found.'); + } + const embeddableObject: any = await factory.create({ + ...input, + title: 'Visitors by region', + }); + + if (embeddableObject && !isErrorEmbeddable(embeddableObject)) { + embeddableObject.setRenderTooltipContent(renderTooltipContent); + await embeddableObject.setLayerList(getLayerList()); + } + + setEmbeddable(embeddableObject); + } + + setupEmbeddable(); + + // we want this effect to execute exactly once after the component mounts + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // We can only render after embeddable has already initialized + useEffect(() => { + if (embeddableRoot.current && embeddable) { + embeddable.render(embeddableRoot.current); + } + }, [embeddable, embeddableRoot]); + + return ( + +
+ + ); +} + +EmbeddedMapComponent.displayName = 'EmbeddedMap'; + +export const EmbeddedMap = React.memo(EmbeddedMapComponent); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/LayerList.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/LayerList.ts new file mode 100644 index 000000000000..138a3f4018c6 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/LayerList.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EMSFileSourceDescriptor, + EMSTMSSourceDescriptor, + ESTermSourceDescriptor, + LayerDescriptor as BaseLayerDescriptor, + VectorLayerDescriptor as BaseVectorLayerDescriptor, + VectorStyleDescriptor, +} from '../../../../../../maps/common/descriptor_types'; +import { + AGG_TYPE, + COLOR_MAP_TYPE, + FIELD_ORIGIN, + LABEL_BORDER_SIZES, + STYLE_TYPE, + SYMBOLIZE_AS_TYPES, +} from '../../../../../../maps/common/constants'; + +import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../../../../src/plugins/apm_oss/public'; + +const ES_TERM_SOURCE: ESTermSourceDescriptor = { + type: 'ES_TERM_SOURCE', + id: '3657625d-17b0-41ef-99ba-3a2b2938655c', + indexPatternTitle: 'apm-*', + term: 'client.geo.country_iso_code', + metrics: [ + { + type: AGG_TYPE.AVG, + field: 'transaction.duration.us', + label: 'Page load duration', + }, + ], + indexPatternId: APM_STATIC_INDEX_PATTERN_ID, + applyGlobalQuery: true, +}; + +export const REGION_NAME = 'region_name'; +export const COUNTRY_NAME = 'name'; + +export const TRANSACTION_DURATION_REGION = + '__kbnjoin__avg_of_transaction.duration.us__e62a1b9c-d7ff-4fd4-a0f6-0fdc44bb9e41'; + +export const TRANSACTION_DURATION_COUNTRY = + '__kbnjoin__avg_of_transaction.duration.us__3657625d-17b0-41ef-99ba-3a2b2938655c'; + +interface LayerDescriptor extends BaseLayerDescriptor { + sourceDescriptor: EMSTMSSourceDescriptor; +} + +interface VectorLayerDescriptor extends BaseVectorLayerDescriptor { + sourceDescriptor: EMSFileSourceDescriptor; +} + +export function getLayerList() { + const baseLayer: LayerDescriptor = { + sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true }, + id: 'b7af286d-2580-4f47-be93-9653d594ce7e', + label: null, + minZoom: 0, + maxZoom: 24, + alpha: 1, + visible: true, + style: { type: 'TILE' }, + type: 'VECTOR_TILE', + }; + + const getLayerStyle = (fieldName: string): VectorStyleDescriptor => { + return { + type: 'VECTOR', + properties: { + icon: { type: STYLE_TYPE.STATIC, options: { value: 'marker' } }, + fillColor: { + type: STYLE_TYPE.DYNAMIC, + options: { + color: 'Blue to Red', + colorCategory: 'palette_0', + fieldMetaOptions: { isEnabled: true, sigma: 3 }, + type: COLOR_MAP_TYPE.ORDINAL, + field: { + name: fieldName, + origin: FIELD_ORIGIN.JOIN, + }, + useCustomColorRamp: false, + }, + }, + lineColor: { + type: STYLE_TYPE.DYNAMIC, + options: { color: '#3d3d3d', fieldMetaOptions: { isEnabled: true } }, + }, + lineWidth: { type: STYLE_TYPE.STATIC, options: { size: 1 } }, + iconSize: { type: STYLE_TYPE.STATIC, options: { size: 6 } }, + iconOrientation: { + type: STYLE_TYPE.STATIC, + options: { orientation: 0 }, + }, + labelText: { type: STYLE_TYPE.STATIC, options: { value: '' } }, + labelColor: { + type: STYLE_TYPE.STATIC, + options: { color: '#000000' }, + }, + labelSize: { type: STYLE_TYPE.STATIC, options: { size: 14 } }, + labelBorderColor: { + type: STYLE_TYPE.STATIC, + options: { color: '#FFFFFF' }, + }, + symbolizeAs: { options: { value: SYMBOLIZE_AS_TYPES.CIRCLE } }, + labelBorderSize: { options: { size: LABEL_BORDER_SIZES.SMALL } }, + }, + isTimeAware: true, + }; + }; + + const pageLoadDurationByCountryLayer: VectorLayerDescriptor = { + joins: [ + { + leftField: 'iso2', + right: ES_TERM_SOURCE, + }, + ], + sourceDescriptor: { + type: 'EMS_FILE', + id: 'world_countries', + tooltipProperties: [COUNTRY_NAME], + applyGlobalQuery: true, + }, + style: getLayerStyle(TRANSACTION_DURATION_COUNTRY), + id: 'e8d1d974-eed8-462f-be2c-f0004b7619b2', + label: null, + minZoom: 0, + maxZoom: 24, + alpha: 0.75, + visible: true, + type: 'VECTOR', + }; + + const pageLoadDurationByAdminRegionLayer: VectorLayerDescriptor = { + joins: [ + { + leftField: 'region_iso_code', + right: { + type: 'ES_TERM_SOURCE', + id: 'e62a1b9c-d7ff-4fd4-a0f6-0fdc44bb9e41', + indexPatternTitle: 'apm-*', + term: 'client.geo.region_iso_code', + metrics: [{ type: AGG_TYPE.AVG, field: 'transaction.duration.us' }], + indexPatternId: APM_STATIC_INDEX_PATTERN_ID, + }, + }, + ], + sourceDescriptor: { + type: 'EMS_FILE', + id: 'administrative_regions_lvl2', + tooltipProperties: ['region_iso_code', REGION_NAME], + }, + style: getLayerStyle(TRANSACTION_DURATION_REGION), + id: '0e936d41-8765-41c9-97f0-05e166391366', + label: null, + minZoom: 3, + maxZoom: 24, + alpha: 0.75, + visible: true, + type: 'VECTOR', + }; + return [ + baseLayer, + pageLoadDurationByCountryLayer, + pageLoadDurationByAdminRegionLayer, + ]; +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx new file mode 100644 index 000000000000..07b40addedec --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/MapToolTip.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState } from 'react'; +import { + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, + EuiOutsideClickDetector, + EuiPopoverTitle, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { + COUNTRY_NAME, + REGION_NAME, + TRANSACTION_DURATION_COUNTRY, + TRANSACTION_DURATION_REGION, +} from './LayerList'; +import { RenderTooltipContentParams } from '../../../../../../maps/public'; +import { I18LABELS } from '../translations'; + +type MapToolTipProps = Partial; + +const DescriptionItem = styled(EuiDescriptionListDescription)` + &&& { + width: 25%; + } +`; + +const TitleItem = styled(EuiDescriptionListTitle)` + &&& { + width: 75%; + } +`; + +function MapToolTipComponent({ + closeTooltip, + features = [], + loadFeatureProperties, +}: MapToolTipProps) { + const { id: featureId, layerId } = features[0] ?? {}; + + const [regionName, setRegionName] = useState(featureId as string); + const [pageLoadDuration, setPageLoadDuration] = useState(''); + + const formatPageLoadValue = (val: number) => { + const valInMs = val / 1000; + if (valInMs > 1000) { + return (valInMs / 1000).toFixed(2) + ' sec'; + } + + return (valInMs / 1000).toFixed(0) + ' ms'; + }; + + useEffect(() => { + const loadRegionInfo = async () => { + if (loadFeatureProperties) { + const items = await loadFeatureProperties({ layerId, featureId }); + items.forEach((item) => { + if ( + item.getPropertyKey() === COUNTRY_NAME || + item.getPropertyKey() === REGION_NAME + ) { + setRegionName(item.getRawValue() as string); + } + if ( + item.getPropertyKey() === TRANSACTION_DURATION_REGION || + item.getPropertyKey() === TRANSACTION_DURATION_COUNTRY + ) { + setPageLoadDuration( + formatPageLoadValue(+(item.getRawValue() as string)) + ); + } + }); + } + }; + loadRegionInfo(); + }); + + return ( + { + if (closeTooltip != null) { + closeTooltip(); + } + }} + > + <> + {regionName} + + + {I18LABELS.avgPageLoadDuration} + + {pageLoadDuration} + + + + ); +} + +export const MapToolTip = React.memo(MapToolTipComponent); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx new file mode 100644 index 000000000000..023f5d61a964 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__stories__/MapTooltip.stories.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { storiesOf } from '@storybook/react'; +import React from 'react'; +import { EuiThemeProvider } from '../../../../../../../observability/public'; +import { MapToolTip } from '../MapToolTip'; +import { COUNTRY_NAME, TRANSACTION_DURATION_COUNTRY } from '../LayerList'; + +storiesOf('app/RumDashboard/VisitorsRegionMap', module) + .addDecorator((storyFn) => {storyFn()}) + .add( + 'Tooltip', + () => { + const loadFeatureProps = async () => { + return [ + { + getPropertyKey: () => COUNTRY_NAME, + getRawValue: () => 'United States', + }, + { + getPropertyKey: () => TRANSACTION_DURATION_COUNTRY, + getRawValue: () => 2434353, + }, + ]; + }; + return ( + + ); + }, + { + info: { + propTables: false, + source: false, + }, + } + ); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx new file mode 100644 index 000000000000..790be81bb65c --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/EmbeddedMap.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { render } from 'enzyme'; +import React from 'react'; + +import { EmbeddedMap } from '../EmbeddedMap'; +import { KibanaContextProvider } from '../../../../../../../security_solution/public/common/lib/kibana'; +import { embeddablePluginMock } from '../../../../../../../../../src/plugins/embeddable/public/mocks'; + +describe('Embedded Map', () => { + test('it renders', () => { + const [core] = mockCore(); + + const wrapper = render( + + + + ); + + expect(wrapper).toMatchSnapshot(); + }); +}); + +const mockEmbeddable = embeddablePluginMock.createStartContract(); + +mockEmbeddable.getEmbeddableFactory = jest.fn().mockImplementation(() => ({ + create: () => ({ + reload: jest.fn(), + setRenderTooltipContent: jest.fn(), + setLayerList: jest.fn(), + }), +})); + +const mockCore: () => [any] = () => { + const core = { + embeddable: mockEmbeddable, + }; + + return [core]; +}; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/LayerList.test.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/LayerList.test.ts new file mode 100644 index 000000000000..eb149ee2a132 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/LayerList.test.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 { mockLayerList } from './__mocks__/regions_layer.mock'; +import { getLayerList } from '../LayerList'; + +describe('LayerList', () => { + describe('getLayerList', () => { + test('it returns the region layer', () => { + const layerList = getLayerList(); + expect(layerList).toStrictEqual(mockLayerList); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.tsx new file mode 100644 index 000000000000..cbaae40b0436 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/MapToolTip.test.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 { render, shallow } from 'enzyme'; +import React from 'react'; + +import { MapToolTip } from '../MapToolTip'; + +describe('Map Tooltip', () => { + test('it shallow renders', () => { + const wrapper = shallow(); + + expect(wrapper).toMatchSnapshot(); + }); + + test('it renders', () => { + const wrapper = render(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__mocks__/regions_layer.mock.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__mocks__/regions_layer.mock.ts new file mode 100644 index 000000000000..c45f8b27d7d3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__mocks__/regions_layer.mock.ts @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 mockLayerList = [ + { + sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true }, + id: 'b7af286d-2580-4f47-be93-9653d594ce7e', + label: null, + minZoom: 0, + maxZoom: 24, + alpha: 1, + visible: true, + style: { type: 'TILE' }, + type: 'VECTOR_TILE', + }, + { + joins: [ + { + leftField: 'iso2', + right: { + type: 'ES_TERM_SOURCE', + id: '3657625d-17b0-41ef-99ba-3a2b2938655c', + indexPatternTitle: 'apm-*', + term: 'client.geo.country_iso_code', + metrics: [ + { + type: 'avg', + field: 'transaction.duration.us', + label: 'Page load duration', + }, + ], + indexPatternId: 'apm_static_index_pattern_id', + applyGlobalQuery: true, + }, + }, + ], + sourceDescriptor: { + type: 'EMS_FILE', + id: 'world_countries', + tooltipProperties: ['name'], + applyGlobalQuery: true, + }, + style: { + type: 'VECTOR', + properties: { + icon: { type: 'STATIC', options: { value: 'marker' } }, + fillColor: { + type: 'DYNAMIC', + options: { + color: 'Blue to Red', + colorCategory: 'palette_0', + fieldMetaOptions: { isEnabled: true, sigma: 3 }, + type: 'ORDINAL', + field: { + name: + '__kbnjoin__avg_of_transaction.duration.us__3657625d-17b0-41ef-99ba-3a2b2938655c', + origin: 'join', + }, + useCustomColorRamp: false, + }, + }, + lineColor: { + type: 'DYNAMIC', + options: { color: '#3d3d3d', fieldMetaOptions: { isEnabled: true } }, + }, + lineWidth: { type: 'STATIC', options: { size: 1 } }, + iconSize: { type: 'STATIC', options: { size: 6 } }, + iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, + labelText: { type: 'STATIC', options: { value: '' } }, + labelColor: { type: 'STATIC', options: { color: '#000000' } }, + labelSize: { type: 'STATIC', options: { size: 14 } }, + labelBorderColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, + symbolizeAs: { options: { value: 'circle' } }, + labelBorderSize: { options: { size: 'SMALL' } }, + }, + isTimeAware: true, + }, + id: 'e8d1d974-eed8-462f-be2c-f0004b7619b2', + label: null, + minZoom: 0, + maxZoom: 24, + alpha: 0.75, + visible: true, + type: 'VECTOR', + }, + { + joins: [ + { + leftField: 'region_iso_code', + right: { + type: 'ES_TERM_SOURCE', + id: 'e62a1b9c-d7ff-4fd4-a0f6-0fdc44bb9e41', + indexPatternTitle: 'apm-*', + term: 'client.geo.region_iso_code', + metrics: [{ type: 'avg', field: 'transaction.duration.us' }], + indexPatternId: 'apm_static_index_pattern_id', + }, + }, + ], + sourceDescriptor: { + type: 'EMS_FILE', + id: 'administrative_regions_lvl2', + tooltipProperties: ['region_iso_code', 'region_name'], + }, + style: { + type: 'VECTOR', + properties: { + icon: { type: 'STATIC', options: { value: 'marker' } }, + fillColor: { + type: 'DYNAMIC', + options: { + color: 'Blue to Red', + colorCategory: 'palette_0', + fieldMetaOptions: { isEnabled: true, sigma: 3 }, + type: 'ORDINAL', + field: { + name: + '__kbnjoin__avg_of_transaction.duration.us__e62a1b9c-d7ff-4fd4-a0f6-0fdc44bb9e41', + origin: 'join', + }, + useCustomColorRamp: false, + }, + }, + lineColor: { + type: 'DYNAMIC', + options: { color: '#3d3d3d', fieldMetaOptions: { isEnabled: true } }, + }, + lineWidth: { type: 'STATIC', options: { size: 1 } }, + iconSize: { type: 'STATIC', options: { size: 6 } }, + iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, + labelText: { type: 'STATIC', options: { value: '' } }, + labelColor: { type: 'STATIC', options: { color: '#000000' } }, + labelSize: { type: 'STATIC', options: { size: 14 } }, + labelBorderColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, + symbolizeAs: { options: { value: 'circle' } }, + labelBorderSize: { options: { size: 'SMALL' } }, + }, + isTimeAware: true, + }, + id: '0e936d41-8765-41c9-97f0-05e166391366', + label: null, + minZoom: 3, + maxZoom: 24, + alpha: 0.75, + visible: true, + type: 'VECTOR', + }, +]; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/EmbeddedMap.test.tsx.snap b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/EmbeddedMap.test.tsx.snap new file mode 100644 index 000000000000..67f79c9fc747 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/EmbeddedMap.test.tsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Embedded Map it renders 1`] = ` +.c0 { + z-index: auto; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + height: 100%; + position: relative; +} + +.c0 .embPanel__content { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1 1 100%; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + z-index: 1; + min-height: 0; +} + +.c0.c0.c0 .mapboxgl-canvas { + -webkit-animation: none !important; + animation: none !important; +} + +
+
+
+`; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/MapToolTip.test.tsx.snap b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/MapToolTip.test.tsx.snap new file mode 100644 index 000000000000..860727a7a0f8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/__tests__/__snapshots__/MapToolTip.test.tsx.snap @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Map Tooltip it renders 1`] = ` +Array [ +
, + .c1.c1.c1 { + width: 25%; +} + +.c0.c0.c0 { + width: 75%; +} + +
+
+ Average page load duration +
+
+
, +] +`; + +exports[`Map Tooltip it shallow renders 1`] = ` + + + + + Average page load duration + + + + +`; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/index.tsx new file mode 100644 index 000000000000..44bfe5abbaca --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/index.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 { EuiTitle, EuiSpacer } from '@elastic/eui'; +import { EmbeddedMap } from './EmbeddedMap'; +import { I18LABELS } from '../translations'; + +export function VisitorBreakdownMap() { + return ( + <> + +

{I18LABELS.pageLoadDurationByRegion}

+
+ +
+ +
+ + ); +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useMapFilters.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useMapFilters.ts new file mode 100644 index 000000000000..357e04c538e6 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/VisitorBreakdownMap/useMapFilters.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { FieldFilter as Filter } from '../../../../../../../../src/plugins/data/common'; +import { + CLIENT_GEO_COUNTRY_ISO_CODE, + SERVICE_NAME, + USER_AGENT_DEVICE, + USER_AGENT_NAME, + USER_AGENT_OS, +} from '../../../../../common/elasticsearch_fieldnames'; + +import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../../../../src/plugins/apm_oss/public'; + +const getMatchFilter = (field: string, value: string): Filter => { + return { + meta: { + index: APM_STATIC_INDEX_PATTERN_ID, + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: field, + params: { query: value }, + }, + query: { match_phrase: { [field]: value } }, + }; +}; + +const getMultiMatchFilter = (field: string, values: string[]): Filter => { + return { + meta: { + index: APM_STATIC_INDEX_PATTERN_ID, + type: 'phrases', + key: field, + value: values.join(', '), + params: values, + alias: null, + negate: false, + disabled: false, + }, + query: { + bool: { + should: values.map((value) => ({ match_phrase: { [field]: value } })), + minimum_should_match: 1, + }, + }, + }; +}; +export const useMapFilters = (): Filter[] => { + const { urlParams, uiFilters } = useUrlParams(); + + const { serviceName } = urlParams; + + const { browser, device, os, location } = uiFilters; + + const [mapFilters, setMapFilters] = useState([]); + + const existFilter: Filter = { + meta: { + index: APM_STATIC_INDEX_PATTERN_ID, + alias: null, + negate: false, + disabled: false, + type: 'exists', + key: 'transaction.marks.navigationTiming.fetchStart', + value: 'exists', + }, + exists: { + field: 'transaction.marks.navigationTiming.fetchStart', + }, + }; + + useEffect(() => { + const filters = [existFilter]; + if (serviceName) { + filters.push(getMatchFilter(SERVICE_NAME, serviceName)); + } + if (browser) { + filters.push(getMultiMatchFilter(USER_AGENT_NAME, browser)); + } + if (device) { + filters.push(getMultiMatchFilter(USER_AGENT_DEVICE, device)); + } + if (os) { + filters.push(getMultiMatchFilter(USER_AGENT_OS, os)); + } + if (location) { + filters.push(getMultiMatchFilter(CLIENT_GEO_COUNTRY_ISO_CODE, location)); + } + + setMapFilters(filters); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [serviceName, browser, device, os, location]); + + return mapFilters; +}; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/index.tsx index 8d1959ec14d1..fa0551252b6a 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/index.tsx @@ -58,7 +58,7 @@ export function RumOverview() { return ( <> - + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts index 660ed5a92a0e..ec135168729b 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts @@ -64,6 +64,18 @@ export const I18LABELS = { defaultMessage: 'Operating system', } ), + avgPageLoadDuration: i18n.translate( + 'xpack.apm.rum.visitorBreakdownMap.avgPageLoadDuration', + { + defaultMessage: 'Average page load duration', + } + ), + pageLoadDurationByRegion: i18n.translate( + 'xpack.apm.rum.visitorBreakdownMap.pageLoadDurationByRegion', + { + defaultMessage: 'Page load duration by region', + } + ), }; export const VisitorBreakdownLabel = i18n.translate( diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx index b3d19e1aab2c..5699d0b56219 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/AnomalyDetection.tsx @@ -18,10 +18,13 @@ import { useTheme } from '../../../../hooks/useTheme'; import { fontSize, px } from '../../../../style/variables'; import { asInteger, asDuration } from '../../../../utils/formatters'; import { MLJobLink } from '../../../shared/Links/MachineLearningLinks/MLJobLink'; -import { getSeverityColor, popoverWidth } from '../cytoscapeOptions'; +import { popoverWidth } from '../cytoscapeOptions'; import { TRANSACTION_REQUEST } from '../../../../../common/transaction_types'; -import { ServiceAnomalyStats } from '../../../../../common/anomaly_detection'; -import { getSeverity } from './getSeverity'; +import { + getSeverity, + getSeverityColor, + ServiceAnomalyStats, +} from '../../../../../common/anomaly_detection'; const HealthStatusTitle = styled(EuiTitle)` display: inline; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.ts deleted file mode 100644 index f4eb2033e923..000000000000 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/getSeverity.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. - */ - -export enum severity { - critical = 'critical', - major = 'major', - minor = 'minor', - warning = 'warning', -} - -// TODO: Replace with `getSeverity` from: -// https://github.com/elastic/kibana/blob/0f964f66916480f2de1f4b633e5afafc08cf62a0/x-pack/plugins/ml/common/util/anomaly_utils.ts#L129 -export function getSeverity(score?: number) { - if (typeof score !== 'number') { - return undefined; - } else if (score < 25) { - return severity.warning; - } else if (score >= 25 && score < 50) { - return severity.minor; - } else if (score >= 50 && score < 75) { - return severity.major; - } else if (score >= 75) { - return severity.critical; - } else { - return undefined; - } -} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 9fedcc70bbbc..1ac7157cc2aa 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -11,25 +11,15 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { EuiTheme } from '../../../../../observability/public'; import { defaultIcon, iconForNode } from './icons'; -import { ServiceAnomalyStats } from '../../../../common/anomaly_detection'; -import { severity, getSeverity } from './Popover/getSeverity'; +import { + getSeverity, + getSeverityColor, + ServiceAnomalyStats, + Severity, +} from '../../../../common/anomaly_detection'; export const popoverWidth = 280; -export function getSeverityColor(theme: EuiTheme, nodeSeverity?: string) { - switch (nodeSeverity) { - case severity.warning: - return theme.eui.euiColorVis0; - case severity.minor: - case severity.major: - return theme.eui.euiColorVis5; - case severity.critical: - return theme.eui.euiColorVis9; - default: - return; - } -} - function getNodeSeverity(el: cytoscape.NodeSingular) { const serviceAnomalyStats: ServiceAnomalyStats | undefined = el.data( 'serviceAnomalyStats' @@ -60,7 +50,7 @@ const getBorderStyle: cytoscape.Css.MapperFunction< cytoscape.Css.LineStyle > = (el: cytoscape.NodeSingular) => { const nodeSeverity = getNodeSeverity(el); - if (nodeSeverity === severity.critical) { + if (nodeSeverity === Severity.critical) { return 'double'; } else { return 'solid'; @@ -70,9 +60,9 @@ const getBorderStyle: cytoscape.Css.MapperFunction< function getBorderWidth(el: cytoscape.NodeSingular) { const nodeSeverity = getNodeSeverity(el); - if (nodeSeverity === severity.minor || nodeSeverity === severity.major) { + if (nodeSeverity === Severity.minor || nodeSeverity === Severity.major) { return 4; - } else if (nodeSeverity === severity.critical) { + } else if (nodeSeverity === Severity.critical) { return 8; } else { return 4; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts index 2f4cc0d39d71..c85cf85d3870 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts @@ -5,7 +5,6 @@ */ import cytoscape from 'cytoscape'; -import { getNormalizedAgentName } from '../../../../common/agent_name'; import { AGENT_NAME, SPAN_SUBTYPE, @@ -13,29 +12,22 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import awsIcon from './icons/aws.svg'; import cassandraIcon from './icons/cassandra.svg'; -import darkIcon from './icons/dark.svg'; import databaseIcon from './icons/database.svg'; import defaultIconImport from './icons/default.svg'; import documentsIcon from './icons/documents.svg'; -import dotNetIcon from './icons/dot-net.svg'; import elasticsearchIcon from './icons/elasticsearch.svg'; import globeIcon from './icons/globe.svg'; -import goIcon from './icons/go.svg'; import graphqlIcon from './icons/graphql.svg'; import grpcIcon from './icons/grpc.svg'; import handlebarsIcon from './icons/handlebars.svg'; -import javaIcon from './icons/java.svg'; import kafkaIcon from './icons/kafka.svg'; import mongodbIcon from './icons/mongodb.svg'; import mysqlIcon from './icons/mysql.svg'; -import nodeJsIcon from './icons/nodejs.svg'; -import phpIcon from './icons/php.svg'; import postgresqlIcon from './icons/postgresql.svg'; -import pythonIcon from './icons/python.svg'; import redisIcon from './icons/redis.svg'; -import rubyIcon from './icons/ruby.svg'; -import rumJsIcon from './icons/rumjs.svg'; import websocketIcon from './icons/websocket.svg'; +import javaIcon from '../../shared/AgentIcon/icons/java.svg'; +import { getAgentIcon } from '../../shared/AgentIcon/get_agent_icon'; export const defaultIcon = defaultIconImport; @@ -74,23 +66,6 @@ const typeIcons: { [key: string]: { [key: string]: string } } = { }, }; -const agentIcons: { [key: string]: string } = { - dark: darkIcon, - dotnet: dotNetIcon, - go: goIcon, - java: javaIcon, - 'js-base': rumJsIcon, - nodejs: nodeJsIcon, - php: phpIcon, - python: pythonIcon, - ruby: rubyIcon, -}; - -function getAgentIcon(agentName?: string) { - const normalizedAgentName = getNormalizedAgentName(agentName); - return normalizedAgentName && agentIcons[normalizedAgentName]; -} - function getSpanIcon(type?: string, subtype?: string) { if (!type) { return; diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dark.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dark.svg deleted file mode 100644 index 9ae4b31c1a0d..000000000000 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx index 83fab95bc91c..cb5a57e9ab9f 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useTheme } from '../../../hooks/useTheme'; import { invalidLicenseMessage, - isValidPlatinumLicense, + isActivePlatinumLicense, } from '../../../../common/service_map'; import { useFetcher } from '../../../hooks/useFetcher'; import { useLicense } from '../../../hooks/useLicense'; @@ -36,7 +36,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { const { data = { elements: [] } } = useFetcher(() => { // When we don't have a license or a valid license, don't make the request. - if (!license || !isValidPlatinumLicense(license)) { + if (!license || !isActivePlatinumLicense(license)) { return; } @@ -66,7 +66,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { return null; } - return isValidPlatinumLicense(license) ? ( + return isActivePlatinumLicense(license) ? (
+ {getSeverityLabel(severity)} + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/MLCallout.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/MLCallout.tsx new file mode 100644 index 000000000000..dd632db0f15f --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/MLCallout.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EuiButton } from '@elastic/eui'; +import { EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGrid } from '@elastic/eui'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { APMLink } from '../../../shared/Links/apm/APMLink'; + +export function MLCallout({ onDismiss }: { onDismiss: () => void }) { + return ( + +

+ {i18n.translate('xpack.apm.serviceOverview.mlNudgeMessage.content', { + defaultMessage: `Our integration with ML anomaly detection will enable you to see your services' health status`, + })} +

+ + + + + {i18n.translate( + 'xpack.apm.serviceOverview.mlNudgeMessage.learnMoreButton', + { + defaultMessage: `Learn more`, + } + )} + + + + + onDismiss()}> + {i18n.translate( + 'xpack.apm.serviceOverview.mlNudgeMessage.dismissButton', + { + defaultMessage: `Dismiss message`, + } + )} + + + +
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/ServiceListMetric.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/ServiceListMetric.tsx new file mode 100644 index 000000000000..c94c94d4a0b7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/ServiceListMetric.tsx @@ -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 { EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; + +import React from 'react'; +import { useTheme } from '../../../../hooks/useTheme'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { getEmptySeries } from '../../../shared/charts/CustomPlot/getEmptySeries'; +import { SparkPlot } from '../../../shared/charts/SparkPlot'; + +export function ServiceListMetric({ + color, + series, + valueLabel, +}: { + color: 'euiColorVis1' | 'euiColorVis0' | 'euiColorVis7'; + series?: Array<{ x: number; y: number | null }>; + valueLabel: React.ReactNode; +}) { + const theme = useTheme(); + + const { + urlParams: { start, end }, + } = useUrlParams(); + + const colorValue = theme.eui[color]; + + return ( + + + + + + {valueLabel} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js index 927779b571fd..519d74827097 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js @@ -15,34 +15,62 @@ describe('ServiceOverview -> List', () => { mockMoment(); }); - it('should render empty state', () => { + it('renders empty state', () => { const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); - it('should render with data', () => { + it('renders with data', () => { const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); - it('should render columns correctly', () => { + it('renders columns correctly', () => { const service = { serviceName: 'opbeans-python', agentName: 'python', - transactionsPerMinute: 86.93333333333334, - errorsPerMinute: 12.6, - avgResponseTime: 91535.42944785276, + transactionsPerMinute: { + value: 86.93333333333334, + timeseries: [], + }, + errorsPerMinute: { + value: 12.6, + timeseries: [], + }, + avgResponseTime: { + value: 91535.42944785276, + timeseries: [], + }, environments: ['test'], }; const renderedColumns = SERVICE_COLUMNS.map((c) => c.render(service[c.field], service) ); + expect(renderedColumns[0]).toMatchSnapshot(); - expect(renderedColumns.slice(2)).toEqual([ - 'python', - '92 ms', - '86.9 tpm', - '12.6 err.', - ]); + }); + + describe('without ML data', () => { + it('does not render health column', () => { + const wrapper = shallow( + + ); + + const columns = wrapper.props().columns; + + expect(columns[0].field).not.toBe('severity'); + }); + }); + + describe('with ML data', () => { + it('renders health column', () => { + const wrapper = shallow( + + ); + + const columns = wrapper.props().columns; + + expect(columns[0].field).toBe('severity'); + }); }); }); diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap index 146f6f58031b..da3f6ae89940 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap @@ -1,21 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ServiceOverview -> List should render columns correctly 1`] = ` - - - opbeans-python - - -`; +exports[`ServiceOverview -> List renders columns correctly 1`] = ``; -exports[`ServiceOverview -> List should render empty state 1`] = ` +exports[`ServiceOverview -> List renders empty state 1`] = ` List should render empty state 1`] = ` "name": "Environment", "render": [Function], "sortable": true, - "width": "20%", - }, - Object { - "field": "agentName", - "name": "Agent", - "render": [Function], - "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "avgResponseTime", "name": "Avg. response time", "render": [Function], "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "transactionsPerMinute", "name": "Trans. per minute", "render": [Function], "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "errorsPerMinute", - "name": "Errors per minute", + "name": "Error rate %", "render": [Function], "sortable": true, + "width": "160px", }, ] } initialPageSize={50} - initialSortField="serviceName" + initialSortDirection="desc" + initialSortField="severity" items={Array []} + sortFn={[Function]} /> `; -exports[`ServiceOverview -> List should render with data 1`] = ` +exports[`ServiceOverview -> List renders with data 1`] = ` List should render with data 1`] = ` "name": "Environment", "render": [Function], "sortable": true, - "width": "20%", - }, - Object { - "field": "agentName", - "name": "Agent", - "render": [Function], - "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "avgResponseTime", "name": "Avg. response time", "render": [Function], "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "transactionsPerMinute", "name": "Trans. per minute", "render": [Function], "sortable": true, + "width": "160px", }, Object { + "align": "left", "dataType": "number", "field": "errorsPerMinute", - "name": "Errors per minute", + "name": "Error rate %", "render": [Function], "sortable": true, + "width": "160px", }, ] } initialPageSize={50} - initialSortField="serviceName" + initialSortDirection="desc" + initialSortField="severity" items={ Array [ Object { @@ -125,19 +115,35 @@ exports[`ServiceOverview -> List should render with data 1`] = ` "environments": Array [ "test", ], - "errorsPerMinute": 46.06666666666667, + "errorsPerMinute": Object { + "timeseries": Array [], + "value": 46.06666666666667, + }, "serviceName": "opbeans-node", - "transactionsPerMinute": 0, + "transactionsPerMinute": Object { + "timeseries": Array [], + "value": 0, + }, }, Object { "agentName": "python", - "avgResponseTime": 91535.42944785276, + "avgResponseTime": Object { + "timeseries": Array [], + "value": 91535.42944785276, + }, "environments": Array [], - "errorsPerMinute": 12.6, + "errorsPerMinute": Object { + "timeseries": Array [], + "value": 12.6, + }, "serviceName": "opbeans-python", - "transactionsPerMinute": 86.93333333333334, + "transactionsPerMinute": Object { + "timeseries": Array [], + "value": 86.93333333333334, + }, }, ] } + sortFn={[Function]} /> `; diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json index 2379d27407e0..7f24ad8b0d30 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json @@ -3,17 +3,34 @@ { "serviceName": "opbeans-node", "agentName": "nodejs", - "transactionsPerMinute": 0, - "errorsPerMinute": 46.06666666666667, + "transactionsPerMinute": { + "value": 0, + "timeseries": [] + }, + "errorsPerMinute": { + "value": 46.06666666666667, + "timeseries": [] + }, "avgResponseTime": null, - "environments": ["test"] + "environments": [ + "test" + ] }, { "serviceName": "opbeans-python", "agentName": "python", - "transactionsPerMinute": 86.93333333333334, - "errorsPerMinute": 12.6, - "avgResponseTime": 91535.42944785276, + "transactionsPerMinute": { + "value": 86.93333333333334, + "timeseries": [] + }, + "errorsPerMinute": { + "value": 12.6, + "timeseries": [] + }, + "avgResponseTime": { + "value": 91535.42944785276, + "timeseries": [] + }, "environments": [] } ] diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx index 90cc9af45273..ce256137481c 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx @@ -4,24 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiToolTip } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; +import { ValuesType } from 'utility-types'; +import { orderBy } from 'lodash'; +import { asPercent } from '../../../../../common/utils/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; -import { fontSizes, truncate } from '../../../../style/variables'; +import { fontSizes, px, truncate, unit } from '../../../../style/variables'; import { asDecimal, asMillisecondDuration } from '../../../../utils/formatters'; -import { ManagedTable } from '../../../shared/ManagedTable'; +import { ManagedTable, ITableColumn } from '../../../shared/ManagedTable'; import { EnvironmentBadge } from '../../../shared/EnvironmentBadge'; import { TransactionOverviewLink } from '../../../shared/Links/apm/TransactionOverviewLink'; +import { AgentIcon } from '../../../shared/AgentIcon'; +import { Severity } from '../../../../../common/anomaly_detection'; +import { HealthBadge } from './HealthBadge'; +import { ServiceListMetric } from './ServiceListMetric'; interface Props { items: ServiceListAPIResponse['items']; noItemsMessage?: React.ReactNode; + displayHealthStatus: boolean; } +type ServiceListItem = ValuesType; + function formatNumber(value: number) { if (value === 0) { return '0'; @@ -41,7 +51,18 @@ const AppLink = styled(TransactionOverviewLink)` ${truncate('100%')}; `; -export const SERVICE_COLUMNS = [ +export const SERVICE_COLUMNS: Array> = [ + { + field: 'severity', + name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { + defaultMessage: 'Health', + }), + width: px(unit * 6), + sortable: true, + render: (_, { severity }) => { + return ; + }, + }, { field: 'serviceName', name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', { @@ -49,9 +70,24 @@ export const SERVICE_COLUMNS = [ }), width: '40%', sortable: true, - render: (serviceName: string) => ( - - {formatString(serviceName)} + render: (_, { serviceName, agentName }) => ( + + + {agentName && ( + + + + )} + + + {formatString(serviceName)} + + + ), }, @@ -60,20 +96,12 @@ export const SERVICE_COLUMNS = [ name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { defaultMessage: 'Environment', }), - width: '20%', + width: px(unit * 10), sortable: true, - render: (environments: string[]) => ( - + render: (_, { environments }) => ( + ), }, - { - field: 'agentName', - name: i18n.translate('xpack.apm.servicesTable.agentColumnLabel', { - defaultMessage: 'Agent', - }), - sortable: true, - render: (agentName: string) => formatString(agentName), - }, { field: 'avgResponseTime', name: i18n.translate('xpack.apm.servicesTable.avgResponseTimeColumnLabel', { @@ -81,7 +109,15 @@ export const SERVICE_COLUMNS = [ }), sortable: true, dataType: 'number', - render: (time: number) => asMillisecondDuration(time), + render: (_, { avgResponseTime }) => ( + + ), + align: 'left', + width: px(unit * 10), }, { field: 'transactionsPerMinute', @@ -93,39 +129,107 @@ export const SERVICE_COLUMNS = [ ), sortable: true, dataType: 'number', - render: (value: number) => - `${formatNumber(value)} ${i18n.translate( - 'xpack.apm.servicesTable.transactionsPerMinuteUnitLabel', - { - defaultMessage: 'tpm', - } - )}`, + render: (_, { transactionsPerMinute }) => ( + + ), + align: 'left', + width: px(unit * 10), }, { field: 'errorsPerMinute', - name: i18n.translate('xpack.apm.servicesTable.errorsPerMinuteColumnLabel', { - defaultMessage: 'Errors per minute', + name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { + defaultMessage: 'Error rate %', }), sortable: true, dataType: 'number', - render: (value: number) => - `${formatNumber(value)} ${i18n.translate( - 'xpack.apm.servicesTable.errorsPerMinuteUnitLabel', - { - defaultMessage: 'err.', - } - )}`, + render: (_, { transactionErrorRate }) => { + const value = transactionErrorRate?.value; + + const valueLabel = + value !== null && value !== undefined ? asPercent(value, 1) : ''; + + return ( + + ); + }, + align: 'left', + width: px(unit * 10), }, ]; -export function ServiceList({ items, noItemsMessage }: Props) { +const SEVERITY_ORDER = [ + Severity.warning, + Severity.minor, + Severity.major, + Severity.critical, +]; + +export function ServiceList({ + items, + displayHealthStatus, + noItemsMessage, +}: Props) { + const columns = displayHealthStatus + ? SERVICE_COLUMNS + : SERVICE_COLUMNS.filter((column) => column.field !== 'severity'); + return ( { + // For severity, sort items by severity first, then by TPM + + return sortField === 'severity' + ? orderBy( + itemsToSort, + [ + (item) => { + return item.severity + ? SEVERITY_ORDER.indexOf(item.severity) + : -1; + }, + (item) => item.transactionsPerMinute?.value ?? 0, + ], + [sortDirection, sortDirection] + ) + : orderBy( + itemsToSort, + (item) => { + switch (sortField) { + case 'avgResponseTime': + return item.avgResponseTime?.value ?? 0; + case 'transactionsPerMinute': + return item.transactionsPerMinute?.value ?? 0; + case 'transactionErrorRate': + return item.transactionErrorRate?.value ?? 0; + + default: + return item[sortField as keyof typeof item]; + } + }, + sortDirection + ); + }} /> ); } diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx index d9c5ff5130df..8eeff018ad03 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx @@ -8,6 +8,7 @@ import { render, wait, waitForElement } from '@testing-library/react'; import { CoreStart } from 'kibana/public'; import React, { FunctionComponent, ReactChild } from 'react'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; +import { merge } from 'lodash'; import { ServiceOverview } from '..'; import { ApmPluginContextValue } from '../../../../context/ApmPluginContext'; import { @@ -17,35 +18,38 @@ import { import { FETCH_STATUS } from '../../../../hooks/useFetcher'; import * as useLocalUIFilters from '../../../../hooks/useLocalUIFilters'; import * as urlParamsHooks from '../../../../hooks/useUrlParams'; +import * as useAnomalyDetectionJobs from '../../../../hooks/useAnomalyDetectionJobs'; import { SessionStorageMock } from '../../../../services/__test__/SessionStorageMock'; +import { EuiThemeProvider } from '../../../../../../../legacy/common/eui_styled_components'; const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiStats: () => {} }, } as Partial); +const addWarning = jest.fn(); +const httpGet = jest.fn(); + function wrapper({ children }: { children: ReactChild }) { + const mockPluginContext = (merge({}, mockApmPluginContextValue, { + core: { + http: { + get: httpGet, + }, + notifications: { + toasts: { + addWarning, + }, + }, + }, + }) as unknown) as ApmPluginContextValue; + return ( - - {children} - + + + {children} + + ); } @@ -56,9 +60,6 @@ function renderServiceOverview() { }); } -const addWarning = jest.fn(); -const httpGet = jest.fn(); - describe('Service Overview -> View', () => { beforeEach(() => { // @ts-expect-error @@ -80,6 +81,17 @@ describe('Service Overview -> View', () => { clearValues: () => null, status: FETCH_STATUS.SUCCESS, }); + + jest + .spyOn(useAnomalyDetectionJobs, 'useAnomalyDetectionJobs') + .mockReturnValue({ + status: FETCH_STATUS.SUCCESS, + data: { + jobs: [], + hasLegacyJobs: false, + }, + refetch: () => undefined, + }); }); afterEach(() => { @@ -99,6 +111,7 @@ describe('Service Overview -> View', () => { errorsPerMinute: 200, avgResponseTime: 300, environments: ['test', 'dev'], + severity: 1, }, { serviceName: 'My Go Service', @@ -107,6 +120,7 @@ describe('Service Overview -> View', () => { errorsPerMinute: 500, avgResponseTime: 600, environments: [], + severity: 10, }, ], }); @@ -195,4 +209,57 @@ describe('Service Overview -> View', () => { expect(addWarning).not.toHaveBeenCalled(); }); }); + + describe('when ML data is not found', () => { + it('does not render the health column', async () => { + httpGet.mockResolvedValueOnce({ + hasLegacyData: false, + hasHistoricalData: true, + items: [ + { + serviceName: 'My Python Service', + agentName: 'python', + transactionsPerMinute: 100, + errorsPerMinute: 200, + avgResponseTime: 300, + environments: ['test', 'dev'], + }, + ], + }); + + const { queryByText } = renderServiceOverview(); + + // wait for requests to be made + await wait(() => expect(httpGet).toHaveBeenCalledTimes(1)); + + expect(queryByText('Health')).toBeNull(); + }); + }); + + describe('when ML data is found', () => { + it('renders the health column', async () => { + httpGet.mockResolvedValueOnce({ + hasLegacyData: false, + hasHistoricalData: true, + items: [ + { + serviceName: 'My Python Service', + agentName: 'python', + transactionsPerMinute: 100, + errorsPerMinute: 200, + avgResponseTime: 300, + environments: ['test', 'dev'], + severity: 1, + }, + ], + }); + + const { queryAllByText } = renderServiceOverview(); + + // wait for requests to be made + await wait(() => expect(httpGet).toHaveBeenCalledTimes(1)); + + expect(queryAllByText('Health').length).toBeGreaterThan(1); + }); + }); }); diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index 6d447887627b..b56f7d682027 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -7,7 +7,7 @@ NodeList [ >
- Name + Health
- - My Go Service - + + Unknown + +
- Environment + Name
+ > + + + +
- Agent + Environment
- go + + + + test + + + + + + + dev + + +
- 0.6 ms +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+ 0 ms +
+
- 400.0 tpm +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+ 0 tpm +
+
- Errors per minute + Error rate %
- 500.0 err. +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+
, @@ -247,87 +423,91 @@ NodeList [ >
- Name + Health
- - My Python Service - + + Unknown + +
- Environment + Name
- - - test - - - - - - +
+
- dev - - + + My Go Service + +
+
- Agent + Environment
- python -
+ />
- 0.3 ms +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+ 0 ms +
+
- 100.0 tpm +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+ 0 tpm +
+
- Errors per minute + Error rate %
- 200.0 err. +
+
+
+
+
+
+
+
+
+ N/A +
+
+
+
+
+
+
, diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx index 7146e471a7f8..d9d2cffb6762 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useMemo } from 'react'; import url from 'url'; import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; -import { useFetcher } from '../../../hooks/useFetcher'; +import { useFetcher, FETCH_STATUS } from '../../../hooks/useFetcher'; import { NoServicesMessage } from './NoServicesMessage'; import { ServiceList } from './ServiceList'; import { useUrlParams } from '../../../hooks/useUrlParams'; @@ -18,8 +18,11 @@ import { useTrackPageview } from '../../../../../observability/public'; import { Projection } from '../../../../common/projections'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; +import { MLCallout } from './ServiceList/MLCallout'; +import { useLocalStorage } from '../../../hooks/useLocalStorage'; +import { useAnomalyDetectionJobs } from '../../../hooks/useAnomalyDetectionJobs'; -const initalData = { +const initialData = { items: [], hasHistoricalData: true, hasLegacyData: false, @@ -33,7 +36,7 @@ export function ServiceOverview() { urlParams: { start, end }, uiFilters, } = useUrlParams(); - const { data = initalData, status } = useFetcher( + const { data = initialData, status } = useFetcher( (callApmApi) => { if (start && end) { return callApmApi({ @@ -93,6 +96,26 @@ export function ServiceOverview() { [] ); + const { + data: anomalyDetectionJobsData, + status: anomalyDetectionJobsStatus, + } = useAnomalyDetectionJobs(); + + const [userHasDismissedCallout, setUserHasDismissedCallout] = useLocalStorage( + 'apm.userHasDismissedServiceInventoryMlCallout', + false + ); + + const canCreateJob = !!core.application.capabilities.ml?.canCreateJob; + + const displayMlCallout = + anomalyDetectionJobsStatus === FETCH_STATUS.SUCCESS && + !anomalyDetectionJobsData?.jobs.length && + canCreateJob && + !userHasDismissedCallout; + + const displayHealthStatus = data.items.some((item) => 'severity' in item); + return ( <> @@ -101,17 +124,27 @@ export function ServiceOverview() { - - + {displayMlCallout ? ( + + setUserHasDismissedCallout(true)} /> + + ) : null} + + + + } /> - } - /> - + + + diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts b/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.ts new file mode 100644 index 000000000000..2475eecee8e3 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/get_agent_icon.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 { getNormalizedAgentName } from '../../../../common/agent_name'; +import dotNetIcon from './icons/dot-net.svg'; +import goIcon from './icons/go.svg'; +import javaIcon from './icons/java.svg'; +import nodeJsIcon from './icons/nodejs.svg'; +import phpIcon from './icons/php.svg'; +import pythonIcon from './icons/python.svg'; +import rubyIcon from './icons/ruby.svg'; +import rumJsIcon from './icons/rumjs.svg'; + +const agentIcons: { [key: string]: string } = { + dotnet: dotNetIcon, + go: goIcon, + java: javaIcon, + 'js-base': rumJsIcon, + nodejs: nodeJsIcon, + php: phpIcon, + python: pythonIcon, + ruby: rubyIcon, +}; + +export function getAgentIcon(agentName?: string) { + const normalizedAgentName = getNormalizedAgentName(agentName); + return normalizedAgentName && agentIcons[normalizedAgentName]; +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/dot-net.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/dot-net.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/go.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/go.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/go.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/go.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/java.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/java.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/java.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/java.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/nodejs.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/nodejs.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/php.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/php.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/php.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/python.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/python.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/python.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/python.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/ruby.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/ruby.svg diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg b/x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg similarity index 100% rename from x-pack/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg rename to x-pack/plugins/apm/public/components/shared/AgentIcon/icons/rumjs.svg diff --git a/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx b/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx new file mode 100644 index 000000000000..5646fc05bd28 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/AgentIcon/index.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; +import { getAgentIcon } from './get_agent_icon'; +import { px } from '../../../style/variables'; + +interface Props { + agentName: AgentName; +} + +export function AgentIcon(props: Props) { + const { agentName } = props; + + const icon = getAgentIcon(agentName); + + return {agentName}; +} diff --git a/x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx b/x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx index 9fe52aab8364..9db563a0f6ba 100644 --- a/x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx @@ -33,9 +33,22 @@ interface Props { hidePerPageOptions?: boolean; noItemsMessage?: React.ReactNode; sortItems?: boolean; + sortFn?: ( + items: T[], + sortField: string, + sortDirection: 'asc' | 'desc' + ) => T[]; pagination?: boolean; } +function defaultSortFn( + items: T[], + sortField: string, + sortDirection: 'asc' | 'desc' +) { + return orderBy(items, sortField, sortDirection); +} + function UnoptimizedManagedTable(props: Props) { const history = useHistory(); const { @@ -48,6 +61,7 @@ function UnoptimizedManagedTable(props: Props) { hidePerPageOptions = true, noItemsMessage, sortItems = true, + sortFn = defaultSortFn, pagination = true, } = props; @@ -62,11 +76,11 @@ function UnoptimizedManagedTable(props: Props) { const renderedItems = useMemo(() => { const sortedItems = sortItems - ? orderBy(items, sortField, sortDirection as 'asc' | 'desc') + ? sortFn(items, sortField, sortDirection as 'asc' | 'desc') : items; return sortedItems.slice(page * pageSize, (page + 1) * pageSize); - }, [page, pageSize, sortField, sortDirection, items, sortItems]); + }, [page, pageSize, sortField, sortDirection, items, sortItems, sortFn]); const sort = useMemo(() => { return { diff --git a/x-pack/plugins/apm/public/components/shared/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx b/x-pack/plugins/apm/public/components/shared/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx index fcbdb900368e..5bddfc67200b 100644 --- a/x-pack/plugins/apm/public/components/shared/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx @@ -8,9 +8,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText } from '@elastic/eui'; -import { getSeverityColor } from '../../app/ServiceMap/cytoscapeOptions'; +import { + getSeverityColor, + Severity, +} from '../../../../common/anomaly_detection'; import { useTheme } from '../../../hooks/useTheme'; -import { severity as Severity } from '../../app/ServiceMap/Popover/getSeverity'; type SeverityScore = 0 | 25 | 50 | 75; const ANOMALY_SCORES: SeverityScore[] = [0, 25, 50, 75]; diff --git a/x-pack/plugins/apm/public/components/shared/charts/SparkPlot/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/SparkPlot/index.tsx new file mode 100644 index 000000000000..18b914afea99 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/charts/SparkPlot/index.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { ScaleType, Chart, Settings, AreaSeries } from '@elastic/charts'; +import { EuiIcon } from '@elastic/eui'; +import { EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { px } from '../../../../style/variables'; +import { useChartTheme } from '../../../../../../observability/public'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; + +interface Props { + color: string; + series: Array<{ x: number; y: number | null }>; +} + +export function SparkPlot(props: Props) { + const { series, color } = props; + const chartTheme = useChartTheme(); + + const isEmpty = series.every((point) => point.y === null); + + if (isEmpty) { + return ( + + + + + + + {NOT_AVAILABLE_LABEL} + + + + ); + } + + return ( + + + + + ); +} diff --git a/x-pack/plugins/apm/public/hooks/useAnomalyDetectionJobs.ts b/x-pack/plugins/apm/public/hooks/useAnomalyDetectionJobs.ts new file mode 100644 index 000000000000..56c58bc82967 --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/useAnomalyDetectionJobs.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useFetcher } from './useFetcher'; + +export function useAnomalyDetectionJobs() { + return useFetcher( + (callApmApi) => + callApmApi({ + pathname: `/api/apm/settings/anomaly-detection`, + }), + [], + { showToastOnError: false } + ); +} diff --git a/x-pack/plugins/apm/public/hooks/useLocalStorage.ts b/x-pack/plugins/apm/public/hooks/useLocalStorage.ts new file mode 100644 index 000000000000..cf37b45045f4 --- /dev/null +++ b/x-pack/plugins/apm/public/hooks/useLocalStorage.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState, useEffect } from 'react'; + +export function useLocalStorage(key: string, defaultValue: T) { + const [item, setItem] = useState(getFromStorage()); + + function getFromStorage() { + const storedItem = window.localStorage.getItem(key); + + let toStore: T = defaultValue; + + if (storedItem !== null) { + try { + toStore = JSON.parse(storedItem) as T; + } catch (err) { + window.localStorage.removeItem(key); + // eslint-disable-next-line no-console + console.log(`Unable to decode: ${key}`); + } + } + + return toStore; + } + + const updateFromStorage = () => { + const storedItem = getFromStorage(); + setItem(storedItem); + }; + + const saveToStorage = (value: T) => { + if (value === undefined) { + window.localStorage.removeItem(key); + } else { + window.localStorage.setItem(key, JSON.stringify(value)); + updateFromStorage(); + } + }; + + useEffect(() => { + window.addEventListener('storage', (event: StorageEvent) => { + if (event.key === key) { + updateFromStorage(); + } + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return [item, saveToStorage] as const; +} diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index b950b493c0f1..33e6a4b50a74 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -37,6 +37,7 @@ import { import { AlertType } from '../common/alert_types'; import { featureCatalogueEntry } from './featureCatalogueEntry'; import { toggleAppLinkInNav } from './toggleAppLinkInNav'; +import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; export type ApmPluginSetup = void; export type ApmPluginStart = void; @@ -57,6 +58,7 @@ export interface ApmPluginStartDeps { home: void; licensing: void; triggers_actions_ui: TriggersAndActionsUIPublicPluginStart; + embeddable: EmbeddableStart; } export class ApmPlugin implements Plugin { @@ -127,12 +129,18 @@ export class ApmPlugin implements Plugin { async mount(params: AppMountParameters) { // Load application bundle and Get start service - const [{ renderApp }, [coreStart]] = await Promise.all([ + const [{ renderApp }, [coreStart, corePlugins]] = await Promise.all([ import('./application/csmApp'), core.getStartServices(), ]); - return renderApp(coreStart, pluginSetupDeps, params, config); + return renderApp( + coreStart, + pluginSetupDeps, + params, + config, + corePlugins as ApmPluginStartDeps + ); }, }); } diff --git a/x-pack/plugins/apm/scripts/tsconfig.json b/x-pack/plugins/apm/scripts/tsconfig.json index 64602bc6b276..f1643608496a 100644 --- a/x-pack/plugins/apm/scripts/tsconfig.json +++ b/x-pack/plugins/apm/scripts/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "../../../../tsconfig.base.json", "include": [ - "./**/*" + "./**/*", + "../observability" ], "exclude": [], "compilerOptions": { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts index e7eb7b8de65e..93af51b572aa 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_transaction_duration_anomaly_alert_type.ts @@ -81,6 +81,11 @@ export function registerTransactionDurationAnomalyAlertType({ anomalyDetectors, alertParams.environment ); + + if (mlJobIds.length === 0) { + return {}; + } + const anomalySearchParams = { body: { size: 0, diff --git a/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts b/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts index 75b0471424e7..5b78d97d5b68 100644 --- a/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/get_bucket_size/index.ts @@ -7,22 +7,23 @@ import moment from 'moment'; // @ts-expect-error import { calculateAuto } from './calculate_auto'; -// @ts-expect-error -import { unitToSeconds } from './unit_to_seconds'; -export function getBucketSize(start: number, end: number, interval: string) { +export function getBucketSize( + start: number, + end: number, + numBuckets: number = 100 +) { const duration = moment.duration(end - start, 'ms'); - const bucketSize = Math.max(calculateAuto.near(100, duration).asSeconds(), 1); + const bucketSize = Math.max( + calculateAuto.near(numBuckets, duration).asSeconds(), + 1 + ); const intervalString = `${bucketSize}s`; - const matches = interval && interval.match(/^([\d]+)([shmdwMy]|ms)$/); - const minBucketSize = matches - ? Number(matches[1]) * unitToSeconds(matches[2]) - : 0; - if (bucketSize < minBucketSize) { + if (bucketSize < 0) { return { - bucketSize: minBucketSize, - intervalString: interval, + bucketSize: 0, + intervalString: 'auto', }; } diff --git a/x-pack/plugins/apm/server/lib/helpers/metrics.ts b/x-pack/plugins/apm/server/lib/helpers/metrics.ts index 9f5b5cdf4755..ea018868f951 100644 --- a/x-pack/plugins/apm/server/lib/helpers/metrics.ts +++ b/x-pack/plugins/apm/server/lib/helpers/metrics.ts @@ -11,7 +11,7 @@ export function getMetricsDateHistogramParams( end: number, metricsInterval: number ) { - const { bucketSize } = getBucketSize(start, end, 'auto'); + const { bucketSize } = getBucketSize(start, end); return { field: '@timestamp', diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index 6b69e57389df..eba75433a514 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -5,6 +5,7 @@ */ import moment from 'moment'; +import { isActivePlatinumLicense } from '../../../common/service_map'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; import { KibanaRequest } from '../../../../../../src/core/server'; import { APMConfig } from '../..'; @@ -98,11 +99,14 @@ export async function setupRequest( context, request, }), - ml: getMlSetup( - context.plugins.ml, - context.core.savedObjects.client, - request - ), + ml: + context.plugins.ml && isActivePlatinumLicense(context.licensing.license) + ? getMlSetup( + context.plugins.ml, + context.core.savedObjects.client, + request + ) + : undefined, config, }; @@ -115,14 +119,10 @@ export async function setupRequest( } function getMlSetup( - ml: APMRequestHandlerContext['plugins']['ml'], + ml: Required['ml'], savedObjectsClient: APMRequestHandlerContext['core']['savedObjects']['client'], request: KibanaRequest ) { - if (!ml) { - return; - } - return { mlSystem: ml.mlSystemProvider(request), anomalyDetectors: ml.anomalyDetectorsProvider(request), diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index 551384da2cca..d7e64bdcacd1 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -44,7 +44,7 @@ export async function fetchAndTransformGcMetrics({ }) { const { start, end, apmEventClient, config } = setup; - const { bucketSize } = getBucketSize(start, end, 'auto'); + const { bucketSize } = getBucketSize(start, end); const projection = getMetricsProjection({ setup, @@ -74,7 +74,7 @@ export async function fetchAndTransformGcMetrics({ field: `${LABEL_NAME}`, }, aggs: { - over_time: { + timeseries: { date_histogram: getMetricsDateHistogramParams( start, end, @@ -123,7 +123,7 @@ export async function fetchAndTransformGcMetrics({ const series = aggregations.per_pool.buckets.map((poolBucket, i) => { const label = poolBucket.key as string; - const timeseriesData = poolBucket.over_time; + const timeseriesData = poolBucket.timeseries; const data = timeseriesData.buckets.map((bucket) => { // derivative/value will be undefined for the first hit and if the `max` value is null diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index ec274d20b600..ed8ae923e6e6 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Logger } from 'kibana/server'; import Boom from 'boom'; import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { PromiseReturnType } from '../../../typings/common'; @@ -27,11 +26,9 @@ export type ServiceAnomaliesResponse = PromiseReturnType< export async function getServiceAnomalies({ setup, - logger, environment, }: { setup: Setup & SetupTimeRange; - logger: Logger; environment?: string; }) { const { ml, start, end } = setup; @@ -41,11 +38,20 @@ export async function getServiceAnomalies({ } const mlCapabilities = await ml.mlSystem.mlCapabilities(); + if (!mlCapabilities.mlFeatureEnabledInSpace) { throw Boom.forbidden(ML_ERRORS.ML_NOT_AVAILABLE_IN_SPACE); } const mlJobIds = await getMLJobIds(ml.anomalyDetectors, environment); + + if (!mlJobIds.length) { + return { + mlJobIds: [], + serviceAnomalies: {}, + }; + } + const params = { body: { size: 0, @@ -120,7 +126,9 @@ interface ServiceAnomaliesAggResponse { function transformResponseToServiceAnomalies( response: ServiceAnomaliesAggResponse ): Record { - const serviceAnomaliesMap = response.aggregations.services.buckets.reduce( + const serviceAnomaliesMap = ( + response.aggregations?.services.buckets ?? [] + ).reduce( (statsByServiceName, { key: serviceName, top_score: topScoreAgg }) => { return { ...statsByServiceName, @@ -153,7 +161,7 @@ export async function getMLJobIds( (job) => job.custom_settings?.job_tags?.environment === environment ); if (!matchingMLJob) { - throw new Error(`ML job Not Found for environment "${environment}".`); + return []; } return [matchingMLJob.job_id]; } diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts index d1c99d778c8f..1e26b6f3f58f 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts @@ -58,6 +58,9 @@ describe('getServiceMapServiceNodeInfo', () => { indices: {}, start: 1593460053026000, end: 1593497863217000, + config: { + 'xpack.apm.metricsInterval': 30, + }, } as unknown) as Setup & SetupTimeRange; const environment = 'test environment'; const serviceName = 'test service name'; diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index ca86c1d93fa6..c5e072e07399 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -105,6 +105,24 @@ Array [ "field": "transaction.duration.us", }, }, + "timeseries": Object { + "aggs": Object { + "average": Object { + "avg": Object { + "field": "transaction.duration.us", + }, + }, + }, + "date_histogram": Object { + "extended_bounds": Object { + "max": 1528977600000, + "min": 1528113600000, + }, + "field": "@timestamp", + "fixed_interval": "43200s", + "min_doc_count": 0, + }, + }, }, "terms": Object { "field": "service.name", @@ -194,6 +212,19 @@ Array [ "body": Object { "aggs": Object { "services": Object { + "aggs": Object { + "timeseries": Object { + "date_histogram": Object { + "extended_bounds": Object { + "max": 1528977600000, + "min": 1528113600000, + }, + "field": "@timestamp", + "fixed_interval": "43200s", + "min_doc_count": 0, + }, + }, + }, "terms": Object { "field": "service.name", "size": 500, @@ -226,12 +257,37 @@ Array [ Object { "apm": Object { "events": Array [ - "error", + "transaction", ], }, "body": Object { "aggs": Object { "services": Object { + "aggs": Object { + "outcomes": Object { + "terms": Object { + "field": "event.outcome", + }, + }, + "timeseries": Object { + "aggs": Object { + "outcomes": Object { + "terms": Object { + "field": "event.outcome", + }, + }, + }, + "date_histogram": Object { + "extended_bounds": Object { + "max": 1528977600000, + "min": 1528113600000, + }, + "field": "@timestamp", + "fixed_interval": "43200s", + "min_doc_count": 0, + }, + }, + }, "terms": Object { "field": "service.name", "size": 500, @@ -255,6 +311,14 @@ Array [ "my.custom.ui.filter": "foo-bar", }, }, + Object { + "terms": Object { + "event.outcome": Array [ + "failure", + "success", + ], + }, + }, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index d888b43b63fa..50a968467fb4 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -15,15 +15,22 @@ import { getTransactionDurationAverages, getAgentNames, getTransactionRates, - getErrorRates, + getTransactionErrorRates, getEnvironments, + getHealthStatuses, } from './get_services_items_stats'; export type ServiceListAPIResponse = PromiseReturnType; export type ServicesItemsSetup = Setup & SetupTimeRange & SetupUIFilters; export type ServicesItemsProjection = ReturnType; -export async function getServicesItems(setup: ServicesItemsSetup) { +export async function getServicesItems({ + setup, + mlAnomaliesEnvironment, +}: { + setup: ServicesItemsSetup; + mlAnomaliesEnvironment?: string; +}) { const params = { projection: getServicesProjection({ setup }), setup, @@ -33,22 +40,25 @@ export async function getServicesItems(setup: ServicesItemsSetup) { transactionDurationAverages, agentNames, transactionRates, - errorRates, + transactionErrorRates, environments, + healthStatuses, ] = await Promise.all([ getTransactionDurationAverages(params), getAgentNames(params), getTransactionRates(params), - getErrorRates(params), + getTransactionErrorRates(params), getEnvironments(params), + getHealthStatuses(params, mlAnomaliesEnvironment), ]); const allMetrics = [ ...transactionDurationAverages, ...agentNames, ...transactionRates, - ...errorRates, + ...transactionErrorRates, ...environments, + ...healthStatuses, ]; return joinByKey(allMetrics, 'serviceName'); diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts index ddce3b667a60..ab6b61ca2174 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts @@ -4,10 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EventOutcome } from '../../../../common/event_outcome'; +import { getSeverity } from '../../../../common/anomaly_detection'; +import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; import { TRANSACTION_DURATION, AGENT_NAME, SERVICE_ENVIRONMENT, + EVENT_OUTCOME, } from '../../../../common/elasticsearch_fieldnames'; import { mergeProjection } from '../../../projections/util/merge_projection'; import { ProcessorEvent } from '../../../../common/processor_event'; @@ -15,6 +19,21 @@ import { ServicesItemsSetup, ServicesItemsProjection, } from './get_services_items'; +import { getBucketSize } from '../../helpers/get_bucket_size'; +import { + getMLJobIds, + getServiceAnomalies, +} from '../../service_map/get_service_anomalies'; +import { AggregationResultOf } from '../../../../typings/elasticsearch/aggregations'; + +function getDateHistogramOpts(start: number, end: number) { + return { + field: '@timestamp', + fixed_interval: getBucketSize(start, end, 20).intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }; +} const MAX_NUMBER_OF_SERVICES = 500; @@ -30,7 +49,7 @@ export const getTransactionDurationAverages = async ({ setup, projection, }: AggregationParams) => { - const { apmEventClient } = setup; + const { apmEventClient, start, end } = setup; const response = await apmEventClient.search( mergeProjection(projection, { @@ -51,6 +70,16 @@ export const getTransactionDurationAverages = async ({ field: TRANSACTION_DURATION, }, }, + timeseries: { + date_histogram: getDateHistogramOpts(start, end), + aggs: { + average: { + avg: { + field: TRANSACTION_DURATION, + }, + }, + }, + }, }, }, }, @@ -64,9 +93,15 @@ export const getTransactionDurationAverages = async ({ return []; } - return aggregations.services.buckets.map((bucket) => ({ - serviceName: bucket.key as string, - avgResponseTime: bucket.average.value, + return aggregations.services.buckets.map((serviceBucket) => ({ + serviceName: serviceBucket.key as string, + avgResponseTime: { + value: serviceBucket.average.value, + timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ + x: dateBucket.key, + y: dateBucket.average.value, + })), + }, })); }; @@ -112,9 +147,10 @@ export const getAgentNames = async ({ return []; } - return aggregations.services.buckets.map((bucket) => ({ - serviceName: bucket.key as string, - agentName: bucket.agent_name.hits.hits[0]?._source.agent.name, + return aggregations.services.buckets.map((serviceBucket) => ({ + serviceName: serviceBucket.key as string, + agentName: serviceBucket.agent_name.hits.hits[0]?._source.agent + .name as AgentName, })); }; @@ -122,7 +158,7 @@ export const getTransactionRates = async ({ setup, projection, }: AggregationParams) => { - const { apmEventClient } = setup; + const { apmEventClient, start, end } = setup; const response = await apmEventClient.search( mergeProjection(projection, { apm: { @@ -136,6 +172,11 @@ export const getTransactionRates = async ({ ...projection.body.aggs.services.terms, size: MAX_NUMBER_OF_SERVICES, }, + aggs: { + timeseries: { + date_histogram: getDateHistogramOpts(start, end), + }, + }, }, }, }, @@ -150,33 +191,67 @@ export const getTransactionRates = async ({ const deltaAsMinutes = getDeltaAsMinutes(setup); - return aggregations.services.buckets.map((bucket) => { - const transactionsPerMinute = bucket.doc_count / deltaAsMinutes; + return aggregations.services.buckets.map((serviceBucket) => { + const transactionsPerMinute = serviceBucket.doc_count / deltaAsMinutes; return { - serviceName: bucket.key as string, - transactionsPerMinute, + serviceName: serviceBucket.key as string, + transactionsPerMinute: { + value: transactionsPerMinute, + timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ + x: dateBucket.key, + y: dateBucket.doc_count / deltaAsMinutes, + })), + }, }; }); }; -export const getErrorRates = async ({ +export const getTransactionErrorRates = async ({ setup, projection, }: AggregationParams) => { - const { apmEventClient } = setup; + const { apmEventClient, start, end } = setup; + + const outcomes = { + terms: { + field: EVENT_OUTCOME, + }, + }; + const response = await apmEventClient.search( mergeProjection(projection, { apm: { - events: [ProcessorEvent.error], + events: [ProcessorEvent.transaction], }, body: { size: 0, + query: { + bool: { + filter: [ + ...projection.body.query.bool.filter, + { + terms: { + [EVENT_OUTCOME]: [EventOutcome.failure, EventOutcome.success], + }, + }, + ], + }, + }, aggs: { services: { terms: { ...projection.body.aggs.services.terms, size: MAX_NUMBER_OF_SERVICES, }, + aggs: { + outcomes, + timeseries: { + date_histogram: getDateHistogramOpts(start, end), + aggs: { + outcomes, + }, + }, + }, }, }, }, @@ -189,13 +264,36 @@ export const getErrorRates = async ({ return []; } - const deltaAsMinutes = getDeltaAsMinutes(setup); + function calculateTransactionErrorPercentage( + outcomeResponse: AggregationResultOf + ) { + const successfulTransactions = + outcomeResponse.buckets.find( + (bucket) => bucket.key === EventOutcome.success + )?.doc_count ?? 0; + const failedTransactions = + outcomeResponse.buckets.find( + (bucket) => bucket.key === EventOutcome.failure + )?.doc_count ?? 0; - return aggregations.services.buckets.map((bucket) => { - const errorsPerMinute = bucket.doc_count / deltaAsMinutes; + return failedTransactions / (successfulTransactions + failedTransactions); + } + + return aggregations.services.buckets.map((serviceBucket) => { + const transactionErrorRate = calculateTransactionErrorPercentage( + serviceBucket.outcomes + ); return { - serviceName: bucket.key as string, - errorsPerMinute, + serviceName: serviceBucket.key as string, + transactionErrorRate: { + value: transactionErrorRate, + timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => { + return { + x: dateBucket.key, + y: calculateTransactionErrorPercentage(dateBucket.outcomes), + }; + }), + }, }; }); }; @@ -241,8 +339,43 @@ export const getEnvironments = async ({ return []; } - return aggregations.services.buckets.map((bucket) => ({ - serviceName: bucket.key as string, - environments: bucket.environments.buckets.map((env) => env.key as string), + return aggregations.services.buckets.map((serviceBucket) => ({ + serviceName: serviceBucket.key as string, + environments: serviceBucket.environments.buckets.map( + (envBucket) => envBucket.key as string + ), })); }; + +export const getHealthStatuses = async ( + { setup }: AggregationParams, + mlAnomaliesEnvironment?: string +) => { + if (!setup.ml) { + return []; + } + + const jobIds = await getMLJobIds( + setup.ml.anomalyDetectors, + mlAnomaliesEnvironment + ); + if (!jobIds.length) { + return []; + } + + const anomalies = await getServiceAnomalies({ + setup, + environment: mlAnomaliesEnvironment, + }); + + return Object.keys(anomalies.serviceAnomalies).map((serviceName) => { + const stats = anomalies.serviceAnomalies[serviceName]; + + const severity = getSeverity(stats.anomalyScore); + + return { + serviceName, + severity, + }; + }); +}; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts index 5a909ebd6ec5..28b4c64a4af4 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts @@ -17,11 +17,15 @@ import { getServicesItems } from './get_services_items'; export type ServiceListAPIResponse = PromiseReturnType; -export async function getServices( - setup: Setup & SetupTimeRange & SetupUIFilters -) { +export async function getServices({ + setup, + mlAnomaliesEnvironment, +}: { + setup: Setup & SetupTimeRange & SetupUIFilters; + mlAnomaliesEnvironment?: string; +}) { const [items, hasLegacyData] = await Promise.all([ - getServicesItems(setup), + getServicesItems({ setup, mlAnomaliesEnvironment }), getLegacyDataStatus(setup), ]); diff --git a/x-pack/plugins/apm/server/lib/services/queries.test.ts b/x-pack/plugins/apm/server/lib/services/queries.test.ts index 99c58a17d396..9b0dd7a03ca5 100644 --- a/x-pack/plugins/apm/server/lib/services/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/services/queries.test.ts @@ -38,7 +38,7 @@ describe('services queries', () => { }); it('fetches the service items', async () => { - mock = await inspectSearchParams((setup) => getServicesItems(setup)); + mock = await inspectSearchParams((setup) => getServicesItems({ setup })); const allParams = mock.spy.mock.calls.map((call) => call[0]); diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts index f7b7f7216816..1e08b04416e1 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts @@ -62,7 +62,7 @@ export async function getErrorRate({ total_transactions: { date_histogram: { field: '@timestamp', - fixed_interval: getBucketSize(start, end, 'auto').intervalString, + fixed_interval: getBucketSize(start, end).intervalString, min_doc_count: 0, extended_bounds: { min: start, max: end }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts index f68082dfaa1e..51118278fb82 100644 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts @@ -24,7 +24,7 @@ export type ESResponse = PromiseReturnType; export function fetcher(options: Options) { const { end, apmEventClient, start, uiFiltersES } = options.setup; const { serviceName, transactionName } = options; - const { intervalString } = getBucketSize(start, end, 'auto'); + const { intervalString } = getBucketSize(start, end); const transactionNameFilter = transactionName ? [{ term: { [TRANSACTION_NAME]: transactionName } }] diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts index 596c3137ec19..d8865f0049d3 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts @@ -64,16 +64,10 @@ export async function getAnomalySeries({ return; } - let mlJobIds: string[] = []; - try { - mlJobIds = await getMLJobIds( - setup.ml.anomalyDetectors, - uiFilters.environment - ); - } catch (error) { - logger.error(error); - return; - } + const mlJobIds = await getMLJobIds( + setup.ml.anomalyDetectors, + uiFilters.environment + ); // don't fetch anomalies if there are isn't exaclty 1 ML job match for the given environment if (mlJobIds.length !== 1) { @@ -87,7 +81,7 @@ export async function getAnomalySeries({ } const { start, end } = setup; - const { intervalString, bucketSize } = getBucketSize(start, end, 'auto'); + const { intervalString, bucketSize } = getBucketSize(start, end); const esResponse = await anomalySeriesFetcher({ serviceName, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts index 1498c22e327d..f39529b59caa 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts @@ -35,7 +35,7 @@ export function timeseriesFetcher({ setup: Setup & SetupTimeRange & SetupUIFilters; }) { const { start, end, uiFiltersES, apmEventClient } = setup; - const { intervalString } = getBucketSize(start, end, 'auto'); + const { intervalString } = getBucketSize(start, end); const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts index 8a0fe1a57736..ea06bd57bfff 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts @@ -20,7 +20,7 @@ export async function getApmTimeseriesData(options: { setup: Setup & SetupTimeRange & SetupUIFilters; }) { const { start, end } = options.setup; - const { bucketSize } = getBucketSize(start, end, 'auto'); + const { bucketSize } = getBucketSize(start, end); const durationAsMinutes = (end - start) / 1000 / 60; const timeseriesResponse = await timeseriesFetcher(options); diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index f7e3977ae7d3..f25e37927f09 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -127,7 +127,7 @@ export class APMPlugin implements Plugin { }; }); - plugins.features.registerFeature(APM_FEATURE); + plugins.features.registerKibanaFeature(APM_FEATURE); plugins.licensing.featureUsage.register( APM_SERVICE_MAPS_FEATURE_NAME, APM_SERVICE_MAPS_LICENSE_TYPE diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index 971e247d9898..8533d54ed627 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -8,7 +8,7 @@ import Boom from 'boom'; import * as t from 'io-ts'; import { invalidLicenseMessage, - isValidPlatinumLicense, + isActivePlatinumLicense, } from '../../common/service_map'; import { setupRequest } from '../lib/helpers/setup_request'; import { getServiceMap } from '../lib/service_map/get_service_map'; @@ -33,7 +33,7 @@ export const serviceMapRoute = createRoute(() => ({ if (!context.config['xpack.apm.serviceMapEnabled']) { throw Boom.notFound(); } - if (!isValidPlatinumLicense(context.licensing.license)) { + if (!isActivePlatinumLicense(context.licensing.license)) { throw Boom.forbidden(invalidLicenseMessage); } context.licensing.featureUsage.notifyUsage(APM_SERVICE_MAPS_FEATURE_NAME); @@ -59,7 +59,7 @@ export const serviceMapServiceNodeRoute = createRoute(() => ({ if (!context.config['xpack.apm.serviceMapEnabled']) { throw Boom.notFound(); } - if (!isValidPlatinumLicense(context.licensing.license)) { + if (!isActivePlatinumLicense(context.licensing.license)) { throw Boom.forbidden(invalidLicenseMessage); } const logger = context.logger; diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 74ab717b8de5..cc7f25867df2 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -16,6 +16,7 @@ import { createRoute } from './create_route'; import { uiFiltersRt, rangeRt } from './default_api_types'; import { getServiceAnnotations } from '../lib/services/annotations'; import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt'; +import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters'; export const servicesRoute = createRoute(() => ({ path: '/api/apm/services', @@ -23,8 +24,17 @@ export const servicesRoute = createRoute(() => ({ query: t.intersection([uiFiltersRt, rangeRt]), }, handler: async ({ context, request }) => { + const { environment } = getParsedUiFilters({ + uiFilters: context.params.query.uiFilters, + logger: context.logger, + }); + const setup = await setupRequest(context, request); - const services = await getServices(setup); + + const services = await getServices({ + setup, + mlAnomaliesEnvironment: environment, + }); return services; }, diff --git a/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts b/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts index ac25f22751f2..290e81bd2997 100644 --- a/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts +++ b/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts @@ -6,6 +6,7 @@ import * as t from 'io-ts'; import Boom from 'boom'; +import { isActivePlatinumLicense } from '../../../common/service_map'; import { ML_ERRORS } from '../../../common/anomaly_detection'; import { createRoute } from '../create_route'; import { getAnomalyDetectionJobs } from '../../lib/anomaly_detection/get_anomaly_detection_jobs'; @@ -24,8 +25,7 @@ export const anomalyDetectionJobsRoute = createRoute(() => ({ handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - const license = context.licensing.license; - if (!license.isActive || !license.hasAtLeast('platinum')) { + if (!isActivePlatinumLicense(context.licensing.license)) { throw Boom.forbidden(ML_ERRORS.INVALID_LICENSE); } @@ -56,8 +56,7 @@ export const createAnomalyDetectionJobsRoute = createRoute(() => ({ const { environments } = context.params.body; const setup = await setupRequest(context, request); - const license = context.licensing.license; - if (!license.isActive || !license.hasAtLeast('platinum')) { + if (!isActivePlatinumLicense(context.licensing.license)) { throw Boom.forbidden(ML_ERRORS.INVALID_LICENSE); } diff --git a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts index 7a7592b24896..bbd2c9eb8624 100644 --- a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts +++ b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts @@ -346,6 +346,12 @@ export type ValidAggregationKeysOf< T extends Record > = keyof (UnionToIntersection extends never ? T : UnionToIntersection); +export type AggregationResultOf< + TAggregationOptionsMap extends AggregationOptionsMap, + TDocument +> = AggregationResponsePart[AggregationType & + ValidAggregationKeysOf]; + export type AggregationResponseMap< TAggregationInputMap extends AggregationInputMap | undefined, TDocument diff --git a/x-pack/plugins/beats_management/kibana.json b/x-pack/plugins/beats_management/kibana.json index 3fd1ab6fd870..c1070eedf07a 100644 --- a/x-pack/plugins/beats_management/kibana.json +++ b/x-pack/plugins/beats_management/kibana.json @@ -7,7 +7,8 @@ "requiredPlugins": [ "data", "licensing", - "management" + "management", + "features" ], "optionalPlugins": [ "security" diff --git a/x-pack/plugins/beats_management/server/plugin.ts b/x-pack/plugins/beats_management/server/plugin.ts index 92c2278148bc..fde0a2efecdd 100644 --- a/x-pack/plugins/beats_management/server/plugin.ts +++ b/x-pack/plugins/beats_management/server/plugin.ts @@ -11,6 +11,7 @@ import { Plugin, PluginInitializerContext, } from '../../../../src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { SecurityPluginSetup } from '../../security/server'; import { LicensingPluginStart } from '../../licensing/server'; import { BeatsManagementConfigType } from '../common'; @@ -22,6 +23,7 @@ import { beatsIndexTemplate } from './index_templates'; interface SetupDeps { security?: SecurityPluginSetup; + features: FeaturesPluginSetup; } interface StartDeps { @@ -42,7 +44,7 @@ export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDep private readonly initializerContext: PluginInitializerContext ) {} - public async setup(core: CoreSetup, { security }: SetupDeps) { + public async setup(core: CoreSetup, { features, security }: SetupDeps) { this.securitySetup = security; const router = core.http.createRouter(); @@ -52,6 +54,20 @@ export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDep return this.beatsLibs!; }); + features.registerElasticsearchFeature({ + id: 'beats_management', + management: { + ingest: ['beats_management'], + }, + privileges: [ + { + ui: [], + requiredClusterPrivileges: [], + requiredRoles: ['beats_admin'], + }, + ], + }); + return {}; } diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index c822ed86cb01..9a41a00883c1 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -37,7 +37,7 @@ export class CanvasPlugin implements Plugin { coreSetup.savedObjects.registerType(workpadType); coreSetup.savedObjects.registerType(workpadTemplateType); - plugins.features.registerFeature({ + plugins.features.registerKibanaFeature({ id: 'canvas', name: 'Canvas', order: 400, diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts index a22d7ae5cea2..545ccf82c3d7 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts @@ -13,6 +13,7 @@ import { SUPPORTED_CONNECTORS, SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, + RESILIENT_ACTION_TYPE_ID, } from '../../../../../common/constants'; /* @@ -37,8 +38,12 @@ export function initCaseConfigureGetActionConnector({ caseService, router }: Rou (action) => SUPPORTED_CONNECTORS.includes(action.actionTypeId) && // Need this filtering temporary to display only Case owned ServiceNow connectors - (![SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID].includes(action.actionTypeId) || - ([SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID].includes(action.actionTypeId) && + (![SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, RESILIENT_ACTION_TYPE_ID].includes( + action.actionTypeId + ) || + ([SERVICENOW_ACTION_TYPE_ID, JIRA_ACTION_TYPE_ID, RESILIENT_ACTION_TYPE_ID].includes( + action.actionTypeId + ) && action.config?.isCaseOwned === true)) ); return response.ok({ body: results }); diff --git a/x-pack/plugins/cross_cluster_replication/kibana.json b/x-pack/plugins/cross_cluster_replication/kibana.json index 13746bb0e34c..292820f81adb 100644 --- a/x-pack/plugins/cross_cluster_replication/kibana.json +++ b/x-pack/plugins/cross_cluster_replication/kibana.json @@ -8,7 +8,8 @@ "licensing", "management", "remoteClusters", - "indexManagement" + "indexManagement", + "features" ], "optionalPlugins": [ "usageCollection" diff --git a/x-pack/plugins/cross_cluster_replication/server/plugin.ts b/x-pack/plugins/cross_cluster_replication/server/plugin.ts index e39b4dfd471a..d40a53f28987 100644 --- a/x-pack/plugins/cross_cluster_replication/server/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/server/plugin.ts @@ -87,7 +87,7 @@ export class CrossClusterReplicationServerPlugin implements Plugin { this.ccrEsClient = this.ccrEsClient ?? (await getCustomEsClient(getStartServices)); return { diff --git a/x-pack/plugins/cross_cluster_replication/server/types.ts b/x-pack/plugins/cross_cluster_replication/server/types.ts index c287acf86eb2..62c96b48c437 100644 --- a/x-pack/plugins/cross_cluster_replication/server/types.ts +++ b/x-pack/plugins/cross_cluster_replication/server/types.ts @@ -5,6 +5,7 @@ */ import { IRouter } from 'src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; import { RemoteClustersPluginSetup } from '../../remote_clusters/server'; @@ -16,6 +17,7 @@ export interface Dependencies { licensing: LicensingPluginSetup; indexManagement: IndexManagementPluginSetup; remoteClusters: RemoteClustersPluginSetup; + features: FeaturesPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/dashboard_mode/kibana.json b/x-pack/plugins/dashboard_mode/kibana.json index 4777b9b25be2..81e2073b5c7f 100644 --- a/x-pack/plugins/dashboard_mode/kibana.json +++ b/x-pack/plugins/dashboard_mode/kibana.json @@ -9,6 +9,7 @@ "optionalPlugins": ["security"], "requiredPlugins": [ "kibanaLegacy", + "urlForwarding", "dashboard" ], "server": true, diff --git a/x-pack/plugins/dashboard_mode/public/plugin.ts b/x-pack/plugins/dashboard_mode/public/plugin.ts index d988de5851cf..96486bd6da8c 100644 --- a/x-pack/plugins/dashboard_mode/public/plugin.ts +++ b/x-pack/plugins/dashboard_mode/public/plugin.ts @@ -7,6 +7,7 @@ import { trimStart } from 'lodash'; import { CoreSetup } from 'kibana/public'; import { KibanaLegacyStart } from '../../../../src/plugins/kibana_legacy/public'; +import { UrlForwardingStart } from '../../../../src/plugins/url_forwarding/public'; import { createDashboardEditUrl, DashboardConstants, @@ -22,7 +23,11 @@ function dashboardAppIdPrefix() { return trimStart(createDashboardEditUrl(''), '/'); } -function migratePath(currentHash: string, kibanaLegacy: KibanaLegacyStart) { +function migratePath( + currentHash: string, + kibanaLegacy: KibanaLegacyStart, + urlForwarding: UrlForwardingStart +) { if (currentHash === '' || currentHash === '#' || currentHash === '#/') { return `#${defaultUrl(kibanaLegacy.config.defaultAppId || '')}`; } @@ -30,7 +35,7 @@ function migratePath(currentHash: string, kibanaLegacy: KibanaLegacyStart) { return currentHash; } - const forwards = kibanaLegacy.getForwards(); + const forwards = urlForwarding.getForwards(); if (currentHash.startsWith('#/dashboards')) { const { rewritePath: migrateListingPath } = forwards.find( @@ -46,18 +51,18 @@ function migratePath(currentHash: string, kibanaLegacy: KibanaLegacyStart) { } export const plugin = () => ({ - setup(core: CoreSetup<{ kibanaLegacy: KibanaLegacyStart }>) { + setup(core: CoreSetup<{ kibanaLegacy: KibanaLegacyStart; urlForwarding: UrlForwardingStart }>) { core.application.register({ id: 'dashboard_mode', title: 'Dashboard mode', navLinkStatus: AppNavLinkStatus.hidden, mount: async () => { - const [coreStart, { kibanaLegacy }] = await core.getStartServices(); + const [coreStart, { kibanaLegacy, urlForwarding }] = await core.getStartServices(); kibanaLegacy.dashboardConfig.turnHideWriteControlsOn(); coreStart.chrome.navLinks.showOnly('dashboards'); setTimeout(() => { coreStart.application.navigateToApp('dashboards', { - path: migratePath(window.location.hash, kibanaLegacy), + path: migratePath(window.location.hash, kibanaLegacy, urlForwarding), }); }, 0); return () => {}; diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index 637af39339e2..5ded0f8f0dec 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -6,10 +6,11 @@ "xpack", "data_enhanced" ], "requiredPlugins": [ - "data" + "data", + "features" ], - "optionalPlugins": ["kibanaReact", "kibanaUtils", "usageCollection"], + "optionalPlugins": ["kibanaUtils", "usageCollection"], "server": true, "ui": true, - "requiredBundles": ["kibanaReact", "kibanaUtils"] + "requiredBundles": ["kibanaUtils"] } diff --git a/x-pack/plugins/data_enhanced/public/search/long_query_notification.tsx b/x-pack/plugins/data_enhanced/public/search/long_query_notification.tsx deleted file mode 100644 index 325cf1145fa5..000000000000 --- a/x-pack/plugins/data_enhanced/public/search/long_query_notification.tsx +++ /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 { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; - -interface Props { - cancel: () => void; - runBeyondTimeout: () => void; -} - -export function getLongQueryNotification(props: Props) { - return toMountPoint( - - ); -} - -export function LongQueryNotification(props: Props) { - return ( -
- - - - - - - - - - - - - -
- ); -} diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts index 261e03887acd..af2fc8560254 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts @@ -60,9 +60,6 @@ describe('EnhancedSearchInterceptor', () => { mockUsageCollector = { trackQueryTimedOut: jest.fn(), trackQueriesCancelled: jest.fn(), - trackLongQueryPopupShown: jest.fn(), - trackLongQueryDialogDismissed: jest.fn(), - trackLongQueryRunBeyondTimeout: jest.fn(), }; const mockPromise = new Promise((resolve) => { @@ -390,88 +387,4 @@ describe('EnhancedSearchInterceptor', () => { expect(mockUsageCollector.trackQueriesCancelled).toBeCalledTimes(1); }); }); - - describe('runBeyondTimeout', () => { - const timedResponses = [ - { - time: 250, - value: { - isPartial: true, - isRunning: true, - id: 1, - rawResponse: { - took: 1, - }, - }, - }, - { - time: 2000, - value: { - isPartial: false, - isRunning: false, - id: 1, - rawResponse: { - took: 1, - }, - }, - }, - ]; - - test('times out if runBeyondTimeout is not called', async () => { - mockFetchImplementation(timedResponses); - - const response = searchInterceptor.search({}); - response.subscribe({ next, error }); - - await timeTravel(250); - - expect(next).toHaveBeenCalled(); - expect(next.mock.calls[0][0]).toStrictEqual(timedResponses[0].value); - - await timeTravel(750); - - expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); - }); - - test('times out if runBeyondTimeout is called too late', async () => { - mockFetchImplementation(timedResponses); - - const response = searchInterceptor.search({}); - response.subscribe({ next, error }); - setTimeout(() => searchInterceptor.runBeyondTimeout(), 1100); - - await timeTravel(250); - - expect(next).toHaveBeenCalled(); - expect(next.mock.calls[0][0]).toStrictEqual(timedResponses[0].value); - - await timeTravel(750); - - expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(AbortError); - }); - - test('should prevent the request from timing out', async () => { - mockFetchImplementation(timedResponses); - - const response = searchInterceptor.search({}, { pollInterval: 0 }); - response.subscribe({ next, error, complete }); - setTimeout(() => searchInterceptor.runBeyondTimeout(), 500); - - await timeTravel(250); - - expect(next).toHaveBeenCalled(); - expect(next.mock.calls[0][0]).toStrictEqual(timedResponses[0].value); - - await timeTravel(250); // Run beyond timeout - await timeTravel(1750); // Final response - - expect(next).toHaveBeenCalledTimes(2); - expect(next.mock.calls[0][0]).toStrictEqual(timedResponses[0].value); - expect(next.mock.calls[1][0]).toStrictEqual(timedResponses[1].value); - expect(error).not.toHaveBeenCalled(); - expect(mockUsageCollector.trackLongQueryRunBeyondTimeout).toBeCalledTimes(1); - }); - }); }); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index 61cf579d3136..f7ae9fc6d0f9 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -6,7 +6,8 @@ import { throwError, EMPTY, timer, from, Subscription } from 'rxjs'; import { mergeMap, expand, takeUntil, finalize, tap } from 'rxjs/operators'; -import { getLongQueryNotification } from './long_query_notification'; +import { debounce } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { SearchInterceptor, SearchInterceptorDeps, @@ -42,38 +43,11 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { * Abort our `AbortController`, which in turn aborts any intercepted searches. */ public cancelPending = () => { - this.hideToast(); this.abortController.abort(); this.abortController = new AbortController(); if (this.deps.usageCollector) this.deps.usageCollector.trackQueriesCancelled(); }; - /** - * Un-schedule timing out all of the searches intercepted. - */ - public runBeyondTimeout = () => { - this.hideToast(); - this.timeoutSubscriptions.unsubscribe(); - if (this.deps.usageCollector) this.deps.usageCollector.trackLongQueryRunBeyondTimeout(); - }; - - protected showToast = () => { - if (this.longRunningToast) return; - this.longRunningToast = this.deps.toasts.addInfo( - { - title: 'Your query is taking a while', - text: getLongQueryNotification({ - cancel: this.cancelPending, - runBeyondTimeout: this.runBeyondTimeout, - }), - }, - { - toastLifeTimeMs: 1000000, - } - ); - if (this.deps.usageCollector) this.deps.usageCollector.trackLongQueryPopupShown(); - }; - public search( request: IAsyncSearchRequest, { pollInterval = 1000, ...options }: IAsyncSearchOptions = {} @@ -127,4 +101,28 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { }) ); } + + // Right now we are debouncing but we will hook this up with background sessions to show only one + // error notification per session. + protected showTimeoutError = debounce( + (e: Error) => { + const message = this.application.capabilities.advancedSettings?.save + ? i18n.translate('xpack.data.search.timeoutIncreaseSetting', { + defaultMessage: + 'One or more queries timed out. Increase run time with the search.timeout advanced setting.', + }) + : i18n.translate('xpack.data.search.timeoutContactAdmin', { + defaultMessage: + 'One or more queries timed out. Contact your system administrator to increase the run time.', + }); + this.deps.toasts.addError(e, { + title: 'Timed out', + toastMessage: message, + }); + }, + 60000, + { + leading: true, + } + ); } diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts index 3b05e83d208b..a1dff00ddfdd 100644 --- a/x-pack/plugins/data_enhanced/server/plugin.ts +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -18,8 +18,8 @@ import { } from '../../../../src/plugins/data/server'; import { enhancedEsSearchStrategyProvider } from './search'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; -import { ENHANCED_ES_SEARCH_STRATEGY } from '../common'; import { getUiSettings } from './ui_settings'; +import { ENHANCED_ES_SEARCH_STRATEGY } from '../common'; interface SetupDependencies { data: DataPluginSetup; 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 eda6178dc8e5..72ea1f096e8f 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 @@ -7,6 +7,7 @@ import { first } from 'rxjs/operators'; import { SearchResponse } from 'elasticsearch'; import { Observable } from 'rxjs'; +import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import { SharedGlobalConfig, RequestHandlerContext, Logger } from '../../../../../src/core/server'; import { getTotalLoaded, @@ -40,8 +41,8 @@ export const enhancedEsSearchStrategyProvider = ( try { const response = isAsync - ? await asyncSearch(context, request) - : await rollupSearch(context, request); + ? await asyncSearch(context, request, options) + : await rollupSearch(context, request, options); if ( usage && @@ -69,9 +70,10 @@ export const enhancedEsSearchStrategyProvider = ( async function asyncSearch( context: RequestHandlerContext, - request: IEnhancedEsSearchRequest + request: IEnhancedEsSearchRequest, + options?: ISearchOptions ): Promise { - let esResponse; + let promise: TransportRequestPromise; const esClient = context.core.elasticsearch.client.asCurrentUser; const uiSettingsClient = await context.core.uiSettings.client; @@ -89,14 +91,17 @@ export const enhancedEsSearchStrategyProvider = ( ...request.params, }); - esResponse = await esClient.asyncSearch.submit(submitOptions); + promise = esClient.asyncSearch.submit(submitOptions); } else { - esResponse = await esClient.asyncSearch.get({ + promise = esClient.asyncSearch.get({ id: request.id, ...toSnakeCase(asyncOptions), }); } + // Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 + if (options?.abortSignal) options.abortSignal.addEventListener('abort', () => promise.abort()); + const esResponse = await promise; const { id, response, is_partial: isPartial, is_running: isRunning } = esResponse.body; return { id, @@ -109,7 +114,8 @@ export const enhancedEsSearchStrategyProvider = ( const rollupSearch = async function ( context: RequestHandlerContext, - request: IEnhancedEsSearchRequest + request: IEnhancedEsSearchRequest, + options?: ISearchOptions ): Promise { const esClient = context.core.elasticsearch.client.asCurrentUser; const uiSettingsClient = await context.core.uiSettings.client; @@ -123,13 +129,17 @@ export const enhancedEsSearchStrategyProvider = ( ...params, }); - const esResponse = await esClient.transport.request({ + const promise = esClient.transport.request({ method, path, body, querystring, }); + // Temporary workaround until https://github.com/elastic/elasticsearch-js/issues/1297 + if (options?.abortSignal) options.abortSignal.addEventListener('abort', () => promise.abort()); + const esResponse = await promise; + const response = esResponse.body as SearchResponse; return { rawResponse: response, diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts index 6a11663ea6c3..4906d0342be8 100644 --- a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts +++ b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.test.ts @@ -54,6 +54,7 @@ describe('UrlDrilldown', () => { getGlobalScope: () => ({ kibanaUrl: 'http://localhost:5601/' }), getOpenModal: () => Promise.resolve(coreMock.createStart().overlays.openModal), getSyntaxHelpDocsLink: () => 'http://localhost:5601/docs', + getVariablesHelpDocsLink: () => 'http://localhost:5601/docs', navigateToUrl: mockNavigateToUrl, }); diff --git a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx index d5ab095fdd28..80478e6490b8 100644 --- a/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx +++ b/x-pack/plugins/embeddable_enhanced/public/drilldowns/url_drilldown/url_drilldown.tsx @@ -31,6 +31,7 @@ interface UrlDrilldownDeps { navigateToUrl: (url: string) => Promise; getOpenModal: () => Promise; getSyntaxHelpDocsLink: () => string; + getVariablesHelpDocsLink: () => string; } export type ActionContext = ChartActionContext; @@ -74,6 +75,7 @@ export class UrlDrilldown implements Drilldown ); }; diff --git a/x-pack/plugins/embeddable_enhanced/public/plugin.ts b/x-pack/plugins/embeddable_enhanced/public/plugin.ts index 37e102b40131..187db998e06e 100644 --- a/x-pack/plugins/embeddable_enhanced/public/plugin.ts +++ b/x-pack/plugins/embeddable_enhanced/public/plugin.ts @@ -75,7 +75,10 @@ export class EmbeddableEnhancedPlugin navigateToUrl: (url: string) => core.getStartServices().then(([{ application }]) => application.navigateToUrl(url)), getOpenModal: () => core.getStartServices().then(([{ overlays }]) => overlays.openModal), - getSyntaxHelpDocsLink: () => startServices().core.docLinks.links.dashboard.drilldowns, // TODO: replace with docs https://github.com/elastic/kibana/issues/69414 + getSyntaxHelpDocsLink: () => + startServices().core.docLinks.links.dashboard.urlDrilldownTemplateSyntax, + getVariablesHelpDocsLink: () => + startServices().core.docLinks.links.dashboard.urlDrilldownVariables, }) ); diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts index f8d66b8ecac2..18834f55af0a 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts @@ -555,7 +555,18 @@ describe('#bulkUpdate', () => { }); describe('namespace', () => { - const doTest = async (namespace: string, expectNamespaceInDescriptor: boolean) => { + interface TestParams { + optionsNamespace: string | undefined; + objectNamespace: string | undefined; + expectOptionsNamespaceInDescriptor: boolean; + expectObjectNamespaceInDescriptor: boolean; + } + const doTest = async ({ + optionsNamespace, + objectNamespace, + expectOptionsNamespaceInDescriptor, + expectObjectNamespaceInDescriptor, + }: TestParams) => { const docs = [ { id: 'some-id', @@ -566,12 +577,13 @@ describe('#bulkUpdate', () => { attrThree: 'three', }, version: 'some-version', + namespace: objectNamespace, }, ]; - const options = { namespace }; + const options = { namespace: optionsNamespace }; mockBaseClient.bulkUpdate.mockResolvedValue({ - saved_objects: docs.map((doc) => ({ ...doc, references: undefined })), + saved_objects: docs.map(({ namespace, ...doc }) => ({ ...doc, references: undefined })), }); await expect(wrapper.bulkUpdate(docs, options)).resolves.toEqual({ @@ -594,7 +606,11 @@ describe('#bulkUpdate', () => { { type: 'known-type', id: 'some-id', - namespace: expectNamespaceInDescriptor ? namespace : undefined, + namespace: expectObjectNamespaceInDescriptor + ? objectNamespace + : expectOptionsNamespaceInDescriptor + ? optionsNamespace + : undefined, }, { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' }, { user: mockAuthenticatedUser() } @@ -612,7 +628,7 @@ describe('#bulkUpdate', () => { attrThree: 'three', }, version: 'some-version', - + namespace: objectNamespace, references: undefined, }, ], @@ -620,13 +636,46 @@ describe('#bulkUpdate', () => { ); }; - it('uses `namespace` to encrypt attributes if it is specified when type is single-namespace', async () => { - await doTest('some-namespace', true); + it('does not use options `namespace` or object `namespace` to encrypt attributes if neither are specified', async () => { + await doTest({ + optionsNamespace: undefined, + objectNamespace: undefined, + expectOptionsNamespaceInDescriptor: false, + expectObjectNamespaceInDescriptor: false, + }); }); - it('does not use `namespace` to encrypt attributes if it is specified when type is not single-namespace', async () => { - mockBaseTypeRegistry.isSingleNamespace.mockReturnValue(false); - await doTest('some-namespace', false); + describe('with a single-namespace type', () => { + it('uses options `namespace` to encrypt attributes if it is specified and object `namespace` is not', async () => { + await doTest({ + optionsNamespace: 'some-namespace', + objectNamespace: undefined, + expectOptionsNamespaceInDescriptor: true, + expectObjectNamespaceInDescriptor: false, + }); + }); + + it('uses object `namespace` to encrypt attributes if it is specified', async () => { + // object namespace supersedes options namespace + await doTest({ + optionsNamespace: 'some-namespace', + objectNamespace: 'another-namespace', + expectOptionsNamespaceInDescriptor: false, + expectObjectNamespaceInDescriptor: true, + }); + }); + }); + + describe('with a non-single-namespace type', () => { + it('does not use object `namespace` or options `namespace` to encrypt attributes if it is specified', async () => { + mockBaseTypeRegistry.isSingleNamespace.mockReturnValue(false); + await doTest({ + optionsNamespace: 'some-namespace', + objectNamespace: 'another-namespace', + expectOptionsNamespaceInDescriptor: false, + expectObjectNamespaceInDescriptor: false, + }); + }); }); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts index a2725cbc6a27..0eeb9943b5be 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts @@ -150,14 +150,14 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon // sequential processing. const encryptedObjects = await Promise.all( objects.map(async (object) => { - const { type, id, attributes } = object; + const { type, id, attributes, namespace: objectNamespace } = object; if (!this.options.service.isRegistered(type)) { return object; } const namespace = getDescriptorNamespace( this.options.baseTypeRegistry, type, - options?.namespace + objectNamespace ?? options?.namespace ); return { ...object, diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts index b2842df909a1..7201f13fb930 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ISavedObjectTypeRegistry } from 'kibana/server'; +import { ISavedObjectTypeRegistry, SavedObjectsUtils } from '../../../../../src/core/server'; export const getDescriptorNamespace = ( typeRegistry: ISavedObjectTypeRegistry, @@ -12,5 +12,12 @@ export const getDescriptorNamespace = ( namespace?: string ) => { const descriptorNamespace = typeRegistry.isSingleNamespace(type) ? namespace : undefined; - return descriptorNamespace === 'default' ? undefined : descriptorNamespace; + return normalizeNamespace(descriptorNamespace); }; + +/** + * Ensure that a namespace is always in its namespace ID representation. + * This allows `'default'` to be used interchangeably with `undefined`. + */ +const normalizeNamespace = (namespace?: string) => + namespace === undefined ? namespace : SavedObjectsUtils.namespaceStringToId(namespace); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts index bc31b7df5d97..c52eceb2d2fd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.test.ts @@ -16,7 +16,28 @@ describe('AppLogic', () => { }); const DEFAULT_VALUES = { + account: {}, hasInitialized: false, + isFederatedAuth: true, + organization: {}, + }; + + const expectedLogicValues = { + account: { + canCreateInvitations: true, + canCreatePersonalSources: true, + groups: ['Default', 'Cats'], + id: 'some-id-string', + isAdmin: true, + isCurated: false, + viewedOnboardingPage: true, + }, + hasInitialized: true, + isFederatedAuth: false, + organization: { + defaultOrgName: 'My Organization', + name: 'ACME Donuts', + }, }; it('has expected default values', () => { @@ -27,9 +48,7 @@ describe('AppLogic', () => { it('sets values based on passed props', () => { AppLogic.actions.initializeAppData(DEFAULT_INITIAL_APP_DATA); - expect(AppLogic.values).toEqual({ - hasInitialized: true, - }); + expect(AppLogic.values).toEqual(expectedLogicValues); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts index 5bf2b41cfc26..f88a00f63f48 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/app_logic.ts @@ -7,18 +7,26 @@ import { kea, MakeLogicType } from 'kea'; import { IInitialAppData } from '../../../common/types'; -import { IWorkplaceSearchInitialData } from '../../../common/types/workplace_search'; +import { + IOrganization, + IWorkplaceSearchInitialData, + IAccount, +} from '../../../common/types/workplace_search'; export interface IAppValues extends IWorkplaceSearchInitialData { hasInitialized: boolean; + isFederatedAuth: boolean; } export interface IAppActions { - initializeAppData(props: IInitialAppData): void; + initializeAppData(props: IInitialAppData): IInitialAppData; } export const AppLogic = kea>({ actions: { - initializeAppData: ({ workplaceSearch }) => workplaceSearch, + initializeAppData: ({ workplaceSearch, isFederatedAuth }) => ({ + workplaceSearch, + isFederatedAuth, + }), }, reducers: { hasInitialized: [ @@ -27,5 +35,23 @@ export const AppLogic = kea>({ initializeAppData: () => true, }, ], + isFederatedAuth: [ + true, + { + initializeAppData: (_, { isFederatedAuth }) => !!isFederatedAuth, + }, + ], + organization: [ + {} as IOrganization, + { + initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.organization, + }, + ], + account: [ + {} as IAccount, + { + initializeAppData: (_, { workplaceSearch }) => workplaceSearch!.account, + }, + ], }, }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/index.ts index 9e86993a5289..9f281a541334 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { setMockValues, mockValues, mockActions } from './overview_logic.mock'; +export { setMockValues, mockOverviewValues, mockActions } from './overview_logic.mock'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/overview_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/overview_logic.mock.ts index 9ce3021917a2..569e6543ee86 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/overview_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/__mocks__/overview_logic.mock.ts @@ -5,19 +5,18 @@ */ import { IOverviewValues } from '../overview_logic'; -import { IAccount, IOrganization } from '../../../types'; -export const mockValues = { +import { DEFAULT_INITIAL_APP_DATA } from '../../../../../../common/__mocks__'; + +const { workplaceSearch: mockAppValues } = DEFAULT_INITIAL_APP_DATA; + +export const mockOverviewValues = { accountsCount: 0, activityFeed: [], canCreateContentSources: false, - canCreateInvitations: false, - fpAccount: {} as IAccount, hasOrgSources: false, hasUsers: false, - isFederatedAuth: true, isOldAccount: false, - organization: {} as IOrganization, pendingInvitationsCount: 0, personalSourcesCount: 0, sourcesCount: 0, @@ -28,6 +27,8 @@ export const mockActions = { initializeOverview: jest.fn(() => ({})), }; +const mockValues = { ...mockOverviewValues, ...mockAppValues, isFederatedAuth: true }; + jest.mock('kea', () => ({ ...(jest.requireActual('kea') as object), useActions: jest.fn(() => ({ ...mockActions })), @@ -37,8 +38,5 @@ jest.mock('kea', () => ({ import { useValues } from 'kea'; export const setMockValues = (values: object) => { - (useValues as jest.Mock).mockImplementationOnce(() => ({ - ...mockValues, - ...values, - })); + (useValues as jest.Mock).mockImplementation(() => ({ ...mockValues, ...values })); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx index acbc66259c2a..0f3eee074cae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.test.tsx @@ -25,6 +25,7 @@ const account = { canCreatePersonalSources: true, groups: [], isCurated: false, + canCreateInvitations: true, }; describe('OnboardingSteps', () => { @@ -60,9 +61,8 @@ describe('OnboardingSteps', () => { describe('Users & Invitations', () => { it('renders 0 users when not on federated auth', () => { setMockValues({ - canCreateInvitations: true, isFederatedAuth: false, - fpAccount: account, + account, accountsCount: 0, hasUsers: false, }); @@ -78,7 +78,7 @@ describe('OnboardingSteps', () => { it('renders completed users state', () => { setMockValues({ isFederatedAuth: false, - fpAccount: account, + account, accountsCount: 1, hasUsers: true, }); @@ -90,7 +90,13 @@ describe('OnboardingSteps', () => { }); it('disables link when the user cannot create invitations', () => { - setMockValues({ isFederatedAuth: false, canCreateInvitations: false }); + setMockValues({ + isFederatedAuth: false, + account: { + ...account, + canCreateInvitations: false, + }, + }); const wrapper = shallow(); expect(wrapper.find(OnboardingCard).last().prop('actionPath')).toBe(undefined); }); @@ -98,6 +104,12 @@ describe('OnboardingSteps', () => { describe('Org Name', () => { it('renders button to change name', () => { + setMockValues({ + organization: { + name: 'foo', + defaultOrgName: 'foo', + }, + }); const wrapper = shallow(); const button = wrapper diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx index 5598123f1c28..0baadfc912ad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/onboarding_steps.tsx @@ -28,6 +28,7 @@ import { ORG_SOURCES_PATH, USERS_PATH, ORG_SETTINGS_PATH } from '../../routes'; import { ContentSection } from '../../components/shared/content_section'; +import { AppLogic } from '../../app_logic'; import { OverviewLogic } from './overview_logic'; import { OnboardingCard } from './onboarding_card'; @@ -58,16 +59,18 @@ const ONBOARDING_USERS_CARD_DESCRIPTION = i18n.translate( ); export const OnboardingSteps: React.FC = () => { + const { + isFederatedAuth, + organization: { name, defaultOrgName }, + account: { isCurated, canCreateInvitations }, + } = useValues(AppLogic); + const { hasUsers, hasOrgSources, canCreateContentSources, - canCreateInvitations, accountsCount, sourcesCount, - fpAccount: { isCurated }, - organization: { name, defaultOrgName }, - isFederatedAuth, } = useValues(OverviewLogic); const accountsPath = diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.tsx index 4dc762e29deb..6614ac58b074 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/organization_stats.tsx @@ -14,18 +14,17 @@ import { i18n } from '@kbn/i18n'; import { ContentSection } from '../../components/shared/content_section'; import { ORG_SOURCES_PATH, USERS_PATH } from '../../routes'; +import { AppLogic } from '../../app_logic'; import { OverviewLogic } from './overview_logic'; import { StatisticCard } from './statistic_card'; export const OrganizationStats: React.FC = () => { - const { - sourcesCount, - pendingInvitationsCount, - accountsCount, - personalSourcesCount, - isFederatedAuth, - } = useValues(OverviewLogic); + const { isFederatedAuth } = useValues(AppLogic); + + const { sourcesCount, pendingInvitationsCount, accountsCount, personalSourcesCount } = useValues( + OverviewLogic + ); return ( { - const { initializeOverview } = useActions(OverviewLogic); - const { - dataLoading, - hasUsers, - hasOrgSources, - isOldAccount, organization: { name: orgName, defaultOrgName }, - } = useValues(OverviewLogic); + } = useValues(AppLogic); + + const { initializeOverview } = useActions(OverviewLogic); + const { dataLoading, hasUsers, hasOrgSources, isOldAccount } = useValues(OverviewLogic); useEffect(() => { initializeOverview(); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts index 6989635064ca..1ec770e9defc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.test.ts @@ -9,7 +9,7 @@ import { resetContext } from 'kea'; jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn() } } } })); import { HttpLogic } from '../../../shared/http'; -import { mockValues } from './__mocks__'; +import { mockOverviewValues } from './__mocks__'; import { OverviewLogic } from './overview_logic'; describe('OverviewLogic', () => { @@ -20,32 +20,19 @@ describe('OverviewLogic', () => { }); it('has expected default values', () => { - expect(OverviewLogic.values).toEqual(mockValues); + expect(OverviewLogic.values).toEqual(mockOverviewValues); }); describe('setServerData', () => { const feed = [{ foo: 'bar' }] as any; - const account = { - id: '1243', - groups: ['Default'], - isAdmin: true, - isCurated: false, - canCreatePersonalSources: true, - viewedOnboardingPage: false, - }; - const org = { name: 'ACME', defaultOrgName: 'Org' }; const data = { accountsCount: 1, activityFeed: feed, canCreateContentSources: true, - canCreateInvitations: true, - fpAccount: account, hasOrgSources: true, hasUsers: true, - isFederatedAuth: false, isOldAccount: true, - organization: org, pendingInvitationsCount: 1, personalSourcesCount: 1, sourcesCount: 1, @@ -60,10 +47,6 @@ describe('OverviewLogic', () => { }); it('will set server values', () => { - expect(OverviewLogic.values.organization).toEqual(org); - expect(OverviewLogic.values.isFederatedAuth).toEqual(false); - expect(OverviewLogic.values.fpAccount).toEqual(account); - expect(OverviewLogic.values.canCreateInvitations).toEqual(true); expect(OverviewLogic.values.hasUsers).toEqual(true); expect(OverviewLogic.values.hasOrgSources).toEqual(true); expect(OverviewLogic.values.canCreateContentSources).toEqual(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.ts index 2c6846b6db7d..787d5295db1c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/overview_logic.ts @@ -7,24 +7,18 @@ import { kea, MakeLogicType } from 'kea'; import { HttpLogic } from '../../../shared/http'; -import { IAccount, IOrganization } from '../../types'; - import { IFeedActivity } from './recent_activity'; export interface IOverviewServerData { hasUsers: boolean; hasOrgSources: boolean; canCreateContentSources: boolean; - canCreateInvitations: boolean; isOldAccount: boolean; sourcesCount: number; pendingInvitationsCount: number; accountsCount: number; personalSourcesCount: number; activityFeed: IFeedActivity[]; - organization: IOrganization; - isFederatedAuth: boolean; - fpAccount: IAccount; } export interface IOverviewActions { @@ -42,30 +36,6 @@ export const OverviewLogic = kea null, }, reducers: { - organization: [ - {} as IOrganization, - { - setServerData: (_, { organization }) => organization, - }, - ], - isFederatedAuth: [ - true, - { - setServerData: (_, { isFederatedAuth }) => isFederatedAuth, - }, - ], - fpAccount: [ - {} as IAccount, - { - setServerData: (_, { fpAccount }) => fpAccount, - }, - ], - canCreateInvitations: [ - false, - { - setServerData: (_, { canCreateInvitations }) => canCreateInvitations, - }, - ], hasUsers: [ false, { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx index 22a82af18527..31613098f9fc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.test.tsx @@ -12,6 +12,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { EuiEmptyPrompt, EuiLink } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { RecentActivity, RecentActivityItem } from './recent_activity'; @@ -61,4 +62,19 @@ describe('RecentActivity', () => { expect(wrapper.find('.activity--error__label')).toHaveLength(1); expect(wrapper.find(EuiLink).prop('color')).toEqual('danger'); }); + + it('renders recent activity message for default org name', () => { + setMockValues({ + organization: { + name: 'foo', + defaultOrgName: 'foo', + }, + }); + const wrapper = shallow(); + const emptyPrompt = wrapper.find(EuiEmptyPrompt).dive(); + + expect(emptyPrompt.find(FormattedMessage).prop('defaultMessage')).toEqual( + 'Your organization has no recent activity' + ); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx index 441f45a947a4..0813999c9a07 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/recent_activity.tsx @@ -17,6 +17,7 @@ import { sendTelemetry } from '../../../shared/telemetry'; import { KibanaContext, IKibanaContext } from '../../../index'; import { SOURCE_DETAILS_PATH, getContentSourcePath } from '../../routes'; +import { AppLogic } from '../../app_logic'; import { OverviewLogic } from './overview_logic'; import './recent_activity.scss'; @@ -32,8 +33,9 @@ export interface IFeedActivity { export const RecentActivity: React.FC = () => { const { organization: { name, defaultOrgName }, - activityFeed, - } = useValues(OverviewLogic); + } = useValues(AppLogic); + + const { activityFeed } = useValues(OverviewLogic); return ( ) {} + + public get id() { + return this.config.id; + } + + public get catalogue() { + return this.config.catalogue; + } + + public get management() { + return this.config.management; + } + + public get privileges() { + return this.config.privileges; + } + + public toRaw() { + return { ...this.config } as ElasticsearchFeatureConfig; + } +} diff --git a/x-pack/plugins/features/common/feature_elasticsearch_privileges.ts b/x-pack/plugins/features/common/feature_elasticsearch_privileges.ts new file mode 100644 index 000000000000..1100b2cc648c --- /dev/null +++ b/x-pack/plugins/features/common/feature_elasticsearch_privileges.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. + */ + +/** + * Elasticsearch Feature privilege definition + */ +export interface FeatureElasticsearchPrivileges { + /** + * A set of Elasticsearch cluster privileges which are required for this feature to be enabled. + * See https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html + * + */ + requiredClusterPrivileges: string[]; + + /** + * A set of Elasticsearch index privileges which are required for this feature to be enabled, keyed on index name or pattern. + * See https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html#privileges-list-indices + * + * @example + * + * Requiring `read` access to `logstash-*` and `all` access to `foo-*` + * ```ts + * feature.registerElasticsearchPrivilege({ + * privileges: [{ + * requiredIndexPrivileges: { + * ['logstash-*']: ['read'], + * ['foo-*]: ['all'] + * } + * }] + * }) + * ``` + * + */ + requiredIndexPrivileges?: { + [indexName: string]: string[]; + }; + + /** + * A set of Elasticsearch roles which are required for this feature to be enabled. + * + * @deprecated do not rely on hard-coded role names. + * + * This is relied on by the reporting feature, and should be removed once reporting + * migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/issues/19914 + */ + requiredRoles?: string[]; + + /** + * A list of UI Capabilities that should be granted to users with this privilege. + * These capabilities will automatically be namespaces within your feature id. + * + * @example + * ```ts + * { + * ui: ['show', 'save'] + * } + * + * This translates in the UI to the following (assuming a feature id of "foo"): + * import { uiCapabilities } from 'ui/capabilities'; + * + * const canShowApp = uiCapabilities.foo.show; + * const canSave = uiCapabilities.foo.save; + * ``` + * Note: Since these are automatically namespaced, you are free to use generic names like "show" and "save". + * + * @see UICapabilities + */ + ui: string[]; +} diff --git a/x-pack/plugins/features/common/index.ts b/x-pack/plugins/features/common/index.ts index e359efbda20d..a08de2f11871 100644 --- a/x-pack/plugins/features/common/index.ts +++ b/x-pack/plugins/features/common/index.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +export { FeatureElasticsearchPrivileges } from './feature_elasticsearch_privileges'; export { FeatureKibanaPrivileges } from './feature_kibana_privileges'; -export { Feature, FeatureConfig } from './feature'; +export { ElasticsearchFeature, ElasticsearchFeatureConfig } from './elasticsearch_feature'; +export { KibanaFeature, KibanaFeatureConfig } from './kibana_feature'; export { SubFeature, SubFeatureConfig, diff --git a/x-pack/plugins/features/common/feature.ts b/x-pack/plugins/features/common/kibana_feature.ts similarity index 92% rename from x-pack/plugins/features/common/feature.ts rename to x-pack/plugins/features/common/kibana_feature.ts index 1b700fb1a6ad..a600ada554af 100644 --- a/x-pack/plugins/features/common/feature.ts +++ b/x-pack/plugins/features/common/kibana_feature.ts @@ -6,7 +6,7 @@ import { RecursiveReadonly } from '@kbn/utility-types'; import { FeatureKibanaPrivileges } from './feature_kibana_privileges'; -import { SubFeatureConfig, SubFeature } from './sub_feature'; +import { SubFeatureConfig, SubFeature as KibanaSubFeature } from './sub_feature'; import { ReservedKibanaPrivilege } from './reserved_kibana_privilege'; /** @@ -14,7 +14,7 @@ import { ReservedKibanaPrivilege } from './reserved_kibana_privilege'; * Feature registration allows plugins to hide their applications with spaces, * and secure access when configured for security. */ -export interface FeatureConfig { +export interface KibanaFeatureConfig { /** * Unique identifier for this feature. * This identifier is also used when generating UI Capabilities. @@ -137,12 +137,12 @@ export interface FeatureConfig { }; } -export class Feature { - public readonly subFeatures: SubFeature[]; +export class KibanaFeature { + public readonly subFeatures: KibanaSubFeature[]; - constructor(protected readonly config: RecursiveReadonly) { + constructor(protected readonly config: RecursiveReadonly) { this.subFeatures = (config.subFeatures ?? []).map( - (subFeatureConfig) => new SubFeature(subFeatureConfig) + (subFeatureConfig) => new KibanaSubFeature(subFeatureConfig) ); } @@ -199,6 +199,6 @@ export class Feature { } public toRaw() { - return { ...this.config } as FeatureConfig; + return { ...this.config } as KibanaFeatureConfig; } } diff --git a/x-pack/plugins/features/public/features_api_client.ts b/x-pack/plugins/features/public/features_api_client.ts index 50cc54a197f5..cacc623aa853 100644 --- a/x-pack/plugins/features/public/features_api_client.ts +++ b/x-pack/plugins/features/public/features_api_client.ts @@ -5,13 +5,13 @@ */ import { HttpSetup } from 'src/core/public'; -import { FeatureConfig, Feature } from '.'; +import { KibanaFeatureConfig, KibanaFeature } from '.'; export class FeaturesAPIClient { constructor(private readonly http: HttpSetup) {} public async getFeatures() { - const features = await this.http.get('/api/features'); - return features.map((config) => new Feature(config)); + const features = await this.http.get('/api/features'); + return features.map((config) => new KibanaFeature(config)); } } diff --git a/x-pack/plugins/features/public/index.ts b/x-pack/plugins/features/public/index.ts index f19c7f947d97..7d86312e466e 100644 --- a/x-pack/plugins/features/public/index.ts +++ b/x-pack/plugins/features/public/index.ts @@ -8,8 +8,8 @@ import { PluginInitializer } from 'src/core/public'; import { FeaturesPlugin, FeaturesPluginSetup, FeaturesPluginStart } from './plugin'; export { - Feature, - FeatureConfig, + KibanaFeature, + KibanaFeatureConfig, FeatureKibanaPrivileges, SubFeatureConfig, SubFeaturePrivilegeConfig, diff --git a/x-pack/plugins/features/server/__snapshots__/feature_registry.test.ts.snap b/x-pack/plugins/features/server/__snapshots__/feature_registry.test.ts.snap index e033b241f9e2..fdeb53dd2fa1 100644 --- a/x-pack/plugins/features/server/__snapshots__/feature_registry.test.ts.snap +++ b/x-pack/plugins/features/server/__snapshots__/feature_registry.test.ts.snap @@ -1,27 +1,27 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`FeatureRegistry prevents features from being registered with a catalogue entry of "" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" is not allowed to be empty]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a catalogue entry of "" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" is not allowed to be empty]]"`; -exports[`FeatureRegistry prevents features from being registered with a catalogue entry of "contains space" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a catalogue entry of "contains space" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]"`; -exports[`FeatureRegistry prevents features from being registered with a catalogue entry of "contains_invalid()_chars" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a catalogue entry of "contains_invalid()_chars" 1`] = `"child \\"catalogue\\" fails because [\\"catalogue\\" at position 0 fails because [\\"0\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]"`; -exports[`FeatureRegistry prevents features from being registered with a management id of "" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" is not allowed to be empty]]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a management id of "" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" is not allowed to be empty]]]"`; -exports[`FeatureRegistry prevents features from being registered with a management id of "contains space" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a management id of "contains space" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]]"`; -exports[`FeatureRegistry prevents features from being registered with a management id of "contains_invalid()_chars" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a management id of "contains_invalid()_chars" 1`] = `"child \\"management\\" fails because [child \\"kibana\\" fails because [\\"kibana\\" at position 0 fails because [\\"0\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]]]"`; -exports[`FeatureRegistry prevents features from being registered with a navLinkId of "" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" is not allowed to be empty]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a navLinkId of "" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" is not allowed to be empty]"`; -exports[`FeatureRegistry prevents features from being registered with a navLinkId of "contains space" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a navLinkId of "contains space" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" with value \\"contains space\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]"`; -exports[`FeatureRegistry prevents features from being registered with a navLinkId of "contains_invalid()_chars" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with a navLinkId of "contains_invalid()_chars" 1`] = `"child \\"navLinkId\\" fails because [\\"navLinkId\\" with value \\"contains_invalid()_chars\\" fails to match the required pattern: /^[a-zA-Z0-9:_-]+$/]"`; -exports[`FeatureRegistry prevents features from being registered with an ID of "catalogue" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with an ID of "catalogue" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; -exports[`FeatureRegistry prevents features from being registered with an ID of "doesn't match valid regex" 1`] = `"child \\"id\\" fails because [\\"id\\" with value \\"doesn't match valid regex\\" fails to match the required pattern: /^[a-zA-Z0-9_-]+$/]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with an ID of "doesn't match valid regex" 1`] = `"child \\"id\\" fails because [\\"id\\" with value \\"doesn't match valid regex\\" fails to match the required pattern: /^[a-zA-Z0-9_-]+$/]"`; -exports[`FeatureRegistry prevents features from being registered with an ID of "management" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with an ID of "management" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; -exports[`FeatureRegistry prevents features from being registered with an ID of "navLinks" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; +exports[`FeatureRegistry Kibana Features prevents features from being registered with an ID of "navLinks" 1`] = `"child \\"id\\" fails because [\\"id\\" contains an invalid value]"`; diff --git a/x-pack/plugins/features/server/feature_registry.test.ts b/x-pack/plugins/features/server/feature_registry.test.ts index f123068e4175..24aae3a69ee5 100644 --- a/x-pack/plugins/features/server/feature_registry.test.ts +++ b/x-pack/plugins/features/server/feature_registry.test.ts @@ -5,1192 +5,1389 @@ */ import { FeatureRegistry } from './feature_registry'; -import { FeatureConfig } from '../common/feature'; +import { ElasticsearchFeatureConfig, KibanaFeatureConfig } from '../common'; describe('FeatureRegistry', () => { - it('allows a minimal feature to be registered', () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: null, - }; + describe('Kibana Features', () => { + it('allows a minimal feature to be registered', () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + }; - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); - expect(result).toHaveLength(1); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); + expect(result).toHaveLength(1); - // Should be the equal, but not the same instance (i.e., a defensive copy) - expect(result[0].toRaw()).not.toBe(feature); - expect(result[0].toRaw()).toEqual(feature); - }); + // Should be the equal, but not the same instance (i.e., a defensive copy) + expect(result[0].toRaw()).not.toBe(feature); + expect(result[0].toRaw()).toEqual(feature); + }); - it('allows a complex feature to be registered', () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - excludeFromBasePrivileges: true, - icon: 'addDataApp', - navLinkId: 'someNavLink', - app: ['app1'], - validLicenses: ['standard', 'basic', 'gold', 'platinum'], - catalogue: ['foo'], - management: { - foo: ['bar'], - }, - privileges: { - all: { - catalogue: ['foo'], - management: { - foo: ['bar'], - }, - app: ['app1'], - savedObject: { - all: ['space', 'etc', 'telemetry'], - read: ['canvas', 'config', 'url'], - }, - api: ['someApiEndpointTag', 'anotherEndpointTag'], - ui: ['allowsFoo', 'showBar', 'showBaz'], + it('allows a complex feature to be registered', () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + excludeFromBasePrivileges: true, + icon: 'addDataApp', + navLinkId: 'someNavLink', + app: ['app1'], + validLicenses: ['standard', 'basic', 'gold', 'platinum'], + catalogue: ['foo'], + management: { + foo: ['bar'], }, - read: { - savedObject: { - all: [], - read: ['config', 'url'], + privileges: { + all: { + catalogue: ['foo'], + management: { + foo: ['bar'], + }, + app: ['app1'], + savedObject: { + all: ['space', 'etc', 'telemetry'], + read: ['canvas', 'config', 'url'], + }, + api: ['someApiEndpointTag', 'anotherEndpointTag'], + ui: ['allowsFoo', 'showBar', 'showBaz'], + }, + read: { + savedObject: { + all: [], + read: ['config', 'url'], + }, + ui: [], }, - ui: [], }, - }, - subFeatures: [ - { - name: 'sub-feature-1', - privilegeGroups: [ - { - groupType: 'independent', - privileges: [ - { - id: 'foo', - name: 'foo', - includeIn: 'read', - savedObject: { - all: [], - read: [], + subFeatures: [ + { + name: 'sub-feature-1', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'foo', + name: 'foo', + includeIn: 'read', + savedObject: { + all: [], + read: [], + }, + ui: [], }, - ui: [], - }, - ], - }, - { - groupType: 'mutually_exclusive', - privileges: [ - { - id: 'bar', - name: 'bar', - includeIn: 'all', - savedObject: { - all: [], - read: [], + ], + }, + { + groupType: 'mutually_exclusive', + privileges: [ + { + id: 'bar', + name: 'bar', + includeIn: 'all', + savedObject: { + all: [], + read: [], + }, + ui: [], }, - ui: [], - }, - { - id: 'baz', - name: 'baz', - includeIn: 'none', - savedObject: { - all: [], - read: [], + { + id: 'baz', + name: 'baz', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], }, - ui: [], + ], + }, + ], + }, + ], + privilegesTooltip: 'some fancy tooltip', + reserved: { + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['foo'], + management: { + foo: ['bar'], }, - ], + app: ['app1'], + savedObject: { + all: ['space', 'etc', 'telemetry'], + read: ['canvas', 'config', 'url'], + }, + api: ['someApiEndpointTag', 'anotherEndpointTag'], + ui: ['allowsFoo', 'showBar', 'showBaz'], + }, }, ], + description: 'some completely adequate description', }, - ], - privilegesTooltip: 'some fancy tooltip', - reserved: { - privileges: [ - { - id: 'reserved', - privilege: { - catalogue: ['foo'], - management: { - foo: ['bar'], - }, - app: ['app1'], - savedObject: { - all: ['space', 'etc', 'telemetry'], - read: ['canvas', 'config', 'url'], - }, - api: ['someApiEndpointTag', 'anotherEndpointTag'], - ui: ['allowsFoo', 'showBar', 'showBaz'], - }, - }, - ], - description: 'some completely adequate description', - }, - }; + }; - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); - expect(result).toHaveLength(1); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); + expect(result).toHaveLength(1); - // Should be the equal, but not the same instance (i.e., a defensive copy) - expect(result[0].toRaw()).not.toBe(feature); - expect(result[0].toRaw()).toEqual(feature); - }); + // Should be the equal, but not the same instance (i.e., a defensive copy) + expect(result[0].toRaw()).not.toBe(feature); + expect(result[0].toRaw()).toEqual(feature); + }); - it(`requires a value for privileges`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - } as any; + it(`requires a value for privileges`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + } as any; - const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"child \\"privileges\\" fails because [\\"privileges\\" is required]"` - ); - }); + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"child \\"privileges\\" fails because [\\"privileges\\" is required]"` + ); + }); - it(`does not allow sub-features to be registered when no primary privileges are not registered`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: null, - subFeatures: [ - { - name: 'my sub feature', - privilegeGroups: [ - { - groupType: 'independent', - privileges: [ - { - id: 'my-sub-priv', - name: 'my sub priv', - includeIn: 'none', - savedObject: { - all: [], - read: [], + it(`does not allow sub-features to be registered when no primary privileges are not registered`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + subFeatures: [ + { + name: 'my sub feature', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'my-sub-priv', + name: 'my sub priv', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], }, - ui: [], - }, - ], - }, - ], - }, - ], - }; + ], + }, + ], + }, + ], + }; - const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"child \\"subFeatures\\" fails because [\\"subFeatures\\" must contain less than or equal to 0 items]"` - ); - }); + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"child \\"subFeatures\\" fails because [\\"subFeatures\\" must contain less than or equal to 0 items]"` + ); + }); - it(`automatically grants 'all' access to telemetry saved objects for the 'all' privilege`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: { - all: { - ui: [], - savedObject: { - all: [], - read: [], + it(`automatically grants 'all' access to telemetry saved objects for the 'all' privilege`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: { + all: { + ui: [], + savedObject: { + all: [], + read: [], + }, }, - }, - read: { - ui: [], - savedObject: { - all: [], - read: [], + read: { + ui: [], + savedObject: { + all: [], + read: [], + }, }, }, - }, - }; + }; - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); - expect(result[0].privileges).toHaveProperty('all'); - expect(result[0].privileges).toHaveProperty('read'); + expect(result[0].privileges).toHaveProperty('all'); + expect(result[0].privileges).toHaveProperty('read'); - const allPrivilege = result[0].privileges?.all; - expect(allPrivilege?.savedObject.all).toEqual(['telemetry']); - }); + const allPrivilege = result[0].privileges?.all; + expect(allPrivilege?.savedObject.all).toEqual(['telemetry']); + }); - it(`automatically grants 'read' access to config and url saved objects for both privileges`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: { - all: { - ui: [], - savedObject: { - all: [], - read: [], + it(`automatically grants 'read' access to config and url saved objects for both privileges`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: { + all: { + ui: [], + savedObject: { + all: [], + read: [], + }, }, - }, - read: { - ui: [], - savedObject: { - all: [], - read: [], + read: { + ui: [], + savedObject: { + all: [], + read: [], + }, }, }, - }, - }; + }; - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); - expect(result[0].privileges).toHaveProperty('all'); - expect(result[0].privileges).toHaveProperty('read'); + expect(result[0].privileges).toHaveProperty('all'); + expect(result[0].privileges).toHaveProperty('read'); - const allPrivilege = result[0].privileges?.all; - const readPrivilege = result[0].privileges?.read; - expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']); - expect(readPrivilege?.savedObject.read).toEqual(['config', 'url']); - }); + const allPrivilege = result[0].privileges?.all; + const readPrivilege = result[0].privileges?.read; + expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']); + expect(readPrivilege?.savedObject.read).toEqual(['config', 'url']); + }); - it(`automatically grants 'all' access to telemetry and 'read' to [config, url] saved objects for the reserved privilege`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: null, - reserved: { - description: 'foo', - privileges: [ - { - id: 'reserved', - privilege: { - ui: [], - savedObject: { - all: [], - read: [], + it(`automatically grants 'all' access to telemetry and 'read' to [config, url] saved objects for the reserved privilege`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved', + privilege: { + ui: [], + savedObject: { + all: [], + read: [], + }, }, }, - }, - ], - }, - }; + ], + }, + }; - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); - const reservedPrivilege = result[0]!.reserved!.privileges[0].privilege; - expect(reservedPrivilege.savedObject.all).toEqual(['telemetry']); - expect(reservedPrivilege.savedObject.read).toEqual(['config', 'url']); - }); + const reservedPrivilege = result[0]!.reserved!.privileges[0].privilege; + expect(reservedPrivilege.savedObject.all).toEqual(['telemetry']); + expect(reservedPrivilege.savedObject.read).toEqual(['config', 'url']); + }); - it(`does not duplicate the automatic grants if specified on the incoming feature`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: { - all: { - ui: [], - savedObject: { - all: ['telemetry'], - read: ['config', 'url'], + it(`does not duplicate the automatic grants if specified on the incoming feature`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: { + all: { + ui: [], + savedObject: { + all: ['telemetry'], + read: ['config', 'url'], + }, }, - }, - read: { - ui: [], - savedObject: { - all: [], - read: ['config', 'url'], + read: { + ui: [], + savedObject: { + all: [], + read: ['config', 'url'], + }, }, }, - }, - }; - - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); + }; - expect(result[0].privileges).toHaveProperty('all'); - expect(result[0].privileges).toHaveProperty('read'); - - const allPrivilege = result[0].privileges!.all; - const readPrivilege = result[0].privileges!.read; - expect(allPrivilege?.savedObject.all).toEqual(['telemetry']); - expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']); - expect(readPrivilege?.savedObject.read).toEqual(['config', 'url']); - }); - - it(`does not allow duplicate features to be registered`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: null, - }; + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); - const duplicateFeature: FeatureConfig = { - id: 'test-feature', - name: 'Duplicate Test Feature', - app: [], - privileges: null, - }; + expect(result[0].privileges).toHaveProperty('all'); + expect(result[0].privileges).toHaveProperty('read'); - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); + const allPrivilege = result[0].privileges!.all; + const readPrivilege = result[0].privileges!.read; + expect(allPrivilege?.savedObject.all).toEqual(['telemetry']); + expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']); + expect(readPrivilege?.savedObject.read).toEqual(['config', 'url']); + }); - expect(() => featureRegistry.register(duplicateFeature)).toThrowErrorMatchingInlineSnapshot( - `"Feature with id test-feature is already registered."` - ); - }); + it(`does not allow duplicate features to be registered`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + }; + + const duplicateFeature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Duplicate Test Feature', + app: [], + privileges: null, + }; - ['contains space', 'contains_invalid()_chars', ''].forEach((prohibitedChars) => { - it(`prevents features from being registered with a navLinkId of "${prohibitedChars}"`, () => { const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + expect(() => - featureRegistry.register({ - id: 'foo', - name: 'some feature', - navLinkId: prohibitedChars, - app: [], - privileges: null, - }) - ).toThrowErrorMatchingSnapshot(); + featureRegistry.registerKibanaFeature(duplicateFeature) + ).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`); }); - it(`prevents features from being registered with a management id of "${prohibitedChars}"`, () => { - const featureRegistry = new FeatureRegistry(); - expect(() => - featureRegistry.register({ - id: 'foo', - name: 'some feature', - management: { - kibana: [prohibitedChars], - }, - app: [], - privileges: null, - }) - ).toThrowErrorMatchingSnapshot(); + ['contains space', 'contains_invalid()_chars', ''].forEach((prohibitedChars) => { + it(`prevents features from being registered with a navLinkId of "${prohibitedChars}"`, () => { + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature({ + id: 'foo', + name: 'some feature', + navLinkId: prohibitedChars, + app: [], + privileges: null, + }) + ).toThrowErrorMatchingSnapshot(); + }); + + it(`prevents features from being registered with a management id of "${prohibitedChars}"`, () => { + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature({ + id: 'foo', + name: 'some feature', + management: { + kibana: [prohibitedChars], + }, + app: [], + privileges: null, + }) + ).toThrowErrorMatchingSnapshot(); + }); + + it(`prevents features from being registered with a catalogue entry of "${prohibitedChars}"`, () => { + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature({ + id: 'foo', + name: 'some feature', + catalogue: [prohibitedChars], + app: [], + privileges: null, + }) + ).toThrowErrorMatchingSnapshot(); + }); }); - it(`prevents features from being registered with a catalogue entry of "${prohibitedChars}"`, () => { - const featureRegistry = new FeatureRegistry(); - expect(() => - featureRegistry.register({ - id: 'foo', - name: 'some feature', - catalogue: [prohibitedChars], - app: [], - privileges: null, - }) - ).toThrowErrorMatchingSnapshot(); + ['catalogue', 'management', 'navLinks', `doesn't match valid regex`].forEach((prohibitedId) => { + it(`prevents features from being registered with an ID of "${prohibitedId}"`, () => { + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature({ + id: prohibitedId, + name: 'some feature', + app: [], + privileges: null, + }) + ).toThrowErrorMatchingSnapshot(); + }); }); - }); - ['catalogue', 'management', 'navLinks', `doesn't match valid regex`].forEach((prohibitedId) => { - it(`prevents features from being registered with an ID of "${prohibitedId}"`, () => { + it('prevents features from being registered with invalid privilege names', () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: ['app1', 'app2'], + privileges: { + foo: { + name: 'Foo', + app: ['app1', 'app2'], + savedObject: { + all: ['config', 'space', 'etc'], + read: ['canvas'], + }, + api: ['someApiEndpointTag', 'anotherEndpointTag'], + ui: ['allowsFoo', 'showBar', 'showBaz'], + }, + } as any, + }; + const featureRegistry = new FeatureRegistry(); expect(() => - featureRegistry.register({ - id: prohibitedId, - name: 'some feature', - app: [], - privileges: null, - }) - ).toThrowErrorMatchingSnapshot(); + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"child \\"privileges\\" fails because [\\"foo\\" is not allowed]"` + ); }); - }); - it('prevents features from being registered with invalid privilege names', () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: ['app1', 'app2'], - privileges: { - foo: { - name: 'Foo', - app: ['app1', 'app2'], - savedObject: { - all: ['config', 'space', 'etc'], - read: ['canvas'], + it(`prevents privileges from specifying app entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: ['bar'], + privileges: { + all: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo', 'bar', 'baz'], + }, + read: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo', 'bar', 'baz'], }, - api: ['someApiEndpointTag', 'anotherEndpointTag'], - ui: ['allowsFoo', 'showBar', 'showBaz'], }, - } as any, - }; + }; - const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"child \\"privileges\\" fails because [\\"foo\\" is not allowed]"` - ); - }); + const featureRegistry = new FeatureRegistry(); - it(`prevents privileges from specifying app entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: ['bar'], - privileges: { - all: { - savedObject: { - all: [], - read: [], + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.all has unknown app entries: foo, baz"` + ); + }); + + it(`prevents features from specifying app entries that don't exist at the privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: ['foo', 'bar', 'baz'], + privileges: { + all: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['bar'], }, - ui: [], - app: ['foo', 'bar', 'baz'], - }, - read: { - savedObject: { - all: [], - read: [], + read: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: ['foo', 'bar', 'baz'], }, - }, - }; + subFeatures: [ + { + name: 'my sub feature', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cool-sub-feature-privilege', + name: 'cool privilege', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo'], + }, + ], + }, + ], + }, + ], + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.all has unknown app entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies app entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying app entries that don't exist at the privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: ['foo', 'bar', 'baz'], - privileges: { - all: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: ['bar'], - }, - read: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - }, - subFeatures: [ - { - name: 'my sub feature', - privilegeGroups: [ + it(`prevents reserved privileges from specifying app entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: ['bar'], + privileges: null, + reserved: { + description: 'something', + privileges: [ { - groupType: 'independent', - privileges: [ - { - id: 'cool-sub-feature-privilege', - name: 'cool privilege', - includeIn: 'none', - savedObject: { - all: [], - read: [], - }, - ui: [], - app: ['foo'], + id: 'reserved', + privilege: { + savedObject: { + all: [], + read: [], }, - ], + ui: [], + app: ['foo', 'bar', 'baz'], + }, }, ], }, - ], - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies app entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.reserved has unknown app entries: foo, baz"` + ); + }); - it(`prevents reserved privileges from specifying app entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: ['bar'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - savedObject: { - all: [], - read: [], + it(`prevents features from specifying app entries that don't exist at the reserved privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: ['foo', 'bar', 'baz'], + privileges: null, + reserved: { + description: 'something', + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo', 'bar'], }, - ui: [], - app: ['foo', 'bar', 'baz'], }, - }, - ], - }, - }; + ], + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.reserved has unknown app entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies app entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying app entries that don't exist at the reserved privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: ['foo', 'bar', 'baz'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: ['foo', 'bar'], + it(`prevents privileges from specifying catalogue entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + privileges: { + all: { + catalogue: ['foo', 'bar', 'baz'], + savedObject: { + all: [], + read: [], }, + ui: [], + app: [], }, - ], - }, - }; + read: { + catalogue: ['foo', 'bar', 'baz'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies app entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.all has unknown catalogue entries: foo, baz"` + ); + }); - it(`prevents privileges from specifying catalogue entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - privileges: { - all: { - catalogue: ['foo', 'bar', 'baz'], - savedObject: { - all: [], - read: [], + it(`prevents features from specifying catalogue entries that don't exist at the privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['foo', 'bar', 'baz'], + privileges: { + all: { + catalogue: ['foo'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], - }, - read: { - catalogue: ['foo', 'bar', 'baz'], - savedObject: { - all: [], - read: [], + read: { + catalogue: ['foo'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], }, - }, - }; + subFeatures: [ + { + name: 'my sub feature', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cool-sub-feature-privilege', + name: 'cool privilege', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], + catalogue: ['bar'], + }, + ], + }, + ], + }, + ], + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.all has unknown catalogue entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying catalogue entries that don't exist at the privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['foo', 'bar', 'baz'], - privileges: { - all: { - catalogue: ['foo'], - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - read: { - catalogue: ['foo'], - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - }, - subFeatures: [ - { - name: 'my sub feature', - privilegeGroups: [ + it(`prevents reserved privileges from specifying catalogue entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + privileges: null, + reserved: { + description: 'something', + privileges: [ { - groupType: 'independent', - privileges: [ - { - id: 'cool-sub-feature-privilege', - name: 'cool privilege', - includeIn: 'none', - savedObject: { - all: [], - read: [], - }, - ui: [], - catalogue: ['bar'], + id: 'reserved', + privilege: { + catalogue: ['foo', 'bar', 'baz'], + savedObject: { + all: [], + read: [], }, - ], + ui: [], + app: [], + }, }, ], }, - ], - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.reserved has unknown catalogue entries: foo, baz"` + ); + }); - it(`prevents reserved privileges from specifying catalogue entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - catalogue: ['foo', 'bar', 'baz'], - savedObject: { - all: [], - read: [], + it(`prevents features from specifying catalogue entries that don't exist at the reserved privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['foo', 'bar', 'baz'], + privileges: null, + reserved: { + description: 'something', + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['foo', 'bar'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], }, - }, - ], - }, - }; + ], + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.reserved has unknown catalogue entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying catalogue entries that don't exist at the reserved privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['foo', 'bar', 'baz'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - catalogue: ['foo', 'bar'], - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], + it(`prevents privileges from specifying alerting entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + alerting: ['bar'], + privileges: { + all: { + alerting: { + all: ['foo', 'bar'], + read: ['baz'], + }, + savedObject: { + all: [], + read: [], }, + ui: [], + app: [], }, - ], - }, - }; + read: { + alerting: { read: ['foo', 'bar', 'baz'] }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.all has unknown alerting entries: foo, baz"` + ); + }); - it(`prevents privileges from specifying alerting entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - alerting: ['bar'], - privileges: { - all: { - alerting: { - all: ['foo', 'bar'], - read: ['baz'], + it(`prevents features from specifying alerting entries that don't exist at the privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + alerting: ['foo', 'bar', 'baz'], + privileges: { + all: { + alerting: { all: ['foo'] }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - savedObject: { - all: [], - read: [], + read: { + alerting: { all: ['foo'] }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], }, - read: { - alerting: { read: ['foo', 'bar', 'baz'] }, - savedObject: { - all: [], - read: [], + subFeatures: [ + { + name: 'my sub feature', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cool-sub-feature-privilege', + name: 'cool privilege', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], + alerting: { all: ['bar'] }, + }, + ], + }, + ], }, - ui: [], - app: [], - }, - }, - }; + ], + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.all has unknown alerting entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying alerting entries that don't exist at the privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - alerting: ['foo', 'bar', 'baz'], - privileges: { - all: { - alerting: { all: ['foo'] }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - read: { - alerting: { all: ['foo'] }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - }, - subFeatures: [ - { - name: 'my sub feature', - privilegeGroups: [ + it(`prevents reserved privileges from specifying alerting entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + alerting: ['bar'], + privileges: null, + reserved: { + description: 'something', + privileges: [ { - groupType: 'independent', - privileges: [ - { - id: 'cool-sub-feature-privilege', - name: 'cool privilege', - includeIn: 'none', - savedObject: { - all: [], - read: [], - }, - ui: [], - alerting: { all: ['bar'] }, + id: 'reserved', + privilege: { + alerting: { all: ['foo', 'bar', 'baz'] }, + savedObject: { + all: [], + read: [], }, - ], + ui: [], + app: [], + }, }, ], }, - ], - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.reserved has unknown alerting entries: foo, baz"` + ); + }); - it(`prevents reserved privileges from specifying alerting entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - alerting: ['bar'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - alerting: { all: ['foo', 'bar', 'baz'] }, - savedObject: { - all: [], - read: [], + it(`prevents features from specifying alerting entries that don't exist at the reserved privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + alerting: ['foo', 'bar', 'baz'], + privileges: null, + reserved: { + description: 'something', + privileges: [ + { + id: 'reserved', + privilege: { + alerting: { all: ['foo', 'bar'] }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], }, - }, - ], - }, - }; + ], + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.reserved has unknown alerting entries: foo, baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"` + ); + }); - it(`prevents features from specifying alerting entries that don't exist at the reserved privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - alerting: ['foo', 'bar', 'baz'], - privileges: null, - reserved: { - description: 'something', - privileges: [ - { - id: 'reserved', - privilege: { - alerting: { all: ['foo', 'bar'] }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], + it(`prevents privileges from specifying management sections that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + management: { + kibana: ['hey'], + }, + privileges: { + all: { + catalogue: ['bar'], + management: { + elasticsearch: ['hey'], + }, + savedObject: { + all: [], + read: [], }, + ui: [], + app: [], }, - ], - }, - }; + read: { + catalogue: ['bar'], + management: { + elasticsearch: ['hey'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.all has unknown management section: elasticsearch"` + ); + }); - it(`prevents privileges from specifying management sections that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - management: { - kibana: ['hey'], - }, - privileges: { - all: { - catalogue: ['bar'], - management: { - elasticsearch: ['hey'], + it(`prevents features from specifying management sections that don't exist at the privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + management: { + kibana: ['hey'], + elasticsearch: ['hey', 'there'], + }, + privileges: { + all: { + catalogue: ['bar'], + management: { + elasticsearch: ['hey'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - savedObject: { - all: [], - read: [], + read: { + catalogue: ['bar'], + management: { + elasticsearch: ['hey'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], }, - ui: [], - app: [], }, - read: { - catalogue: ['bar'], - management: { - elasticsearch: ['hey'], - }, - savedObject: { - all: [], - read: [], + subFeatures: [ + { + name: 'my sub feature', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'cool-sub-feature-privilege', + name: 'cool privilege', + includeIn: 'none', + savedObject: { + all: [], + read: [], + }, + ui: [], + management: { + kibana: ['hey'], + elasticsearch: ['hey'], + }, + }, + ], + }, + ], }, - ui: [], - app: [], + ], + }; + + const featureRegistry = new FeatureRegistry(); + + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies management entries which are not granted to any privileges: elasticsearch.there"` + ); + }); + + it(`prevents reserved privileges from specifying management entries that don't exist at the root level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + management: { + kibana: ['hey'], }, - }, - }; + privileges: null, + reserved: { + description: 'something', + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['bar'], + management: { + kibana: ['hey-there'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + ], + }, + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.all has unknown management section: elasticsearch"` - ); - }); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature privilege test-feature.reserved has unknown management entries for section kibana: hey-there"` + ); + }); - it(`prevents features from specifying management sections that don't exist at the privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - management: { - kibana: ['hey'], - elasticsearch: ['hey', 'there'], - }, - privileges: { - all: { - catalogue: ['bar'], - management: { - elasticsearch: ['hey'], - }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], + it(`prevents features from specifying management entries that don't exist at the reserved privilege level`, () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + catalogue: ['bar'], + management: { + kibana: ['hey', 'hey-there'], }, - read: { - catalogue: ['bar'], - management: { - elasticsearch: ['hey'], - }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], + privileges: null, + reserved: { + description: 'something', + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['bar'], + management: { + kibana: ['hey-there'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + ], }, - }, - subFeatures: [ - { - name: 'my sub feature', - privilegeGroups: [ + }; + + const featureRegistry = new FeatureRegistry(); + + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature specifies management entries which are not granted to any privileges: kibana.hey"` + ); + }); + + it('allows multiple reserved feature privileges to be registered', () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + reserved: { + description: 'my reserved privileges', + privileges: [ { - groupType: 'independent', - privileges: [ - { - id: 'cool-sub-feature-privilege', - name: 'cool privilege', - includeIn: 'none', - savedObject: { - all: [], - read: [], - }, - ui: [], - management: { - kibana: ['hey'], - elasticsearch: ['hey'], - }, + id: 'a_reserved_1', + privilege: { + savedObject: { + all: [], + read: [], }, - ], + ui: [], + app: [], + }, + }, + { + id: 'a_reserved_2', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, }, ], }, - ], - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature); + const result = featureRegistry.getAllKibanaFeatures(); + expect(result).toHaveLength(1); + expect(result[0].reserved?.privileges).toHaveLength(2); + }); + + it('does not allow reserved privilege ids to start with "reserved_"', () => { + const feature: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + reserved: { + description: 'my reserved privileges', + privileges: [ + { + id: 'reserved_1', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + ], + }, + }; - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies management entries which are not granted to any privileges: elasticsearch.there"` - ); + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerKibanaFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"child \\"reserved\\" fails because [child \\"privileges\\" fails because [\\"privileges\\" at position 0 fails because [child \\"id\\" fails because [\\"id\\" with value \\"reserved_1\\" fails to match the required pattern: /^(?!reserved_)[a-zA-Z0-9_-]+$/]]]]"` + ); + }); + + it('cannot register feature after getAll has been called', () => { + const feature1: KibanaFeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + }; + const feature2: KibanaFeatureConfig = { + id: 'test-feature-2', + name: 'Test Feature 2', + app: [], + privileges: null, + }; + + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerKibanaFeature(feature1); + featureRegistry.getAllKibanaFeatures(); + expect(() => { + featureRegistry.registerKibanaFeature(feature2); + }).toThrowErrorMatchingInlineSnapshot( + `"Features are locked, can't register new features. Attempt to register test-feature-2 failed."` + ); + }); }); - it(`prevents reserved privileges from specifying management entries that don't exist at the root level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - management: { - kibana: ['hey'], - }, - privileges: null, - reserved: { - description: 'something', + describe('Elasticsearch Features', () => { + it('allows a minimal feature to be registered', () => { + const feature: ElasticsearchFeatureConfig = { + id: 'test-feature', privileges: [ { - id: 'reserved', - privilege: { - catalogue: ['bar'], - management: { - kibana: ['hey-there'], - }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, + requiredClusterPrivileges: ['all'], + ui: [], }, ], - }, - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerElasticsearchFeature(feature); + const result = featureRegistry.getAllElasticsearchFeatures(); + expect(result).toHaveLength(1); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature privilege test-feature.reserved has unknown management entries for section kibana: hey-there"` - ); - }); + // Should be the equal, but not the same instance (i.e., a defensive copy) + expect(result[0].toRaw()).not.toBe(feature); + expect(result[0].toRaw()).toEqual(feature); + }); - it(`prevents features from specifying management entries that don't exist at the reserved privilege level`, () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - catalogue: ['bar'], - management: { - kibana: ['hey', 'hey-there'], - }, - privileges: null, - reserved: { - description: 'something', + it('allows a complex feature to ge registered', () => { + const feature: ElasticsearchFeatureConfig = { + id: 'test-feature', + management: { + kibana: ['foo'], + data: ['bar'], + }, + catalogue: ['foo', 'bar'], privileges: [ { - id: 'reserved', - privilege: { - catalogue: ['bar'], - management: { - kibana: ['hey-there'], - }, - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], + requiredClusterPrivileges: ['monitor', 'manage'], + requiredIndexPrivileges: { + foo: ['read'], + bar: ['all'], + baz: ['view_index_metadata'], }, + ui: ['ui_a'], + }, + { + requiredClusterPrivileges: [], + requiredRoles: ['some_role'], + ui: ['ui_b'], }, ], - }, - }; + }; - const featureRegistry = new FeatureRegistry(); + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerElasticsearchFeature(feature); + const result = featureRegistry.getAllElasticsearchFeatures(); + expect(result).toHaveLength(1); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"Feature test-feature specifies management entries which are not granted to any privileges: kibana.hey"` - ); - }); + // Should be the equal, but not the same instance (i.e., a defensive copy) + expect(result[0].toRaw()).not.toBe(feature); + expect(result[0].toRaw()).toEqual(feature); + }); - it('allows multiple reserved feature privileges to be registered', () => { - const feature: FeatureConfig = { - id: 'test-feature', - name: 'Test Feature', - app: [], - privileges: null, - reserved: { - description: 'my reserved privileges', + it('requires a value for privileges', () => { + const feature: ElasticsearchFeatureConfig = { + id: 'test-feature', + } as any; + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerElasticsearchFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"child \\"privileges\\" fails because [\\"privileges\\" is required]"` + ); + }); + + it('requires privileges to declare some form of required es privileges', () => { + const feature: ElasticsearchFeatureConfig = { + id: 'test-feature', privileges: [ { - id: 'a_reserved_1', - privilege: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, + ui: [], }, + ], + } as any; + const featureRegistry = new FeatureRegistry(); + expect(() => + featureRegistry.registerElasticsearchFeature(feature) + ).toThrowErrorMatchingInlineSnapshot( + `"Feature test-feature has a privilege definition at index 0 without any privileges defined."` + ); + }); + + it('does not allow duplicate privilege ids', () => { + const feature: ElasticsearchFeatureConfig = { + id: 'test-feature', + privileges: [ { - id: 'a_reserved_2', - privilege: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, + requiredClusterPrivileges: ['all'], + ui: [], }, ], - }, - }; - - const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature); - const result = featureRegistry.getAll(); - expect(result).toHaveLength(1); - expect(result[0].reserved?.privileges).toHaveLength(2); + }; + const featureRegistry = new FeatureRegistry(); + featureRegistry.registerElasticsearchFeature(feature); + expect(() => + featureRegistry.registerElasticsearchFeature(feature) + ).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`); + }); }); - it('does not allow reserved privilege ids to start with "reserved_"', () => { - const feature: FeatureConfig = { + it('does not allow a Kibana feature to share an id with an Elasticsearch feature', () => { + const kibanaFeature: KibanaFeatureConfig = { id: 'test-feature', name: 'Test Feature', app: [], privileges: null, - reserved: { - description: 'my reserved privileges', - privileges: [ - { - id: 'reserved_1', - privilege: { - savedObject: { - all: [], - read: [], - }, - ui: [], - app: [], - }, - }, - ], - }, + }; + + const elasticsearchFeature: ElasticsearchFeatureConfig = { + id: 'test-feature', + privileges: [ + { + requiredClusterPrivileges: ['all'], + ui: [], + }, + ], }; const featureRegistry = new FeatureRegistry(); - expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( - `"child \\"reserved\\" fails because [child \\"privileges\\" fails because [\\"privileges\\" at position 0 fails because [child \\"id\\" fails because [\\"id\\" with value \\"reserved_1\\" fails to match the required pattern: /^(?!reserved_)[a-zA-Z0-9_-]+$/]]]]"` - ); + featureRegistry.registerElasticsearchFeature(elasticsearchFeature); + expect(() => + featureRegistry.registerKibanaFeature(kibanaFeature) + ).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`); }); - it('cannot register feature after getAll has been called', () => { - const feature1: FeatureConfig = { + it('does not allow an Elasticsearch feature to share an id with a Kibana feature', () => { + const kibanaFeature: KibanaFeatureConfig = { id: 'test-feature', name: 'Test Feature', app: [], privileges: null, }; - const feature2: FeatureConfig = { - id: 'test-feature-2', - name: 'Test Feature 2', - app: [], - privileges: null, + + const elasticsearchFeature: ElasticsearchFeatureConfig = { + id: 'test-feature', + privileges: [ + { + requiredClusterPrivileges: ['all'], + ui: [], + }, + ], }; const featureRegistry = new FeatureRegistry(); - featureRegistry.register(feature1); - featureRegistry.getAll(); - expect(() => { - featureRegistry.register(feature2); - }).toThrowErrorMatchingInlineSnapshot( - `"Features are locked, can't register new features. Attempt to register test-feature-2 failed."` - ); + featureRegistry.registerKibanaFeature(kibanaFeature); + expect(() => + featureRegistry.registerElasticsearchFeature(elasticsearchFeature) + ).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`); }); }); diff --git a/x-pack/plugins/features/server/feature_registry.ts b/x-pack/plugins/features/server/feature_registry.ts index 12aafd226f75..d357bdb78279 100644 --- a/x-pack/plugins/features/server/feature_registry.ts +++ b/x-pack/plugins/features/server/feature_registry.ts @@ -5,38 +5,72 @@ */ import { cloneDeep, uniq } from 'lodash'; -import { FeatureConfig, Feature, FeatureKibanaPrivileges } from '../common'; -import { validateFeature } from './feature_schema'; +import { + KibanaFeatureConfig, + KibanaFeature, + FeatureKibanaPrivileges, + ElasticsearchFeatureConfig, + ElasticsearchFeature, +} from '../common'; +import { validateKibanaFeature, validateElasticsearchFeature } from './feature_schema'; export class FeatureRegistry { private locked = false; - private features: Record = {}; + private kibanaFeatures: Record = {}; + private esFeatures: Record = {}; - public register(feature: FeatureConfig) { + public registerKibanaFeature(feature: KibanaFeatureConfig) { if (this.locked) { throw new Error( `Features are locked, can't register new features. Attempt to register ${feature.id} failed.` ); } - validateFeature(feature); + validateKibanaFeature(feature); - if (feature.id in this.features) { + if (feature.id in this.kibanaFeatures || feature.id in this.esFeatures) { throw new Error(`Feature with id ${feature.id} is already registered.`); } const featureCopy = cloneDeep(feature); - this.features[feature.id] = applyAutomaticPrivilegeGrants(featureCopy); + this.kibanaFeatures[feature.id] = applyAutomaticPrivilegeGrants(featureCopy); } - public getAll(): Feature[] { + public registerElasticsearchFeature(feature: ElasticsearchFeatureConfig) { + if (this.locked) { + throw new Error( + `Features are locked, can't register new features. Attempt to register ${feature.id} failed.` + ); + } + + if (feature.id in this.kibanaFeatures || feature.id in this.esFeatures) { + throw new Error(`Feature with id ${feature.id} is already registered.`); + } + + validateElasticsearchFeature(feature); + + const featureCopy = cloneDeep(feature); + + this.esFeatures[feature.id] = featureCopy; + } + + public getAllKibanaFeatures(): KibanaFeature[] { + this.locked = true; + return Object.values(this.kibanaFeatures).map( + (featureConfig) => new KibanaFeature(featureConfig) + ); + } + + public getAllElasticsearchFeatures(): ElasticsearchFeature[] { this.locked = true; - return Object.values(this.features).map((featureConfig) => new Feature(featureConfig)); + return Object.values(this.esFeatures).map( + (featureConfig) => new ElasticsearchFeature(featureConfig) + ); } } -function applyAutomaticPrivilegeGrants(feature: FeatureConfig): FeatureConfig { +function applyAutomaticPrivilegeGrants(feature: KibanaFeatureConfig): KibanaFeatureConfig { const allPrivilege = feature.privileges?.all; const readPrivilege = feature.privileges?.read; const reservedPrivileges = (feature.reserved?.privileges ?? []).map((rp) => rp.privilege); diff --git a/x-pack/plugins/features/server/feature_schema.ts b/x-pack/plugins/features/server/feature_schema.ts index 95298603d706..06a3eb158d99 100644 --- a/x-pack/plugins/features/server/feature_schema.ts +++ b/x-pack/plugins/features/server/feature_schema.ts @@ -8,8 +8,8 @@ import Joi from 'joi'; import { difference } from 'lodash'; import { Capabilities as UICapabilities } from '../../../../src/core/server'; -import { FeatureConfig } from '../common/feature'; -import { FeatureKibanaPrivileges } from '.'; +import { KibanaFeatureConfig } from '../common'; +import { FeatureKibanaPrivileges, ElasticsearchFeatureConfig } from '.'; // Each feature gets its own property on the UICapabilities object, // but that object has a few built-in properties which should not be overwritten. @@ -28,7 +28,7 @@ const managementSchema = Joi.object().pattern( const catalogueSchema = Joi.array().items(Joi.string().regex(uiCapabilitiesRegex)); const alertingSchema = Joi.array().items(Joi.string()); -const privilegeSchema = Joi.object({ +const kibanaPrivilegeSchema = Joi.object({ excludeFromBasePrivileges: Joi.boolean(), management: managementSchema, catalogue: catalogueSchema, @@ -45,7 +45,7 @@ const privilegeSchema = Joi.object({ ui: Joi.array().items(Joi.string().regex(uiCapabilitiesRegex)).required(), }); -const subFeaturePrivilegeSchema = Joi.object({ +const kibanaSubFeaturePrivilegeSchema = Joi.object({ id: Joi.string().regex(subFeaturePrivilegePartRegex).required(), name: Joi.string().required(), includeIn: Joi.string().allow('all', 'read', 'none').required(), @@ -64,17 +64,17 @@ const subFeaturePrivilegeSchema = Joi.object({ ui: Joi.array().items(Joi.string().regex(uiCapabilitiesRegex)).required(), }); -const subFeatureSchema = Joi.object({ +const kibanaSubFeatureSchema = Joi.object({ name: Joi.string().required(), privilegeGroups: Joi.array().items( Joi.object({ groupType: Joi.string().valid('mutually_exclusive', 'independent').required(), - privileges: Joi.array().items(subFeaturePrivilegeSchema).min(1), + privileges: Joi.array().items(kibanaSubFeaturePrivilegeSchema).min(1), }) ), }); -const schema = Joi.object({ +const kibanaFeatureSchema = Joi.object({ id: Joi.string() .regex(featurePrivilegePartRegex) .invalid(...prohibitedFeatureIds) @@ -93,15 +93,15 @@ const schema = Joi.object({ catalogue: catalogueSchema, alerting: alertingSchema, privileges: Joi.object({ - all: privilegeSchema, - read: privilegeSchema, + all: kibanaPrivilegeSchema, + read: kibanaPrivilegeSchema, }) .allow(null) .required(), subFeatures: Joi.when('privileges', { is: null, - then: Joi.array().items(subFeatureSchema).max(0), - otherwise: Joi.array().items(subFeatureSchema), + then: Joi.array().items(kibanaSubFeatureSchema).max(0), + otherwise: Joi.array().items(kibanaSubFeatureSchema), }), privilegesTooltip: Joi.string(), reserved: Joi.object({ @@ -110,15 +110,32 @@ const schema = Joi.object({ .items( Joi.object({ id: Joi.string().regex(reservedFeaturePrrivilegePartRegex).required(), - privilege: privilegeSchema.required(), + privilege: kibanaPrivilegeSchema.required(), }) ) .required(), }), }); -export function validateFeature(feature: FeatureConfig) { - const validateResult = Joi.validate(feature, schema); +const elasticsearchPrivilegeSchema = Joi.object({ + ui: Joi.array().items(Joi.string()).required(), + requiredClusterPrivileges: Joi.array().items(Joi.string()), + requiredIndexPrivileges: Joi.object().pattern(Joi.string(), Joi.array().items(Joi.string())), + requiredRoles: Joi.array().items(Joi.string()), +}); + +const elasticsearchFeatureSchema = Joi.object({ + id: Joi.string() + .regex(featurePrivilegePartRegex) + .invalid(...prohibitedFeatureIds) + .required(), + management: managementSchema, + catalogue: catalogueSchema, + privileges: Joi.array().items(elasticsearchPrivilegeSchema).required(), +}); + +export function validateKibanaFeature(feature: KibanaFeatureConfig) { + const validateResult = Joi.validate(feature, kibanaFeatureSchema); if (validateResult.error) { throw validateResult.error; } @@ -303,3 +320,29 @@ export function validateFeature(feature: FeatureConfig) { ); } } + +export function validateElasticsearchFeature(feature: ElasticsearchFeatureConfig) { + const validateResult = Joi.validate(feature, elasticsearchFeatureSchema); + if (validateResult.error) { + throw validateResult.error; + } + // the following validation can't be enforced by the Joi schema without a very convoluted and verbose definition + const { privileges } = feature; + privileges.forEach((privilege, index) => { + const { + requiredClusterPrivileges = [], + requiredIndexPrivileges = [], + requiredRoles = [], + } = privilege; + + if ( + requiredClusterPrivileges.length === 0 && + requiredIndexPrivileges.length === 0 && + requiredRoles.length === 0 + ) { + throw new Error( + `Feature ${feature.id} has a privilege definition at index ${index} without any privileges defined.` + ); + } + }); +} diff --git a/x-pack/plugins/features/server/index.ts b/x-pack/plugins/features/server/index.ts index 48a350ae8f8f..28c0fee04159 100644 --- a/x-pack/plugins/features/server/index.ts +++ b/x-pack/plugins/features/server/index.ts @@ -13,7 +13,14 @@ import { Plugin } from './plugin'; // run-time contracts. export { uiCapabilitiesRegex } from './feature_schema'; -export { Feature, FeatureConfig, FeatureKibanaPrivileges } from '../common'; +export { + KibanaFeature, + KibanaFeatureConfig, + FeatureKibanaPrivileges, + ElasticsearchFeature, + ElasticsearchFeatureConfig, + FeatureElasticsearchPrivileges, +} from '../common'; export { PluginSetupContract, PluginStartContract } from './plugin'; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/x-pack/plugins/features/server/mocks.ts b/x-pack/plugins/features/server/mocks.ts index d9437169a745..91c297c50e46 100644 --- a/x-pack/plugins/features/server/mocks.ts +++ b/x-pack/plugins/features/server/mocks.ts @@ -8,15 +8,18 @@ import { PluginSetupContract, PluginStartContract } from './plugin'; const createSetup = (): jest.Mocked => { return { - getFeatures: jest.fn(), + getKibanaFeatures: jest.fn(), + getElasticsearchFeatures: jest.fn(), getFeaturesUICapabilities: jest.fn(), - registerFeature: jest.fn(), + registerKibanaFeature: jest.fn(), + registerElasticsearchFeature: jest.fn(), }; }; const createStart = (): jest.Mocked => { return { - getFeatures: jest.fn(), + getKibanaFeatures: jest.fn(), + getElasticsearchFeatures: jest.fn(), }; }; diff --git a/x-pack/plugins/features/server/oss_features.test.ts b/x-pack/plugins/features/server/oss_features.test.ts index c38f2afc8838..961656aba8bf 100644 --- a/x-pack/plugins/features/server/oss_features.test.ts +++ b/x-pack/plugins/features/server/oss_features.test.ts @@ -6,7 +6,7 @@ import { buildOSSFeatures } from './oss_features'; import { featurePrivilegeIterator } from '../../security/server/authorization'; -import { Feature } from '.'; +import { KibanaFeature } from '.'; describe('buildOSSFeatures', () => { it('returns features including timelion', () => { @@ -48,7 +48,7 @@ Array [ features.forEach((featureConfig) => { it(`returns the ${featureConfig.id} feature augmented with appropriate sub feature privileges`, () => { const privileges = []; - for (const featurePrivilege of featurePrivilegeIterator(new Feature(featureConfig), { + for (const featurePrivilege of featurePrivilegeIterator(new KibanaFeature(featureConfig), { augmentWithSubFeaturePrivileges: true, })) { privileges.push(featurePrivilege); diff --git a/x-pack/plugins/features/server/oss_features.ts b/x-pack/plugins/features/server/oss_features.ts index 4122c590e74b..3ff6b1b7bf44 100644 --- a/x-pack/plugins/features/server/oss_features.ts +++ b/x-pack/plugins/features/server/oss_features.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { FeatureConfig } from '../common/feature'; +import { KibanaFeatureConfig } from '../common'; export interface BuildOSSFeaturesParams { savedObjectTypes: string[]; @@ -368,10 +368,10 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS }, }, ...(includeTimelion ? [timelionFeature] : []), - ] as FeatureConfig[]; + ] as KibanaFeatureConfig[]; }; -const timelionFeature: FeatureConfig = { +const timelionFeature: KibanaFeatureConfig = { id: 'timelion', name: 'Timelion', order: 350, diff --git a/x-pack/plugins/features/server/plugin.test.ts b/x-pack/plugins/features/server/plugin.test.ts index 00d578f5ca86..ee11e0e2bbe2 100644 --- a/x-pack/plugins/features/server/plugin.test.ts +++ b/x-pack/plugins/features/server/plugin.test.ts @@ -28,19 +28,19 @@ describe('Features Plugin', () => { coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry); }); - it('returns OSS + registered features', async () => { + it('returns OSS + registered kibana features', async () => { const plugin = new Plugin(initContext); - const { registerFeature } = await plugin.setup(coreSetup, {}); - registerFeature({ + const { registerKibanaFeature } = await plugin.setup(coreSetup, {}); + registerKibanaFeature({ id: 'baz', name: 'baz', app: [], privileges: null, }); - const { getFeatures } = await plugin.start(coreStart); + const { getKibanaFeatures } = plugin.start(coreStart); - expect(getFeatures().map((f) => f.id)).toMatchInlineSnapshot(` + expect(getKibanaFeatures().map((f) => f.id)).toMatchInlineSnapshot(` Array [ "baz", "discover", @@ -54,9 +54,9 @@ describe('Features Plugin', () => { `); }); - it('returns OSS + registered features with timelion when available', async () => { + it('returns OSS + registered kibana features with timelion when available', async () => { const plugin = new Plugin(initContext); - const { registerFeature } = await plugin.setup(coreSetup, { + const { registerKibanaFeature: registerFeature } = await plugin.setup(coreSetup, { visTypeTimelion: { uiEnabled: true }, }); registerFeature({ @@ -66,9 +66,9 @@ describe('Features Plugin', () => { privileges: null, }); - const { getFeatures } = await plugin.start(coreStart); + const { getKibanaFeatures } = plugin.start(coreStart); - expect(getFeatures().map((f) => f.id)).toMatchInlineSnapshot(` + expect(getKibanaFeatures().map((f) => f.id)).toMatchInlineSnapshot(` Array [ "baz", "discover", @@ -83,19 +83,41 @@ describe('Features Plugin', () => { `); }); - it('registers not hidden saved objects types', async () => { + it('registers kibana features with not hidden saved objects types', async () => { const plugin = new Plugin(initContext); await plugin.setup(coreSetup, {}); - const { getFeatures } = await plugin.start(coreStart); + const { getKibanaFeatures } = plugin.start(coreStart); const soTypes = - getFeatures().find((f) => f.id === 'savedObjectsManagement')?.privileges?.all.savedObject - .all || []; + getKibanaFeatures().find((f) => f.id === 'savedObjectsManagement')?.privileges?.all + .savedObject.all || []; expect(soTypes.includes('foo')).toBe(true); expect(soTypes.includes('bar')).toBe(false); }); + it('returns registered elasticsearch features', async () => { + const plugin = new Plugin(initContext); + const { registerElasticsearchFeature } = await plugin.setup(coreSetup, {}); + registerElasticsearchFeature({ + id: 'baz', + privileges: [ + { + requiredClusterPrivileges: ['all'], + ui: ['baz-ui'], + }, + ], + }); + + const { getElasticsearchFeatures } = plugin.start(coreStart); + + expect(getElasticsearchFeatures().map((f) => f.id)).toMatchInlineSnapshot(` + Array [ + "baz", + ] + `); + }); + it('registers a capabilities provider', async () => { const plugin = new Plugin(initContext); await plugin.setup(coreSetup, {}); diff --git a/x-pack/plugins/features/server/plugin.ts b/x-pack/plugins/features/server/plugin.ts index 61b66d95ca44..8a799887bba0 100644 --- a/x-pack/plugins/features/server/plugin.ts +++ b/x-pack/plugins/features/server/plugin.ts @@ -15,27 +15,40 @@ import { Capabilities as UICapabilities } from '../../../../src/core/server'; import { deepFreeze } from '../../../../src/core/server'; import { PluginSetupContract as TimelionSetupContract } from '../../../../src/plugins/vis_type_timelion/server'; import { FeatureRegistry } from './feature_registry'; -import { Feature, FeatureConfig } from '../common/feature'; import { uiCapabilitiesForFeatures } from './ui_capabilities_for_features'; import { buildOSSFeatures } from './oss_features'; import { defineRoutes } from './routes'; +import { + ElasticsearchFeatureConfig, + ElasticsearchFeature, + KibanaFeature, + KibanaFeatureConfig, +} from '../common'; /** * Describes public Features plugin contract returned at the `setup` stage. */ export interface PluginSetupContract { - registerFeature(feature: FeatureConfig): void; + registerKibanaFeature(feature: KibanaFeatureConfig): void; + registerElasticsearchFeature(feature: ElasticsearchFeatureConfig): void; + /* + * Calling this function during setup will crash Kibana. + * Use start contract instead. + * @deprecated + * */ + getKibanaFeatures(): KibanaFeature[]; /* * Calling this function during setup will crash Kibana. * Use start contract instead. * @deprecated * */ - getFeatures(): Feature[]; + getElasticsearchFeatures(): ElasticsearchFeature[]; getFeaturesUICapabilities(): UICapabilities; } export interface PluginStartContract { - getFeatures(): Feature[]; + getElasticsearchFeatures(): ElasticsearchFeature[]; + getKibanaFeatures(): KibanaFeature[]; } /** @@ -62,13 +75,22 @@ export class Plugin { }); const getFeaturesUICapabilities = () => - uiCapabilitiesForFeatures(this.featureRegistry.getAll()); + uiCapabilitiesForFeatures( + this.featureRegistry.getAllKibanaFeatures(), + this.featureRegistry.getAllElasticsearchFeatures() + ); core.capabilities.registerProvider(getFeaturesUICapabilities); return deepFreeze({ - registerFeature: this.featureRegistry.register.bind(this.featureRegistry), - getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), + registerKibanaFeature: this.featureRegistry.registerKibanaFeature.bind(this.featureRegistry), + registerElasticsearchFeature: this.featureRegistry.registerElasticsearchFeature.bind( + this.featureRegistry + ), + getKibanaFeatures: this.featureRegistry.getAllKibanaFeatures.bind(this.featureRegistry), + getElasticsearchFeatures: this.featureRegistry.getAllElasticsearchFeatures.bind( + this.featureRegistry + ), getFeaturesUICapabilities, }); } @@ -77,7 +99,10 @@ export class Plugin { this.registerOssFeatures(core.savedObjects); return deepFreeze({ - getFeatures: this.featureRegistry.getAll.bind(this.featureRegistry), + getElasticsearchFeatures: this.featureRegistry.getAllElasticsearchFeatures.bind( + this.featureRegistry + ), + getKibanaFeatures: this.featureRegistry.getAllKibanaFeatures.bind(this.featureRegistry), }); } @@ -98,7 +123,7 @@ export class Plugin { }); for (const feature of features) { - this.featureRegistry.register(feature); + this.featureRegistry.registerKibanaFeature(feature); } } } diff --git a/x-pack/plugins/features/server/routes/index.test.ts b/x-pack/plugins/features/server/routes/index.test.ts index 3d1efc8a479b..30aa6d07f6b5 100644 --- a/x-pack/plugins/features/server/routes/index.test.ts +++ b/x-pack/plugins/features/server/routes/index.test.ts @@ -11,7 +11,7 @@ import { httpServerMock, httpServiceMock, coreMock } from '../../../../../src/co import { LicenseType } from '../../../licensing/server/'; import { licensingMock } from '../../../licensing/server/mocks'; import { RequestHandler } from '../../../../../src/core/server'; -import { FeatureConfig } from '../../common'; +import { KibanaFeatureConfig } from '../../common'; function createContextMock(licenseType: LicenseType = 'gold') { return { @@ -24,14 +24,14 @@ describe('GET /api/features', () => { let routeHandler: RequestHandler; beforeEach(() => { const featureRegistry = new FeatureRegistry(); - featureRegistry.register({ + featureRegistry.registerKibanaFeature({ id: 'feature_1', name: 'Feature 1', app: [], privileges: null, }); - featureRegistry.register({ + featureRegistry.registerKibanaFeature({ id: 'feature_2', name: 'Feature 2', order: 2, @@ -39,7 +39,7 @@ describe('GET /api/features', () => { privileges: null, }); - featureRegistry.register({ + featureRegistry.registerKibanaFeature({ id: 'feature_3', name: 'Feature 2', order: 1, @@ -47,7 +47,7 @@ describe('GET /api/features', () => { privileges: null, }); - featureRegistry.register({ + featureRegistry.registerKibanaFeature({ id: 'licensed_feature', name: 'Licensed Feature', app: ['bar-app'], @@ -70,7 +70,7 @@ describe('GET /api/features', () => { expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; - const body = call[0]!.body as FeatureConfig[]; + const body = call[0]!.body as KibanaFeatureConfig[]; const features = body.map((feature) => ({ id: feature.id, order: feature.order })); expect(features).toEqual([ @@ -99,7 +99,7 @@ describe('GET /api/features', () => { expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; - const body = call[0]!.body as FeatureConfig[]; + const body = call[0]!.body as KibanaFeatureConfig[]; const features = body.map((feature) => ({ id: feature.id, order: feature.order })); @@ -129,7 +129,7 @@ describe('GET /api/features', () => { expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; - const body = call[0]!.body as FeatureConfig[]; + const body = call[0]!.body as KibanaFeatureConfig[]; const features = body.map((feature) => ({ id: feature.id, order: feature.order })); @@ -159,7 +159,7 @@ describe('GET /api/features', () => { expect(mockResponse.ok).toHaveBeenCalledTimes(1); const [call] = mockResponse.ok.mock.calls; - const body = call[0]!.body as FeatureConfig[]; + const body = call[0]!.body as KibanaFeatureConfig[]; const features = body.map((feature) => ({ id: feature.id, order: feature.order })); diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index 147d34d124fc..b5a4203d7a76 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -26,7 +26,7 @@ export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) }, }, (context, request, response) => { - const allFeatures = featureRegistry.getAll(); + const allFeatures = featureRegistry.getAllKibanaFeatures(); return response.ok({ body: allFeatures diff --git a/x-pack/plugins/features/server/ui_capabilities_for_features.test.ts b/x-pack/plugins/features/server/ui_capabilities_for_features.test.ts index 35dcc4cf42b3..7532bc0573b0 100644 --- a/x-pack/plugins/features/server/ui_capabilities_for_features.test.ts +++ b/x-pack/plugins/features/server/ui_capabilities_for_features.test.ts @@ -5,10 +5,10 @@ */ import { uiCapabilitiesForFeatures } from './ui_capabilities_for_features'; -import { Feature } from '.'; -import { SubFeaturePrivilegeGroupConfig } from '../common'; +import { KibanaFeature } from '.'; +import { SubFeaturePrivilegeGroupConfig, ElasticsearchFeature } from '../common'; -function createFeaturePrivilege(capabilities: string[] = []) { +function createKibanaFeaturePrivilege(capabilities: string[] = []) { return { savedObject: { all: [], @@ -19,7 +19,7 @@ function createFeaturePrivilege(capabilities: string[] = []) { }; } -function createSubFeaturePrivilege(privilegeId: string, capabilities: string[] = []) { +function createKibanaSubFeaturePrivilege(privilegeId: string, capabilities: string[] = []) { return { id: privilegeId, name: `sub-feature privilege ${privilegeId}`, @@ -35,44 +35,101 @@ function createSubFeaturePrivilege(privilegeId: string, capabilities: string[] = describe('populateUICapabilities', () => { it('handles no original uiCapabilities and no registered features gracefully', () => { - expect(uiCapabilitiesForFeatures([])).toEqual({}); + expect(uiCapabilitiesForFeatures([], [])).toEqual({}); }); - it('handles features with no registered capabilities', () => { + it('handles kibana features with no registered capabilities', () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(), - read: createFeaturePrivilege(), - }, - }), - ]) + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(), + read: createKibanaFeaturePrivilege(), + }, + }), + ], + [] + ) + ).toEqual({ + catalogue: {}, + management: {}, + newFeature: {}, + }); + }); + + it('handles elasticsearch features with no registered capabilities', () => { + expect( + uiCapabilitiesForFeatures( + [], + [ + new ElasticsearchFeature({ + id: 'newFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: [], + }, + ], + }), + ] + ) ).toEqual({ catalogue: {}, + management: {}, newFeature: {}, }); }); - it('augments the original uiCapabilities with registered feature capabilities', () => { + it('augments the original uiCapabilities with registered kibana feature capabilities', () => { + expect( + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(), + }, + }), + ], + [] + ) + ).toEqual({ + catalogue: {}, + management: {}, + newFeature: { + capability1: true, + capability2: true, + }, + }); + }); + + it('augments the original uiCapabilities with registered elasticsearch feature capabilities', () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(), - }, - }), - ]) + uiCapabilitiesForFeatures( + [], + [ + new ElasticsearchFeature({ + id: 'newFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['capability1', 'capability2'], + }, + ], + }), + ] + ) ).toEqual({ catalogue: {}, + management: {}, newFeature: { capability1: true, capability2: true, @@ -80,26 +137,66 @@ describe('populateUICapabilities', () => { }); }); - it('combines catalogue entries from multiple features', () => { + it('combines catalogue entries from multiple kibana features', () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - catalogue: ['anotherFooEntry', 'anotherBarEntry'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['capability3', 'capability4']), - }, - }), - ]) + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + catalogue: ['anotherFooEntry', 'anotherBarEntry'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['capability3', 'capability4']), + }, + }), + ], + [] + ) ).toEqual({ catalogue: { anotherFooEntry: true, anotherBarEntry: true, }, + management: {}, + newFeature: { + capability1: true, + capability2: true, + capability3: true, + capability4: true, + }, + }); + }); + + it('combines catalogue entries from multiple elasticsearch privileges', () => { + expect( + uiCapabilitiesForFeatures( + [], + [ + new ElasticsearchFeature({ + id: 'newFeature', + catalogue: ['anotherFooEntry', 'anotherBarEntry'], + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['capability1', 'capability2'], + }, + { + requiredClusterPrivileges: [], + ui: ['capability3', 'capability4'], + }, + ], + }), + ] + ) + ).toEqual({ + catalogue: { + anotherFooEntry: true, + anotherBarEntry: true, + }, + management: {}, newFeature: { capability1: true, capability2: true, @@ -111,20 +208,24 @@ describe('populateUICapabilities', () => { it(`merges capabilities from all feature privileges`, () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['capability3', 'capability4', 'capability5']), - }, - }), - ]) + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['capability3', 'capability4', 'capability5']), + }, + }), + ], + [] + ) ).toEqual({ catalogue: {}, + management: {}, newFeature: { capability1: true, capability2: true, @@ -137,30 +238,38 @@ describe('populateUICapabilities', () => { it(`supports capabilities from reserved privileges`, () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - privileges: null, - reserved: { - description: '', - privileges: [ - { - id: 'rp_1', - privilege: createFeaturePrivilege(['capability1', 'capability2']), - }, - { - id: 'rp_2', - privilege: createFeaturePrivilege(['capability3', 'capability4', 'capability5']), - }, - ], - }, - }), - ]) + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + privileges: null, + reserved: { + description: '', + privileges: [ + { + id: 'rp_1', + privilege: createKibanaFeaturePrivilege(['capability1', 'capability2']), + }, + { + id: 'rp_2', + privilege: createKibanaFeaturePrivilege([ + 'capability3', + 'capability4', + 'capability5', + ]), + }, + ], + }, + }), + ], + [] + ) ).toEqual({ catalogue: {}, + management: {}, newFeature: { capability1: true, capability2: true, @@ -173,53 +282,60 @@ describe('populateUICapabilities', () => { it(`supports merging features with sub privileges`, () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['capability3', 'capability4']), - }, - subFeatures: [ - { - name: 'sub-feature-1', - privilegeGroups: [ - { - groupType: 'independent', - privileges: [ - createSubFeaturePrivilege('privilege-1', ['capability5']), - createSubFeaturePrivilege('privilege-2', ['capability6']), - ], - } as SubFeaturePrivilegeGroupConfig, - { - groupType: 'mutually_exclusive', - privileges: [ - createSubFeaturePrivilege('privilege-3', ['capability7']), - createSubFeaturePrivilege('privilege-4', ['capability8']), - ], - } as SubFeaturePrivilegeGroupConfig, - ], + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['capability3', 'capability4']), }, - { - name: 'sub-feature-2', - privilegeGroups: [ - { - name: 'Group Name', - groupType: 'independent', - privileges: [ - createSubFeaturePrivilege('privilege-5', ['capability9', 'capability10']), - ], - } as SubFeaturePrivilegeGroupConfig, - ], - }, - ], - }), - ]) + subFeatures: [ + { + name: 'sub-feature-1', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + createKibanaSubFeaturePrivilege('privilege-1', ['capability5']), + createKibanaSubFeaturePrivilege('privilege-2', ['capability6']), + ], + } as SubFeaturePrivilegeGroupConfig, + { + groupType: 'mutually_exclusive', + privileges: [ + createKibanaSubFeaturePrivilege('privilege-3', ['capability7']), + createKibanaSubFeaturePrivilege('privilege-4', ['capability8']), + ], + } as SubFeaturePrivilegeGroupConfig, + ], + }, + { + name: 'sub-feature-2', + privilegeGroups: [ + { + name: 'Group Name', + groupType: 'independent', + privileges: [ + createKibanaSubFeaturePrivilege('privilege-5', [ + 'capability9', + 'capability10', + ]), + ], + } as SubFeaturePrivilegeGroupConfig, + ], + }, + ], + }), + ], + [] + ) ).toEqual({ catalogue: {}, + management: {}, newFeature: { capability1: true, capability2: true, @@ -235,53 +351,132 @@ describe('populateUICapabilities', () => { }); }); - it('supports merging multiple features with multiple privileges each', () => { + it('supports merging multiple kibana features with multiple privileges each', () => { expect( - uiCapabilitiesForFeatures([ - new Feature({ - id: 'newFeature', - name: 'my new feature', - navLinkId: 'newFeatureNavLink', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['capability3', 'capability4']), - }, - }), - new Feature({ - id: 'anotherNewFeature', - name: 'another new feature', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['capability3', 'capability4']), - }, - }), - new Feature({ - id: 'yetAnotherNewFeature', - name: 'yet another new feature', - navLinkId: 'yetAnotherNavLink', - app: ['bar-app'], - privileges: { - all: createFeaturePrivilege(['capability1', 'capability2']), - read: createFeaturePrivilege(['something1', 'something2', 'something3']), - }, - subFeatures: [ - { - name: 'sub-feature-1', - privilegeGroups: [ - { - groupType: 'independent', - privileges: [ - createSubFeaturePrivilege('privilege-1', ['capability3']), - createSubFeaturePrivilege('privilege-2', ['capability4']), - ], - } as SubFeaturePrivilegeGroupConfig, - ], + uiCapabilitiesForFeatures( + [ + new KibanaFeature({ + id: 'newFeature', + name: 'my new feature', + navLinkId: 'newFeatureNavLink', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['capability3', 'capability4']), + }, + }), + new KibanaFeature({ + id: 'anotherNewFeature', + name: 'another new feature', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['capability3', 'capability4']), + }, + }), + new KibanaFeature({ + id: 'yetAnotherNewFeature', + name: 'yet another new feature', + navLinkId: 'yetAnotherNavLink', + app: ['bar-app'], + privileges: { + all: createKibanaFeaturePrivilege(['capability1', 'capability2']), + read: createKibanaFeaturePrivilege(['something1', 'something2', 'something3']), }, - ], - }), - ]) + subFeatures: [ + { + name: 'sub-feature-1', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + createKibanaSubFeaturePrivilege('privilege-1', ['capability3']), + createKibanaSubFeaturePrivilege('privilege-2', ['capability4']), + ], + } as SubFeaturePrivilegeGroupConfig, + ], + }, + ], + }), + ], + [] + ) + ).toEqual({ + anotherNewFeature: { + capability1: true, + capability2: true, + capability3: true, + capability4: true, + }, + catalogue: {}, + management: {}, + newFeature: { + capability1: true, + capability2: true, + capability3: true, + capability4: true, + }, + yetAnotherNewFeature: { + capability1: true, + capability2: true, + capability3: true, + capability4: true, + something1: true, + something2: true, + something3: true, + }, + }); + }); + + it('supports merging multiple elasticsearch features with multiple privileges each', () => { + expect( + uiCapabilitiesForFeatures( + [], + [ + new ElasticsearchFeature({ + id: 'newFeature', + + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['capability1', 'capability2'], + }, + { + requiredClusterPrivileges: [], + ui: ['capability3', 'capability4'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'anotherNewFeature', + + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['capability1', 'capability2'], + }, + { + requiredClusterPrivileges: [], + ui: ['capability3', 'capability4'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'yetAnotherNewFeature', + + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['capability1', 'capability2', 'capability3', 'capability4'], + }, + { + requiredClusterPrivileges: [], + ui: ['something1', 'something2', 'something3'], + }, + ], + }), + ] + ) ).toEqual({ anotherNewFeature: { capability1: true, @@ -290,6 +485,7 @@ describe('populateUICapabilities', () => { capability4: true, }, catalogue: {}, + management: {}, newFeature: { capability1: true, capability2: true, diff --git a/x-pack/plugins/features/server/ui_capabilities_for_features.ts b/x-pack/plugins/features/server/ui_capabilities_for_features.ts index 2570d4540b6a..d582dbfdab50 100644 --- a/x-pack/plugins/features/server/ui_capabilities_for_features.ts +++ b/x-pack/plugins/features/server/ui_capabilities_for_features.ts @@ -5,22 +5,35 @@ */ import _ from 'lodash'; +import { RecursiveReadonly } from '@kbn/utility-types'; import { Capabilities as UICapabilities } from '../../../../src/core/server'; -import { Feature } from '../common/feature'; +import { ElasticsearchFeature, KibanaFeature } from '../common'; const ELIGIBLE_FLAT_MERGE_KEYS = ['catalogue'] as const; +const ELIGIBLE_DEEP_MERGE_KEYS = ['management'] as const; interface FeatureCapabilities { [featureId: string]: Record; } -export function uiCapabilitiesForFeatures(features: Feature[]): UICapabilities { - const featureCapabilities: FeatureCapabilities[] = features.map(getCapabilitiesFromFeature); +export function uiCapabilitiesForFeatures( + kibanaFeatures: KibanaFeature[], + elasticsearchFeatures: ElasticsearchFeature[] +): UICapabilities { + const kibanaFeatureCapabilities = kibanaFeatures.map(getCapabilitiesFromFeature); + const elasticsearchFeatureCapabilities = elasticsearchFeatures.map(getCapabilitiesFromFeature); - return buildCapabilities(...featureCapabilities); + return buildCapabilities(...kibanaFeatureCapabilities, ...elasticsearchFeatureCapabilities); } -function getCapabilitiesFromFeature(feature: Feature): FeatureCapabilities { +function getCapabilitiesFromFeature( + feature: + | Pick< + KibanaFeature, + 'id' | 'catalogue' | 'management' | 'privileges' | 'subFeatures' | 'reserved' + > + | Pick +): FeatureCapabilities { const UIFeatureCapabilities: FeatureCapabilities = { catalogue: {}, [feature.id]: {}, @@ -39,14 +52,34 @@ function getCapabilitiesFromFeature(feature: Feature): FeatureCapabilities { }; } - const featurePrivileges = Object.values(feature.privileges ?? {}); - if (feature.subFeatures) { - featurePrivileges.push( - ...feature.subFeatures.map((sf) => sf.privilegeGroups.map((pg) => pg.privileges)).flat(2) - ); + if (feature.management) { + const sectionEntries = Object.entries(feature.management); + UIFeatureCapabilities.management = sectionEntries.reduce((acc, [sectionId, sectionItems]) => { + return { + ...acc, + [sectionId]: sectionItems.reduce((acc2, item) => { + return { + ...acc2, + [item]: true, + }; + }, {}), + }; + }, {}); } - if (feature.reserved?.privileges) { - featurePrivileges.push(...feature.reserved.privileges.map((rp) => rp.privilege)); + + const featurePrivileges = Object.values(feature.privileges ?? {}) as Writable< + Array<{ ui: RecursiveReadonly }> + >; + + if (isKibanaFeature(feature)) { + if (feature.subFeatures) { + featurePrivileges.push( + ...feature.subFeatures.map((sf) => sf.privilegeGroups.map((pg) => pg.privileges)).flat(2) + ); + } + if (feature.reserved?.privileges) { + featurePrivileges.push(...feature.reserved.privileges.map((rp) => rp.privilege)); + } } featurePrivileges.forEach((privilege) => { @@ -65,6 +98,20 @@ function getCapabilitiesFromFeature(feature: Feature): FeatureCapabilities { return UIFeatureCapabilities; } +function isKibanaFeature( + feature: Partial | Partial +): feature is KibanaFeature { + // Elasticsearch features define privileges as an array, + // whereas Kibana features define privileges as an object, + // or they define reserved privileges, or they don't define either. + // Elasticsearch features are required to defined privileges. + return ( + (feature as any).reserved != null || + (feature.privileges && !Array.isArray(feature.privileges)) || + feature.privileges === null + ); +} + function buildCapabilities(...allFeatureCapabilities: FeatureCapabilities[]): UICapabilities { return allFeatureCapabilities.reduce((acc, capabilities) => { const mergableCapabilities = _.omit(capabilities, ...ELIGIBLE_FLAT_MERGE_KEYS); @@ -81,6 +128,14 @@ function buildCapabilities(...allFeatureCapabilities: FeatureCapabilities[]): UI }; }); + ELIGIBLE_DEEP_MERGE_KEYS.forEach((key) => { + mergedFeatureCapabilities[key] = _.merge( + {}, + mergedFeatureCapabilities[key], + capabilities[key] + ); + }); + return mergedFeatureCapabilities; }, {} as UICapabilities); } diff --git a/x-pack/plugins/graph/server/plugin.ts b/x-pack/plugins/graph/server/plugin.ts index b2b825fa4683..d69c592655fb 100644 --- a/x-pack/plugins/graph/server/plugin.ts +++ b/x-pack/plugins/graph/server/plugin.ts @@ -41,7 +41,7 @@ export class GraphPlugin implements Plugin { } if (features) { - features.registerFeature({ + features.registerKibanaFeature({ id: 'graph', name: i18n.translate('xpack.graph.featureRegistry.graphFeatureName', { defaultMessage: 'Graph', diff --git a/x-pack/plugins/index_lifecycle_management/kibana.json b/x-pack/plugins/index_lifecycle_management/kibana.json index f89928764278..479d651fc669 100644 --- a/x-pack/plugins/index_lifecycle_management/kibana.json +++ b/x-pack/plugins/index_lifecycle_management/kibana.json @@ -5,7 +5,8 @@ "ui": true, "requiredPlugins": [ "licensing", - "management" + "management", + "features" ], "optionalPlugins": [ "usageCollection", diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation.tsx index 6f80afccbff5..6a22d8716514 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/node_allocation.tsx @@ -52,7 +52,7 @@ export const NodeAllocation = ({ phaseData, isShowingErrors, }: React.PropsWithChildren>) => { - const { isLoading, data: nodes, error, sendRequest } = useLoadNodes(); + const { isLoading, data: nodes, error, resendRequest } = useLoadNodes(); const [selectedNodeAttrsForDetails, setSelectedNodeAttrsForDetails] = useState( null @@ -84,7 +84,7 @@ export const NodeAllocation = ({

{message} ({statusCode})

- + = ({ close, selectedNodeAttrs }) => { - const { data, isLoading, error, sendRequest } = useLoadNodeDetails(selectedNodeAttrs); + const { data, isLoading, error, resendRequest } = useLoadNodeDetails(selectedNodeAttrs); let content; if (isLoading) { content = ; @@ -47,7 +47,7 @@ export const NodeAttrsDetails: React.FunctionComponent = ({ close, select

{message} ({statusCode})

- + = ({ onChange, getUrlForApp, }) => { - const { error, isLoading, data, sendRequest } = useLoadSnapshotPolicies(); + const { error, isLoading, data, resendRequest } = useLoadSnapshotPolicies(); const policies = data.map((name: string) => ({ label: name, @@ -75,7 +75,7 @@ export const SnapshotPolicies: React.FunctionComponent = ({ { - const { error, isLoading, data: policies, sendRequest } = useLoadPoliciesList(false); + const { error, isLoading, data: policies, resendRequest } = useLoadPoliciesList(false); if (isLoading) { return ( } actions={ - + = navigateToApp, history, }) => { - const { data: policies, isLoading, error, sendRequest } = useLoadPoliciesList(true); + const { data: policies, isLoading, error, resendRequest } = useLoadPoliciesList(true); if (isLoading) { return ( @@ -53,7 +53,7 @@ export const PolicyTable: React.FunctionComponent =

} actions={ - + = policies={policies || []} history={history} navigateToApp={navigateToApp} - updatePolicies={sendRequest} + updatePolicies={resendRequest} /> ); }; diff --git a/x-pack/plugins/index_lifecycle_management/server/plugin.ts b/x-pack/plugins/index_lifecycle_management/server/plugin.ts index 76d8539eb4a0..3075f9c89eb8 100644 --- a/x-pack/plugins/index_lifecycle_management/server/plugin.ts +++ b/x-pack/plugins/index_lifecycle_management/server/plugin.ts @@ -60,7 +60,10 @@ export class IndexLifecycleManagementServerPlugin implements Plugin { + async setup( + { http }: CoreSetup, + { licensing, indexManagement, features }: Dependencies + ): Promise { const router = http.createRouter(); const config = await this.config$.pipe(first()).toPromise(); @@ -78,6 +81,19 @@ export class IndexLifecycleManagementServerPlugin implements Plugin = ({ } = useGlobalFlyout(); const { api, trackMetric, documentation } = useComponentTemplatesContext(); - const { data, isLoading, error, sendRequest } = api.useLoadComponentTemplates(); + const { data, isLoading, error, resendRequest } = api.useLoadComponentTemplates(); const [componentTemplatesToDelete, setComponentTemplatesToDelete] = useState([]); @@ -170,7 +170,7 @@ export const ComponentTemplateList: React.FunctionComponent = ({ = ({ } else if (data && data.length === 0) { content = ; } else if (error) { - content = ; + content = ; } return ( @@ -194,7 +194,7 @@ export const ComponentTemplateList: React.FunctionComponent = ({ callback={(deleteResponse) => { if (deleteResponse?.hasDeletedComponentTemplates) { // refetch the component templates - sendRequest(); + resendRequest(); // go back to list view (if deleted from details flyout) goToComponentTemplateList(); } diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_above_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_above_parameter.tsx new file mode 100644 index 000000000000..48a8e42f5065 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_above_parameter.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { documentationService } from '../../../../../services/documentation'; +import { getFieldConfig } from '../../../lib'; +import { UseField, Field } from '../../../shared_imports'; +import { EditFieldFormRow } from '../fields/edit_field'; + +interface Props { + defaultToggleValue: boolean; +} + +export const IgnoreAboveParameter: FunctionComponent = ({ defaultToggleValue }) => ( + + + +); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts index 805a6b6ece70..a2d5c7c8d530 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts @@ -69,6 +69,8 @@ export * from './other_type_name_parameter'; export * from './other_type_json_parameter'; +export * from './ignore_above_parameter'; + export const PARAMETER_SERIALIZERS = [relationsSerializer, dynamicSerializer]; export const PARAMETER_DESERIALIZERS = [relationsDeserializer, dynamicDeserializer]; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx index 7c8ac86f1415..e96426ece27e 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx @@ -6,7 +6,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { documentationService } from '../../../../../../services/documentation'; import { NormalizedField, Field as FieldType } from '../../../../types'; import { UseField, Field } from '../../../../shared_imports'; import { getFieldConfig } from '../../../../lib'; @@ -19,6 +18,7 @@ import { NullValueParameter, SimilarityParameter, SplitQueriesOnWhitespaceParameter, + IgnoreAboveParameter, } from '../../field_parameters'; import { BasicParametersSection, EditFieldFormRow, AdvancedParametersSection } from '../edit_field'; @@ -29,6 +29,7 @@ interface Props { const getDefaultToggleValue = (param: string, field: FieldType) => { switch (param) { case 'boost': + case 'ignore_above': case 'similarity': { return field[param] !== undefined && field[param] !== getFieldConfig(param).defaultValue; } @@ -66,28 +67,9 @@ export const FlattenedType = React.memo(({ field }: Props) => { - {/* ignore_above */} - - - + /> diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts index 20623b9d7e62..d84d9c6ea40c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts @@ -28,6 +28,8 @@ import { ObjectType } from './object_type'; import { OtherType } from './other_type'; import { NestedType } from './nested_type'; import { JoinType } from './join_type'; +import { RankFeatureType } from './rank_feature_type'; +import { WildcardType } from './wildcard_type'; const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { alias: AliasType, @@ -52,6 +54,8 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { other: OtherType, nested: NestedType, join: JoinType, + rank_feature: RankFeatureType, + wildcard: WildcardType, }; export const getParametersFormForType = ( diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx index 43377357f1e6..dc4f4b3ba5ff 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx @@ -23,6 +23,7 @@ import { SimilarityParameter, CopyToParameter, SplitQueriesOnWhitespaceParameter, + IgnoreAboveParameter, } from '../../field_parameters'; import { BasicParametersSection, EditFieldFormRow, AdvancedParametersSection } from '../edit_field'; @@ -79,25 +80,9 @@ export const KeywordType = ({ field }: Props) => { - {/* ignore_above */} - - - + /> diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/rank_feature_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/rank_feature_type.tsx new file mode 100644 index 000000000000..136a83c6d17f --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/rank_feature_type.tsx @@ -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 React from 'react'; +import { i18n } from '@kbn/i18n'; + +import { BasicParametersSection, EditFieldFormRow } from '../edit_field'; + +export const RankFeatureType = () => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/wildcard_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/wildcard_type.tsx new file mode 100644 index 000000000000..825b9e17c8d2 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/wildcard_type.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; + +import { NormalizedField, Field as FieldType, ParameterName } from '../../../../types'; +import { getFieldConfig } from '../../../../lib'; +import { IgnoreAboveParameter } from '../../field_parameters'; +import { AdvancedParametersSection } from '../edit_field'; + +interface Props { + field: NormalizedField; +} + +const getDefaultToggleValue = (param: ParameterName, field: FieldType) => { + return field[param] !== undefined && field[param] !== getFieldConfig(param).defaultValue; +}; + +export const WildcardType = ({ field }: Props) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx index edfb6903a858..a8844c7a9b27 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx @@ -784,6 +784,23 @@ export const TYPE_DEFINITION: { [key in DataType]: DataTypeDefinition } = {

), }, + wildcard: { + label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.wildcardDescription', { + defaultMessage: 'Wildcard', + }), + value: 'wildcard', + documentation: { + main: '/keyword.html#wildcard-field-type', + }, + description: () => ( +

+ +

+ ), + }, other: { label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.otherDescription', { defaultMessage: 'Other', @@ -825,6 +842,7 @@ export const MAIN_TYPES: MainType[] = [ 'shape', 'text', 'token_count', + 'wildcard', 'other', ]; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx index c7529ff272e2..f2148f1f657a 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx @@ -692,6 +692,12 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio }, schema: t.boolean, }, + positive_score_impact: { + fieldConfig: { + defaultValue: true, + }, + schema: t.boolean, + }, preserve_separators: { fieldConfig: { defaultValue: true, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts index 6882ddea4ad5..fd0e4ed32bfe 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts @@ -59,6 +59,7 @@ export type MainType = | 'geo_point' | 'geo_shape' | 'token_count' + | 'wildcard' /** * 'other' is a special type that only exists inside of MappingsEditor as a placeholder * for undocumented field types. @@ -124,6 +125,7 @@ export type ParameterName = | 'eager_global_ordinals_join' | 'index_prefixes' | 'index_phrases' + | 'positive_score_impact' | 'norms' | 'norms_keyword' | 'term_vector' diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx index d37576f18e84..4f2a5c4a27b7 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx @@ -49,7 +49,7 @@ export const DataStreamList: React.FunctionComponent {}; + reload: UseRequestResponse['resendRequest']; history: ScopedHistory; includeStats: boolean; filters?: string; diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx index 9203e76fce78..7ec6f1f94a2a 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/legacy_templates/template_table/template_table.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiInMemoryTable, EuiButton, EuiLink, EuiBasicTableColumn } from '@elastic/eui'; import { ScopedHistory } from 'kibana/public'; -import { SendRequestResponse, reactRouterNavigate } from '../../../../../../shared_imports'; +import { UseRequestResponse, reactRouterNavigate } from '../../../../../../shared_imports'; import { TemplateListItem } from '../../../../../../../common'; import { UIM_TEMPLATE_SHOW_DETAILS_CLICK } from '../../../../../../../common/constants'; import { TemplateDeleteModal } from '../../../../../components'; @@ -20,7 +20,7 @@ import { TemplateTypeIndicator } from '../../components'; interface Props { templates: TemplateListItem[]; - reload: () => Promise; + reload: UseRequestResponse['resendRequest']; editTemplate: (name: string, isLegacy?: boolean) => void; cloneTemplate: (name: string, isLegacy?: boolean) => void; history: ScopedHistory; diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx index 5bacffc4c240..94891297c857 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/template_details_content.tsx @@ -31,7 +31,7 @@ import { UIM_TEMPLATE_DETAIL_PANEL_ALIASES_TAB, UIM_TEMPLATE_DETAIL_PANEL_PREVIEW_TAB, } from '../../../../../../common/constants'; -import { SendRequestResponse } from '../../../../../shared_imports'; +import { UseRequestResponse } from '../../../../../shared_imports'; import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components'; import { useLoadIndexTemplate } from '../../../../services/api'; import { decodePathFromReactRouter } from '../../../../services/routing'; @@ -92,7 +92,7 @@ export interface Props { onClose: () => void; editTemplate: (name: string, isLegacy?: boolean) => void; cloneTemplate: (name: string, isLegacy?: boolean) => void; - reload: () => Promise; + reload: UseRequestResponse['resendRequest']; } export const TemplateDetailsContent = ({ diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx index f421bc5d87a5..c711f457123f 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_list.tsx @@ -59,7 +59,7 @@ export const TemplateList: React.FunctionComponent { const { uiMetricService } = useServices(); - const { error, isLoading, data: allTemplates, sendRequest: reload } = useLoadIndexTemplates(); + const { error, isLoading, data: allTemplates, resendRequest: reload } = useLoadIndexTemplates(); const [filters, setFilters] = useState>({ managed: { diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx index 3dffdcde160f..c32fd29cf9f9 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx @@ -12,7 +12,7 @@ import { ScopedHistory } from 'kibana/public'; import { TemplateListItem } from '../../../../../../common'; import { UIM_TEMPLATE_SHOW_DETAILS_CLICK } from '../../../../../../common/constants'; -import { SendRequestResponse, reactRouterNavigate } from '../../../../../shared_imports'; +import { UseRequestResponse, reactRouterNavigate } from '../../../../../shared_imports'; import { encodePathForReactRouter } from '../../../../services/routing'; import { useServices } from '../../../../app_context'; import { TemplateDeleteModal } from '../../../../components'; @@ -21,7 +21,7 @@ import { TemplateTypeIndicator } from '../components'; interface Props { templates: TemplateListItem[]; - reload: () => Promise; + reload: UseRequestResponse['resendRequest']; editTemplate: (name: string) => void; cloneTemplate: (name: string) => void; history: ScopedHistory; diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index f7f992a09050..d58545768732 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -8,6 +8,7 @@ export { SendRequestConfig, SendRequestResponse, UseRequestConfig, + UseRequestResponse, sendRequest, useRequest, Forms, diff --git a/x-pack/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts index 0cd180a980a8..30aeeb6b4536 100644 --- a/x-pack/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -59,7 +59,7 @@ export class IndexMgmtServerPlugin implements Plugin { this.dataManagementESClient = this.dataManagementESClient ?? (await getCustomEsClient(getStartServices)); diff --git a/x-pack/plugins/index_management/server/types.ts b/x-pack/plugins/index_management/server/types.ts index fce0414dee93..7aa91629f0a4 100644 --- a/x-pack/plugins/index_management/server/types.ts +++ b/x-pack/plugins/index_management/server/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { LegacyScopedClusterClient, IRouter } from 'src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; import { License, IndexDataEnricher } from './services'; @@ -12,6 +13,7 @@ import { isEsError } from './shared_imports'; export interface Dependencies { security: SecurityPluginSetup; licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 90b73b9a7585..737f7ed1b6e4 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -132,8 +132,8 @@ export class InfraServerPlugin { ...domainLibs, }; - plugins.features.registerFeature(METRICS_FEATURE); - plugins.features.registerFeature(LOGS_FEATURE); + plugins.features.registerKibanaFeature(METRICS_FEATURE); + plugins.features.registerKibanaFeature(LOGS_FEATURE); plugins.home.sampleData.addAppLinksToSampleDataset('logs', [ { 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 2b8a306577e7..a204373fe2e5 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -21,7 +21,8 @@ export type AgentStatus = | 'unenrolling' | 'degraded'; -export type AgentActionType = 'CONFIG_CHANGE' | 'DATA_DUMP' | 'RESUME' | 'PAUSE' | 'UNENROLL'; +export type AgentActionType = 'CONFIG_CHANGE' | 'UNENROLL'; + export interface NewAgentAction { type: AgentActionType; data?: any; @@ -29,20 +30,44 @@ export interface NewAgentAction { } export interface AgentAction extends NewAgentAction { + type: AgentActionType; + data?: any; + sent_at?: string; id: string; agent_id: string; created_at: string; + ack_data?: any; +} + +export interface AgentPolicyAction extends NewAgentAction { + id: string; + type: AgentActionType; + data?: any; + policy_id: string; + policy_revision: number; + created_at: string; + ack_data?: any; } -export interface AgentActionSOAttributes { +interface CommonAgentActionSOAttributes { type: AgentActionType; sent_at?: string; timestamp?: string; created_at: string; - agent_id: string; data?: string; + ack_data?: string; } +export type AgentActionSOAttributes = CommonAgentActionSOAttributes & { + agent_id: string; +}; +export type AgentPolicyActionSOAttributes = CommonAgentActionSOAttributes & { + policy_id: string; + policy_revision: number; +}; + +export type BaseAgentActionSOAttributes = AgentActionSOAttributes | AgentPolicyActionSOAttributes; + export interface NewAgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; subtype: // State diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index cf8d3ab1c908..54cdeade3764 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -7,11 +7,11 @@ import { Agent, AgentAction, + NewAgentAction, NewAgentEvent, AgentEvent, AgentStatus, AgentType, - NewAgentAction, } from '../models'; export interface GetAgentsRequest { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx index 9f48be54f866..ccf9e45ebc4f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_select_agent_policy.tsx @@ -83,7 +83,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{ data: agentPoliciesData, error: agentPoliciesError, isLoading: isAgentPoliciesLoading, - sendRequest: refreshAgentPolicies, + resendRequest: refreshAgentPolicies, } = useGetAgentPolicies({ page: 1, perPage: 1000, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/details_page/hooks/use_agent_status.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/details_page/hooks/use_agent_status.tsx index 71dcd728d5d1..3483d8dee045 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/details_page/hooks/use_agent_status.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/details_page/hooks/use_agent_status.tsx @@ -25,7 +25,7 @@ export function useGetAgentStatus(policyId?: string, options?: RequestOptions) { isLoading: agentStatusRequest.isLoading, data: agentStatusRequest.data, error: agentStatusRequest.error, - refreshAgentStatus: () => agentStatusRequest.sendRequest, + refreshAgentStatus: () => agentStatusRequest.resendRequest, }; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/list_page/index.tsx index 361b1c33f1a0..fb963dc67ae1 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/list_page/index.tsx @@ -108,7 +108,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { ); // Fetch agent policies - const { isLoading, data: agentPolicyData, sendRequest } = useGetAgentPolicies({ + const { isLoading, data: agentPolicyData, resendRequest } = useGetAgentPolicies({ page: pagination.currentPage, perPage: pagination.pageSize, sortField: sorting?.field, @@ -204,7 +204,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { render: (agentPolicy: AgentPolicy) => ( sendRequest()} + onCopySuccess={() => resendRequest()} /> ), }, @@ -218,7 +218,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { } return cols; - }, [getHref, isFleetEnabled, sendRequest]); + }, [getHref, isFleetEnabled, resendRequest]); const createAgentPolicyButton = useMemo( () => ( @@ -270,7 +270,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { { setIsCreateAgentPolicyFlyoutOpen(false); - sendRequest(); + resendRequest(); }} /> ) : null} @@ -289,7 +289,7 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => { /> - sendRequest()}> + resendRequest()}> = () => { const { pagination, pageSizeOptions } = usePagination(); // Fetch data streams - const { isLoading, data: dataStreamsData, sendRequest } = useGetDataStreams(); + const { isLoading, data: dataStreamsData, resendRequest } = useGetDataStreams(); // Some policies retrieved, set up table props const columns = useMemo(() => { @@ -241,7 +241,7 @@ export const DataStreamListPage: React.FunctionComponent<{}> = () => { key="reloadButton" color="primary" iconType="refresh" - onClick={() => sendRequest()} + onClick={() => resendRequest()} > = ({ ag [key: string]: JSX.Element; }>({}); - const { isLoading, data, sendRequest } = useGetOneAgentEvents(agent.id, { + const { isLoading, data, resendRequest } = useGetOneAgentEvents(agent.id, { page: pagination.currentPage, perPage: pagination.pageSize, kuery: search && search.trim() !== '' ? search.trim() : undefined, }); - const refresh = () => sendRequest(); + const refresh = () => resendRequest(); const total = data ? data.total : 0; const list = data ? data.list : []; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx index 219b343eba41..fe0781f4a240 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx @@ -51,7 +51,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => { isInitialRequest, error, data: agentData, - sendRequest: sendAgentRequest, + resendRequest: sendAgentRequest, } = useGetOneAgent(agentId, { pollIntervalMs: 5000, }); 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 9548340df5b3..46f7ffb85b21 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 @@ -344,7 +344,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { return ( agentsRequest.sendRequest()} + refresh={() => agentsRequest.resendRequest()} onReassignClick={() => setAgentToReassignId(agent.id)} /> ); @@ -394,7 +394,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { agent={agentToReassign} onClose={() => { setAgentToReassignId(undefined); - agentsRequest.sendRequest(); + agentsRequest.resendRequest(); }} /> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx index 7db9d72eb50e..04fef7f4b3f2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx @@ -24,7 +24,7 @@ interface Props { agentPolicies?: AgentPolicy[]; } -export const ManagedInstructions: React.FunctionComponent = ({ agentPolicies }) => { +export const ManagedInstructions = React.memo(({ agentPolicies }) => { const { getHref } = useLink(); const core = useCore(); const fleetStatus = useFleetStatus(); @@ -91,4 +91,4 @@ export const ManagedInstructions: React.FunctionComponent = ({ agentPolic )} ); -}; +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/standalone_instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/standalone_instructions.tsx index 9262cc2cb42a..387ccfc66cbc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/standalone_instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/standalone_instructions.tsx @@ -31,7 +31,7 @@ interface Props { const RUN_INSTRUCTIONS = './elastic-agent run'; -export const StandaloneInstructions: React.FunctionComponent = ({ agentPolicies }) => { +export const StandaloneInstructions = React.memo(({ agentPolicies }) => { const { getHref } = useLink(); const core = useCore(); const { notifications } = core; @@ -189,4 +189,4 @@ export const StandaloneInstructions: React.FunctionComponent = ({ agentPo ); -}; +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx index b3a4938b2231..d85a6e8b5b83 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx @@ -244,7 +244,10 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { render: (_: any, apiKey: EnrollmentAPIKey) => { return ( apiKey.active && ( - enrollmentAPIKeysRequest.sendRequest()} /> + enrollmentAPIKeysRequest.resendRequest()} + /> ) ); }, @@ -258,7 +261,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { agentPolicies={agentPolicies} onClose={() => { setFlyoutOpen(false); - enrollmentAPIKeysRequest.sendRequest(); + enrollmentAPIKeysRequest.resendRequest(); }} /> )} diff --git a/x-pack/plugins/ingest_manager/public/index.ts b/x-pack/plugins/ingest_manager/public/index.ts index 75ba0e584230..730ab59c3eb1 100644 --- a/x-pack/plugins/ingest_manager/public/index.ts +++ b/x-pack/plugins/ingest_manager/public/index.ts @@ -20,3 +20,5 @@ export { export { NewPackagePolicy } from './applications/ingest_manager/types'; export * from './applications/ingest_manager/types/intra_app_route_state'; + +export { pagePathGetters } from './applications/ingest_manager/constants'; diff --git a/x-pack/plugins/ingest_manager/scripts/dev_agent/script.ts b/x-pack/plugins/ingest_manager/scripts/dev_agent/script.ts index 65375a076e9a..47108508ec68 100644 --- a/x-pack/plugins/ingest_manager/scripts/dev_agent/script.ts +++ b/x-pack/plugins/ingest_manager/scripts/dev_agent/script.ts @@ -14,7 +14,11 @@ import { PostAgentEnrollRequest, PostAgentEnrollResponse, } from '../../common/types'; +import * as kibanaPackage from '../../package.json'; +// @ts-ignore +// Using the ts-ignore because we are importing directly from a json to a script file +const version = kibanaPackage.version; const CHECKIN_INTERVAL = 3000; // 3 seconds type Agent = Pick<_Agent, 'id' | 'access_api_key'>; @@ -104,6 +108,7 @@ async function enroll(kibanaURL: string, apiKey: string, log: ToolingLog): Promi ip: '127.0.0.1', system: `${os.type()} ${os.release()}`, memory: os.totalmem(), + elastic: { agent: { version } }, }, user_provided: { dev_agent_version: '0.0.1', diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 4a7677d69d6e..b10f3527a045 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -173,7 +173,7 @@ export class IngestManagerPlugin // Register feature // TODO: Flesh out privileges if (deps.features) { - deps.features.registerFeature({ + deps.features.registerKibanaFeature({ id: PLUGIN_ID, name: 'Ingest Manager', icon: 'savedObjectsApp', diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index b81d44c40f8e..12a0956b7915 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -10,7 +10,6 @@ import { RequestHandler } from 'kibana/server'; import { TypeOf } from '@kbn/config-schema'; import { PostNewAgentActionRequestSchema } from '../../types/rest_spec'; import { ActionsService } from '../../services/agents'; -import { NewAgentAction } from '../../../common/types/models'; import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; export const postNewAgentActionHandlerBuilder = function ( @@ -26,7 +25,7 @@ export const postNewAgentActionHandlerBuilder = function ( const agent = await actionsService.getAgent(soClient, request.params.agentId); - const newAgentAction = request.body.action as NewAgentAction; + const newAgentAction = request.body.action; const savedAgentAction = await actionsService.createAgentAction(soClient, { created_at: new Date().toISOString(), diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts index b19960cc9022..385e256933c1 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts @@ -167,7 +167,7 @@ export const installPackageHandler: RequestHandler< await removeInstallation({ savedObjectsClient, pkgkey, callCluster }); } if (installType === 'update') { - // @ts-ignore installType conditions already check for existence of installedPkg + // @ts-ignore getInstallType ensures we have installedPkg const prevVersion = `${pkgName}-${installedPkg.attributes.version}`; logger.error(`rolling back to ${prevVersion} after error installing ${pkgkey}`); await installPackage({ diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts index aff8e607622d..e86f7b24e2c7 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -98,8 +98,11 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { mappings: { properties: { agent_id: { type: 'keyword' }, + policy_id: { type: 'keyword' }, + policy_revision: { type: 'integer' }, type: { type: 'keyword' }, data: { type: 'binary' }, + ack_data: { type: 'text' }, sent_at: { type: 'date' }, created_at: { type: 'date' }, }, diff --git a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts index a03a3b7f59fb..938cfb435163 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts @@ -21,7 +21,7 @@ import { ListWithKuery, } from '../types'; import { DeleteAgentPolicyResponse, storedPackagePoliciesToAgentInputs } from '../../common'; -import { listAgents } from './agents'; +import { createAgentPolicyAction, listAgents } from './agents'; import { packagePolicyService } from './package_policy'; import { outputService } from './output'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; @@ -67,6 +67,10 @@ class AgentPolicyService { updated_by: user ? user.username : 'system', }); + if (options.bumpRevision) { + await this.triggerAgentPolicyUpdatedEvent(soClient, 'updated', id); + } + return (await this.get(soClient, id)) as AgentPolicy; } @@ -383,6 +387,32 @@ class AgentPolicyService { }; } + public async createFleetPolicyChangeAction( + soClient: SavedObjectsClientContract, + agentPolicyId: string + ) { + const policy = await agentPolicyService.getFullAgentPolicy(soClient, agentPolicyId); + if (!policy || !policy.revision) { + return; + } + const packages = policy.inputs.reduce((acc, input) => { + const packageName = input.meta?.package?.name; + if (packageName && acc.indexOf(packageName) < 0) { + acc.push(packageName); + } + return acc; + }, []); + + await createAgentPolicyAction(soClient, { + type: 'CONFIG_CHANGE', + data: { config: policy } as any, + ack_data: { packages }, + created_at: new Date().toISOString(), + policy_id: policy.id, + policy_revision: policy.revision, + }); + } + public async getFullAgentPolicy( soClient: SavedObjectsClientContract, id: string, diff --git a/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts b/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts index 3c743dd957f6..ff20e25e5bf0 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts @@ -8,6 +8,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForAgentPolicyId } from './api_keys'; import { unenrollForAgentPolicyId } from './agents'; import { outputService } from './output'; +import { agentPolicyService } from './agent_policy'; export async function agentPolicyUpdateEventHandler( soClient: SavedObjectsClientContract, @@ -15,8 +16,9 @@ export async function agentPolicyUpdateEventHandler( agentPolicyId: string ) { const adminUser = await outputService.getAdminUser(soClient); - // If no admin user fleet is not enabled just skip this hook - if (!adminUser) { + const outputId = await outputService.getDefaultOutputId(soClient); + // If no admin user and no default output fleet is not enabled just skip this hook + if (!adminUser || !outputId) { return; } @@ -24,6 +26,11 @@ export async function agentPolicyUpdateEventHandler( await generateEnrollmentAPIKey(soClient, { agentPolicyId, }); + await agentPolicyService.createFleetPolicyChangeAction(soClient, agentPolicyId); + } + + if (action === 'updated') { + await agentPolicyService.createFleetPolicyChangeAction(soClient, agentPolicyId); } if (action === 'deleted') { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts index 80fdc305d0ba..866aa587b8a5 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts @@ -6,45 +6,19 @@ import Boom from 'boom'; import { SavedObjectsBulkResponse } from 'kibana/server'; import { savedObjectsClientMock } from 'src/core/server/mocks'; -import { encryptedSavedObjectsMock } from '../../../../../plugins/encrypted_saved_objects/server/mocks'; import { Agent, - AgentAction, AgentActionSOAttributes, + BaseAgentActionSOAttributes, AgentEvent, } from '../../../common/types/models'; import { AGENT_TYPE_PERMANENT, AGENT_ACTION_SAVED_OBJECT_TYPE } from '../../../common/constants'; import { acknowledgeAgentActions } from './acks'; -import { appContextService } from '../app_context'; -import { IngestManagerAppContext } from '../../plugin'; describe('test agent acks services', () => { it('should succeed on valid and matched actions', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); - const mockStartEncryptedSOPlugin = encryptedSavedObjectsMock.createStart(); - appContextService.start(({ - encryptedSavedObjectsStart: mockStartEncryptedSOPlugin, - } as unknown) as IngestManagerAppContext); - - const [ - { value: mockStartEncryptedSOClient }, - ] = mockStartEncryptedSOPlugin.getClient.mock.results; - - mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue( - Promise.resolve({ - id: 'action1', - references: [], - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - attributes: { - type: 'CONFIG_CHANGE', - agent_id: 'id', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, - }) - ); mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ @@ -65,7 +39,7 @@ describe('test agent acks services', () => { } as SavedObjectsBulkResponse) ); - const agentActions = await acknowledgeAgentActions( + await acknowledgeAgentActions( mockSavedObjectsClient, ({ id: 'id', @@ -81,125 +55,32 @@ describe('test agent acks services', () => { } as AgentEvent, ] ); - expect(agentActions).toEqual([ - ({ - type: 'CONFIG_CHANGE', - id: 'action1', - agent_id: 'id', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - } as unknown) as AgentAction, - ]); }); it('should update config field on the agent if a policy change is acknowledged', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); - const mockStartEncryptedSOPlugin = encryptedSavedObjectsMock.createStart(); - appContextService.start(({ - encryptedSavedObjectsStart: mockStartEncryptedSOPlugin, - } as unknown) as IngestManagerAppContext); - const [ - { value: mockStartEncryptedSOClient }, - ] = mockStartEncryptedSOPlugin.getClient.mock.results; - - mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue( - Promise.resolve({ - id: 'action1', - references: [], - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - attributes: { - type: 'CONFIG_CHANGE', - agent_id: 'id', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - data: JSON.stringify({ - config: { - id: 'policy1', - revision: 4, - settings: { - monitoring: { - enabled: true, - use_output: 'default', - logs: true, - metrics: true, - }, - }, - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://localhost:9200'], - }, - }, - inputs: [ - { - id: 'f2293360-b57c-11ea-8bd3-7bd51e425399', - name: 'system-1', - type: 'logs', - use_output: 'default', - meta: { - package: { - name: 'system', - version: '0.3.0', - }, - }, - dataset: { - namespace: 'default', - }, - streams: [ - { - id: 'logs-system.syslog', - dataset: { - name: 'system.syslog', - }, - paths: ['/var/log/messages*', '/var/log/syslog*'], - exclude_files: ['.gz$'], - multiline: { - pattern: '^\\s', - match: 'after', - }, - processors: [ - { - add_locale: null, - }, - { - add_fields: { - target: '', - fields: { - 'ecs.version': '1.5.0', - }, - }, - }, - ], - }, - ], - }, - ], - }, - }), - }, - }) - ); + const actionAttributes = { + type: 'CONFIG_CHANGE', + policy_id: 'policy1', + policy_revision: 4, + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + ack_data: JSON.stringify({ packages: ['system'] }), + }; mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ saved_objects: [ { - id: 'action1', + id: 'action2', references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, - attributes: { - type: 'CONFIG_CHANGE', - agent_id: 'id', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, + attributes: actionAttributes, }, ], - } as SavedObjectsBulkResponse) + } as SavedObjectsBulkResponse) ); await acknowledgeAgentActions( @@ -214,13 +95,13 @@ describe('test agent acks services', () => { type: 'ACTION_RESULT', subtype: 'CONFIG', timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: 'action1', + action_id: 'action2', agent_id: 'id', } as AgentEvent, ] ); expect(mockSavedObjectsClient.bulkUpdate).toBeCalled(); - expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(2); + expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(1); expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0][0]).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -237,111 +118,25 @@ describe('test agent acks services', () => { it('should not update config field on the agent if a policy change for an old revision is acknowledged', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); - const mockStartEncryptedSOPlugin = encryptedSavedObjectsMock.createStart(); - appContextService.start(({ - encryptedSavedObjectsStart: mockStartEncryptedSOPlugin, - } as unknown) as IngestManagerAppContext); - - const [ - { value: mockStartEncryptedSOClient }, - ] = mockStartEncryptedSOPlugin.getClient.mock.results; - - mockStartEncryptedSOClient.getDecryptedAsInternalUser.mockReturnValue( - Promise.resolve({ - id: 'action1', - references: [], - type: AGENT_ACTION_SAVED_OBJECT_TYPE, - attributes: { - type: 'CONFIG_CHANGE', - agent_id: 'id', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - data: JSON.stringify({ - config: { - id: 'policy1', - revision: 4, - settings: { - monitoring: { - enabled: true, - use_output: 'default', - logs: true, - metrics: true, - }, - }, - outputs: { - default: { - type: 'elasticsearch', - hosts: ['http://localhost:9200'], - }, - }, - inputs: [ - { - id: 'f2293360-b57c-11ea-8bd3-7bd51e425399', - name: 'system-1', - type: 'logs', - use_output: 'default', - meta: { - package: { - name: 'system', - version: '0.3.0', - }, - }, - dataset: { - namespace: 'default', - }, - streams: [ - { - id: 'logs-system.syslog', - dataset: { - name: 'system.syslog', - }, - paths: ['/var/log/messages*', '/var/log/syslog*'], - exclude_files: ['.gz$'], - multiline: { - pattern: '^\\s', - match: 'after', - }, - processors: [ - { - add_locale: null, - }, - { - add_fields: { - target: '', - fields: { - 'ecs.version': '1.5.0', - }, - }, - }, - ], - }, - ], - }, - ], - }, - }), - }, - }) - ); mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ saved_objects: [ { - id: 'action1', + id: 'action3', references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, attributes: { type: 'CONFIG_CHANGE', - agent_id: 'id', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', created_at: '2020-03-14T19:45:02.620Z', + policy_id: 'policy1', + policy_revision: 99, }, }, ], - } as SavedObjectsBulkResponse) + } as SavedObjectsBulkResponse) ); await acknowledgeAgentActions( @@ -357,13 +152,13 @@ describe('test agent acks services', () => { type: 'ACTION_RESULT', subtype: 'CONFIG', timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: 'action1', + action_id: 'action3', agent_id: 'id', } as AgentEvent, ] ); expect(mockSavedObjectsClient.bulkUpdate).toBeCalled(); - expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(1); + expect(mockSavedObjectsClient.bulkUpdate.mock.calls[0][0]).toHaveLength(0); }); it('should fail for actions that cannot be found on agent actions list', async () => { @@ -372,7 +167,7 @@ describe('test agent acks services', () => { Promise.resolve({ saved_objects: [ { - id: 'action1', + id: 'action4', error: { message: 'Not found', statusCode: 404, @@ -394,7 +189,7 @@ describe('test agent acks services', () => { type: 'ACTION_RESULT', subtype: 'CONFIG', timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: 'action2', + action_id: 'action4', agent_id: 'id', } as unknown) as AgentEvent, ] @@ -412,7 +207,7 @@ describe('test agent acks services', () => { Promise.resolve({ saved_objects: [ { - id: 'action1', + id: 'action5', references: [], type: AGENT_ACTION_SAVED_OBJECT_TYPE, attributes: { @@ -439,7 +234,7 @@ describe('test agent acks services', () => { type: 'ACTION', subtype: 'FAILED', timestamp: '2019-01-04T14:32:03.36764-05:00', - action_id: 'action1', + action_id: 'action5', agent_id: 'id', } as unknown) as AgentEvent, ] 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 87572ce405ee..d29dfcec7ef3 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -11,14 +11,15 @@ import { SavedObjectsClientContract, } from 'src/core/server'; import Boom from 'boom'; +import LRU from 'lru-cache'; import { Agent, AgentAction, + AgentPolicyAction, AgentEvent, AgentEventSOAttributes, AgentSOAttributes, AgentActionSOAttributes, - FullAgentPolicy, } from '../../types'; import { AGENT_EVENT_SAVED_OBJECT_TYPE, @@ -30,11 +31,20 @@ import { forceUnenrollAgent } from './unenroll'; const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT']; +const actionCache = new LRU({ + max: 20, + maxAge: 10 * 60 * 1000, // 10 minutes +}); + export async function acknowledgeAgentActions( soClient: SavedObjectsClientContract, agent: Agent, agentEvents: AgentEvent[] ): Promise { + if (agentEvents.length === 0) { + return []; + } + for (const agentEvent of agentEvents) { if (!isAllowedType(agentEvent.type)) { throw Boom.badRequest(`${agentEvent.type} not allowed for acknowledgment only ACTION_RESULT`); @@ -45,9 +55,9 @@ export async function acknowledgeAgentActions( .map((event) => event.action_id) .filter((actionId) => actionId !== undefined) as string[]; - let actions; + let actions: AgentAction[]; try { - actions = await getAgentActionByIds(soClient, actionIds); + actions = await fetchActionsUsingCache(soClient, actionIds); } catch (error) { if (Boom.isBoom(error) && error.output.statusCode === 404) { throw Boom.badRequest(`One or more actions cannot be found`); @@ -55,65 +65,91 @@ export async function acknowledgeAgentActions( throw error; } + const agentActionsIds: string[] = []; for (const action of actions) { - if (action.agent_id !== agent.id) { + if (action.agent_id) { + agentActionsIds.push(action.id); + } + if (action.agent_id && action.agent_id !== agent.id) { throw Boom.badRequest(`${action.id} not found`); } } - if (actions.length === 0) { - return []; - } - const isAgentUnenrolled = actions.some((action) => action.type === 'UNENROLL'); if (isAgentUnenrolled) { await forceUnenrollAgent(soClient, agent.id); } - const agentPolicy = getLatestAgentPolicyIfUpdated(agent, actions); + const configChangeAction = getLatestConfigChangePolicyActionIfUpdated(agent, actions); await soClient.bulkUpdate([ - ...(agentPolicy ? [buildUpdateAgentPolicy(agent.id, agentPolicy)] : []), - ...buildUpdateAgentActionSentAt(actionIds), + ...(configChangeAction + ? [ + { + type: AGENT_SAVED_OBJECT_TYPE, + id: agent.id, + attributes: { + policy_revision: configChangeAction.policy_revision, + packages: configChangeAction?.ack_data?.packages, + }, + }, + ] + : []), + ...buildUpdateAgentActionSentAt(agentActionsIds), ]); return actions; } -function getLatestAgentPolicyIfUpdated(agent: Agent, actions: AgentAction[]) { - return actions.reduce((acc, action) => { - if (action.type !== 'CONFIG_CHANGE') { - return acc; - } - const data = action.data || {}; +async function fetchActionsUsingCache( + soClient: SavedObjectsClientContract, + actionIds: string[] +): Promise { + const missingActionIds: string[] = []; + const actions = actionIds + .map((actionId) => { + const action = actionCache.get(actionId); + if (!action) { + missingActionIds.push(actionId); + } + return action; + }) + .filter((action): action is AgentAction => action !== undefined); + + if (missingActionIds.length === 0) { + return actions; + } - if (data?.config?.id !== agent.policy_id) { - return acc; - } + const freshActions = await getAgentActionByIds(soClient, actionIds, false); + freshActions.forEach((action) => actionCache.set(action.id, action)); - const currentRevision = (acc && acc.revision) || agent.policy_revision || 0; + return [...freshActions, ...actions]; +} - return data?.config?.revision > currentRevision ? data?.config : acc; - }, null); +function isAgentPolicyAction(action: AgentAction | AgentPolicyAction): action is AgentPolicyAction { + return (action as AgentPolicyAction).policy_id !== undefined; } -function buildUpdateAgentPolicy(agentId: string, agentPolicy: FullAgentPolicy) { - const packages = agentPolicy.inputs.reduce((acc, input) => { - const packageName = input.meta?.package?.name; - if (packageName && acc.indexOf(packageName) < 0) { - return [packageName, ...acc]; +function getLatestConfigChangePolicyActionIfUpdated( + agent: Agent, + actions: Array +): AgentPolicyAction | null { + return actions.reduce((acc, action) => { + if ( + !isAgentPolicyAction(action) || + action.type !== 'CONFIG_CHANGE' || + action.policy_id !== agent.policy_id || + (acc?.policy_revision ?? 0) < (agent.policy_revision || 0) + ) { + return acc; } - return acc; - }, []); - return { - type: AGENT_SAVED_OBJECT_TYPE, - id: agentId, - attributes: { - policy_revision: agentPolicy.revision, - packages, - }, - }; + if (action.policy_revision > (acc?.policy_revision ?? 0)) { + return action; + } + + return acc; + }, null); } function buildUpdateAgentActionSentAt( diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index c73900795238..bcb3fc7fdc7b 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -22,7 +22,13 @@ describe('test agent actions services', () => { }; mockSavedObjectsClient.create.mockReturnValue( Promise.resolve({ - attributes: {}, + attributes: { + agent_id: 'agentid', + type: 'CONFIG_CHANGE', + data: JSON.stringify({ content: 'data' }), + sent_at: '2020-03-14T19:45:02.620Z', + created_at: '2020-03-14T19:45:02.620Z', + }, } as SavedObject) ); await createAgentAction(mockSavedObjectsClient, newAgentAction); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index cd0dd9213123..851971433498 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -5,9 +5,20 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { Agent, AgentAction, AgentActionSOAttributes } from '../../../common/types/models'; +import { + Agent, + AgentAction, + AgentPolicyAction, + BaseAgentActionSOAttributes, + AgentActionSOAttributes, + AgentPolicyActionSOAttributes, +} from '../../../common/types/models'; import { AGENT_ACTION_SAVED_OBJECT_TYPE } from '../../../common/constants'; -import { savedObjectToAgentAction } from './saved_objects'; +import { + isAgentActionSavedObject, + isPolicyActionSavedObject, + savedObjectToAgentAction, +} from './saved_objects'; import { appContextService } from '../app_context'; import { nodeTypes } from '../../../../../../src/plugins/data/common'; @@ -15,15 +26,45 @@ export async function createAgentAction( soClient: SavedObjectsClientContract, newAgentAction: Omit ): Promise { - const so = await soClient.create(AGENT_ACTION_SAVED_OBJECT_TYPE, { + return createAction(soClient, newAgentAction); +} + +export function createAgentPolicyAction( + soClient: SavedObjectsClientContract, + newAgentAction: Omit +): Promise { + return createAction(soClient, newAgentAction); +} +async function createAction( + soClient: SavedObjectsClientContract, + newAgentAction: Omit +): Promise; +async function createAction( + soClient: SavedObjectsClientContract, + newAgentAction: Omit +): Promise; +async function createAction( + soClient: SavedObjectsClientContract, + newAgentAction: Omit | Omit +): Promise { + const so = await soClient.create(AGENT_ACTION_SAVED_OBJECT_TYPE, { ...newAgentAction, data: newAgentAction.data ? JSON.stringify(newAgentAction.data) : undefined, + ack_data: newAgentAction.ack_data ? JSON.stringify(newAgentAction.ack_data) : undefined, }); - const agentAction = savedObjectToAgentAction(so); - agentAction.data = newAgentAction.data; + if (isAgentActionSavedObject(so)) { + const agentAction = savedObjectToAgentAction(so); + agentAction.data = newAgentAction.data; + + return agentAction; + } else if (isPolicyActionSavedObject(so)) { + const agentAction = savedObjectToAgentAction(so); + agentAction.data = newAgentAction.data; - return agentAction; + return agentAction; + } + throw new Error('Invalid action'); } export async function getAgentActionsForCheckin( @@ -67,7 +108,8 @@ export async function getAgentActionsForCheckin( export async function getAgentActionByIds( soClient: SavedObjectsClientContract, - actionIds: string[] + actionIds: string[], + decryptData: boolean = true ) { const actions = ( await soClient.bulkGet( @@ -76,7 +118,11 @@ export async function getAgentActionByIds( type: AGENT_ACTION_SAVED_OBJECT_TYPE, })) ) - ).saved_objects.map(savedObjectToAgentAction); + ).saved_objects.map((action) => savedObjectToAgentAction(action)); + + if (!decryptData) { + return actions; + } return Promise.all( actions.map(async (action) => { @@ -93,6 +139,39 @@ export async function getAgentActionByIds( ); } +export async function getAgentPolicyActionByIds( + soClient: SavedObjectsClientContract, + actionIds: string[], + decryptData: boolean = true +) { + const actions = ( + await soClient.bulkGet( + actionIds.map((actionId) => ({ + id: actionId, + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + })) + ) + ).saved_objects.map((action) => savedObjectToAgentAction(action)); + + if (!decryptData) { + return actions; + } + + return Promise.all( + actions.map(async (action) => { + // Get decrypted actions + return savedObjectToAgentAction( + await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + AGENT_ACTION_SAVED_OBJECT_TYPE, + action.id + ) + ); + }) + ); +} + export async function getNewActionsSince(soClient: SavedObjectsClientContract, timestamp: string) { const filter = nodeTypes.function.buildNode('and', [ nodeTypes.function.buildNode( @@ -116,7 +195,26 @@ export async function getNewActionsSince(soClient: SavedObjectsClientContract, t filter, }); - return res.saved_objects.map(savedObjectToAgentAction); + return res.saved_objects + .filter(isAgentActionSavedObject) + .map((so) => savedObjectToAgentAction(so)); +} + +export async function getLatestConfigChangeAction( + soClient: SavedObjectsClientContract, + policyId: string +) { + const res = await soClient.find({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + search: policyId, + searchFields: ['policy_id'], + sortField: 'created_at', + sortOrder: 'DESC', + }); + + if (res.saved_objects[0]) { + return savedObjectToAgentAction(res.saved_objects[0]); + } } export interface ActionsService { @@ -124,6 +222,6 @@ export interface ActionsService { createAgentAction: ( soClient: SavedObjectsClientContract, - newAgentAction: AgentActionSOAttributes + newAgentAction: Omit ) => Promise; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts index eddfb0e64b84..8f586420c3ec 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts @@ -5,6 +5,7 @@ */ import { timer, from, Observable, TimeoutError } from 'rxjs'; +import { omit } from 'lodash'; import { shareReplay, distinctUntilKeyChanged, @@ -16,14 +17,7 @@ import { take, } from 'rxjs/operators'; import { SavedObjectsClientContract, KibanaRequest } from 'src/core/server'; -import { - Agent, - AgentAction, - AgentSOAttributes, - AgentPolicy, - FullAgentPolicy, -} from '../../../types'; -import { agentPolicyService } from '../../agent_policy'; +import { Agent, AgentAction, AgentPolicyAction, AgentSOAttributes } from '../../../types'; import * as APIKeysService from '../../api_keys'; import { AGENT_SAVED_OBJECT_TYPE, @@ -31,7 +25,11 @@ import { AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS, AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL, } from '../../../constants'; -import { createAgentAction, getNewActionsSince } from '../actions'; +import { + getNewActionsSince, + getLatestConfigChangeAction, + getAgentPolicyActionByIds, +} from '../actions'; import { appContextService } from '../../app_context'; import { toPromiseAbortable, AbortError, createRateLimiter } from './rxjs_utils'; @@ -54,27 +52,27 @@ function getInternalUserSOClient() { return appContextService.getInternalUserSOClient(fakeRequest); } -function createAgentPolicySharedObservable(agentPolicyId: string) { +function createNewActionsSharedObservable(): Observable { const internalSOClient = getInternalUserSOClient(); + return timer(0, AGENT_UPDATE_ACTIONS_INTERVAL_MS).pipe( - switchMap(() => - from(agentPolicyService.get(internalSOClient, agentPolicyId) as Promise) - ), - distinctUntilKeyChanged('revision'), - switchMap((data) => - from(agentPolicyService.getFullAgentPolicy(internalSOClient, agentPolicyId)) - ), + switchMap(() => { + return from(getNewActionsSince(internalSOClient, new Date().toISOString())); + }), shareReplay({ refCount: true, bufferSize: 1 }) ); } -function createNewActionsSharedObservable(): Observable { - return timer(0, AGENT_UPDATE_ACTIONS_INTERVAL_MS).pipe( - switchMap(() => { - const internalSOClient = getInternalUserSOClient(); +function createAgentPolicyActionSharedObservable(agentPolicyId: string) { + const internalSOClient = getInternalUserSOClient(); - return from(getNewActionsSince(internalSOClient, new Date().toISOString())); - }), + return timer(0, AGENT_UPDATE_ACTIONS_INTERVAL_MS).pipe( + switchMap(() => from(getLatestConfigChangeAction(internalSOClient, agentPolicyId))), + filter((data): data is AgentPolicyAction => data !== undefined), + distinctUntilKeyChanged('id'), + switchMap((data) => + from(getAgentPolicyActionByIds(internalSOClient, [data.id]).then((r) => r[0])) + ), shareReplay({ refCount: true, bufferSize: 1 }) ); } @@ -102,47 +100,35 @@ async function getOrCreateAgentDefaultOutputAPIKey( return outputAPIKey.key; } -function shouldCreateAgentPolicyAction(agent: Agent, agentPolicy: FullAgentPolicy | null): boolean { - if (!agentPolicy || !agentPolicy.revision) { - return false; - } - const isAgentPolicyOutdated = - !agent.policy_revision || agent.policy_revision < agentPolicy.revision; - if (!isAgentPolicyOutdated) { - return false; - } - - return true; -} - -async function createAgentActionFromAgentPolicy( +async function createAgentActionFromPolicyAction( soClient: SavedObjectsClientContract, agent: Agent, - policy: FullAgentPolicy | null + policyAction: AgentPolicyAction ) { - // Deep clone !not supporting Date, and undefined value. - const newAgentPolicy = JSON.parse(JSON.stringify(policy)); + const newAgentAction: AgentAction = Object.assign( + omit( + // Faster than clone + JSON.parse(JSON.stringify(policyAction)) as AgentPolicyAction, + 'policy_id', + 'policy_revision' + ), + { + agent_id: agent.id, + } + ); // Mutate the policy to set the api token for this agent - newAgentPolicy.outputs.default.api_key = await getOrCreateAgentDefaultOutputAPIKey( + newAgentAction.data.config.outputs.default.api_key = await getOrCreateAgentDefaultOutputAPIKey( soClient, agent ); - const policyChangeAction = await createAgentAction(soClient, { - agent_id: agent.id, - type: 'CONFIG_CHANGE', - data: { config: newAgentPolicy } as any, - created_at: new Date().toISOString(), - sent_at: undefined, - }); - - return [policyChangeAction]; + return [newAgentAction]; } export function agentCheckinStateNewActionsFactory() { // Shared Observables - const agentPolicies$ = new Map>(); + const agentPolicies$ = new Map>(); const newActions$ = createNewActionsSharedObservable(); // Rx operators const rateLimiter = createRateLimiter( @@ -162,7 +148,7 @@ export function agentCheckinStateNewActionsFactory() { } const agentPolicyId = agent.policy_id; if (!agentPolicies$.has(agentPolicyId)) { - agentPolicies$.set(agentPolicyId, createAgentPolicySharedObservable(agentPolicyId)); + agentPolicies$.set(agentPolicyId, createAgentPolicyActionSharedObservable(agentPolicyId)); } const agentPolicy$ = agentPolicies$.get(agentPolicyId); if (!agentPolicy$) { @@ -174,15 +160,22 @@ export function agentCheckinStateNewActionsFactory() { // Set a timeout 3s before the real timeout to have a chance to respond an empty response before socket timeout Math.max((appContextService.getConfig()?.fleet.pollingRequestTimeout ?? 0) - 3000, 3000) ), - filter((agentPolicy) => shouldCreateAgentPolicyAction(agent, agentPolicy)), + filter( + (action) => + agent.policy_id !== undefined && + action.policy_revision !== undefined && + action.policy_id !== undefined && + action.policy_id === agent.policy_id && + (!agent.policy_revision || action.policy_revision > agent.policy_revision) + ), rateLimiter(), - mergeMap((agentPolicy) => createAgentActionFromAgentPolicy(soClient, agent, agentPolicy)), + mergeMap((policyAction) => createAgentActionFromPolicyAction(soClient, agent, policyAction)), merge(newActions$), mergeMap(async (data) => { if (!data) { return; } - const newActions = data.filter((action) => action.agent_id); + const newActions = data.filter((action) => action.agent_id === agent.id); if (newActions.length === 0) { return; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts index 2ab5cc8139f6..3ae664c086da 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts @@ -6,7 +6,15 @@ import Boom from 'boom'; import { SavedObject } from 'src/core/server'; -import { Agent, AgentSOAttributes, AgentAction, AgentActionSOAttributes } from '../../types'; +import { + Agent, + AgentSOAttributes, + AgentAction, + AgentPolicyAction, + AgentActionSOAttributes, + AgentPolicyActionSOAttributes, + BaseAgentActionSOAttributes, +} from '../../types'; export function savedObjectToAgent(so: SavedObject): Agent { if (so.error) { @@ -27,7 +35,13 @@ export function savedObjectToAgent(so: SavedObject): Agent { }; } -export function savedObjectToAgentAction(so: SavedObject): AgentAction { +export function savedObjectToAgentAction(so: SavedObject): AgentAction; +export function savedObjectToAgentAction( + so: SavedObject +): AgentPolicyAction; +export function savedObjectToAgentAction( + so: SavedObject +): AgentAction | AgentPolicyAction { if (so.error) { if (so.error.statusCode === 404) { throw Boom.notFound(so.error.message); @@ -36,9 +50,42 @@ export function savedObjectToAgentAction(so: SavedObject +): so is SavedObject { + return (so.attributes as AgentActionSOAttributes).agent_id !== undefined; +} + +export function isPolicyActionSavedObject( + so: SavedObject +): so is SavedObject { + return (so.attributes as AgentPolicyActionSOAttributes).policy_id !== undefined; +} diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts index cc26e631a621..2f60c74d3514 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.test.ts @@ -42,36 +42,62 @@ const mockInstallationUpdateFail: SavedObject = { }; describe('install', () => { describe('getInstallType', () => { - it('should return correct type when installing and no other version is currently installed', () => {}); - const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined }); - expect(installTypeInstall).toBe('install'); + it('should return correct type when installing and no other version is currently installed', () => { + const installTypeInstall = getInstallType({ pkgVersion: '1.0.0', installedPkg: undefined }); + expect(installTypeInstall).toBe('install'); - it('should return correct type when installing the same version', () => {}); - const installTypeReinstall = getInstallType({ - pkgVersion: '1.0.0', - installedPkg: mockInstallation, + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'update').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'reinstall').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'reupdate').toBe(false); + // @ts-expect-error can only be 'install' if no installedPkg given + expect(installTypeInstall === 'rollback').toBe(false); }); - expect(installTypeReinstall).toBe('reinstall'); - it('should return correct type when moving from one version to another', () => {}); - const installTypeUpdate = getInstallType({ - pkgVersion: '1.0.1', - installedPkg: mockInstallation, + it('should return correct type when installing the same version', () => { + const installTypeReinstall = getInstallType({ + pkgVersion: '1.0.0', + installedPkg: mockInstallation, + }); + expect(installTypeReinstall).toBe('reinstall'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeReinstall === 'install').toBe(false); + }); + + it('should return correct type when moving from one version to another', () => { + const installTypeUpdate = getInstallType({ + pkgVersion: '1.0.1', + installedPkg: mockInstallation, + }); + expect(installTypeUpdate).toBe('update'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeUpdate === 'install').toBe(false); }); - expect(installTypeUpdate).toBe('update'); - it('should return correct type when update fails and trys again', () => {}); - const installTypeReupdate = getInstallType({ - pkgVersion: '1.0.1', - installedPkg: mockInstallationUpdateFail, + it('should return correct type when update fails and trys again', () => { + const installTypeReupdate = getInstallType({ + pkgVersion: '1.0.1', + installedPkg: mockInstallationUpdateFail, + }); + expect(installTypeReupdate).toBe('reupdate'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeReupdate === 'install').toBe(false); }); - expect(installTypeReupdate).toBe('reupdate'); - it('should return correct type when attempting to rollback from a failed update', () => {}); - const installTypeRollback = getInstallType({ - pkgVersion: '1.0.0', - installedPkg: mockInstallationUpdateFail, + it('should return correct type when attempting to rollback from a failed update', () => { + const installTypeRollback = getInstallType({ + pkgVersion: '1.0.0', + installedPkg: mockInstallationUpdateFail, + }); + expect(installTypeRollback).toBe('rollback'); + + // @ts-expect-error cannot be 'install' if given installedPkg + expect(installTypeRollback === 'install').toBe(false); }); - expect(installTypeRollback).toBe('rollback'); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index e6144e030959..54b9c4d3fbb1 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -200,22 +200,20 @@ export async function installPackage({ ); // if this is an update or retrying an update, delete the previous version's pipelines - if (installType === 'update' || installType === 'reupdate') { + if ((installType === 'update' || installType === 'reupdate') && installedPkg) { await deletePreviousPipelines( callCluster, savedObjectsClient, pkgName, - // @ts-ignore installType conditions already check for existence of installedPkg installedPkg.attributes.version ); } // pipelines from a different version may have installed during a failed update - if (installType === 'rollback') { + if (installType === 'rollback' && installedPkg) { await deletePreviousPipelines( callCluster, savedObjectsClient, pkgName, - // @ts-ignore installType conditions already check for existence of installedPkg installedPkg.attributes.install_version ); } @@ -354,17 +352,32 @@ export async function ensurePackagesCompletedInstall( return installingPackages; } -export function getInstallType({ - pkgVersion, - installedPkg, -}: { +interface NoPkgArgs { pkgVersion: string; - installedPkg: SavedObject | undefined; -}): InstallType { - const isInstalledPkg = !!installedPkg; - const currentPkgVersion = installedPkg?.attributes.version; - const lastStartedInstallVersion = installedPkg?.attributes.install_version; - if (!isInstalledPkg) return 'install'; + installedPkg?: undefined; +} + +interface HasPkgArgs { + pkgVersion: string; + installedPkg: SavedObject; +} + +type OnlyInstall = Extract; +type NotInstall = Exclude; + +// overloads +export function getInstallType(args: NoPkgArgs): OnlyInstall; +export function getInstallType(args: HasPkgArgs): NotInstall; +export function getInstallType(args: NoPkgArgs | HasPkgArgs): OnlyInstall | NotInstall; + +// implementation +export function getInstallType(args: NoPkgArgs | HasPkgArgs): OnlyInstall | NotInstall { + const { pkgVersion, installedPkg } = args; + if (!installedPkg) return 'install'; + + const currentPkgVersion = installedPkg.attributes.version; + const lastStartedInstallVersion = installedPkg.attributes.install_version; + if (pkgVersion === currentPkgVersion && pkgVersion !== lastStartedInstallVersion) return 'rollback'; if (pkgVersion === currentPkgVersion) return 'reinstall'; diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index ec3a05a4fa39..f02057bae159 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -170,6 +170,12 @@ export async function setupFleet( }); }) ); + + await Promise.all( + agentPolicies.map((agentPolicy) => + agentPolicyService.createFleetPolicyChangeAction(soClient, agentPolicy.id) + ) + ); } function generateRandomPassword() { diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index 2746dfcd00ce..d00491afef72 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -16,7 +16,10 @@ export { AgentEvent, AgentEventSOAttributes, AgentAction, + AgentPolicyAction, + BaseAgentActionSOAttributes, AgentActionSOAttributes, + AgentPolicyActionSOAttributes, PackagePolicy, PackagePolicyInput, PackagePolicyInputStream, diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index 5ad98cfd4062..b249705fe6c2 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -62,12 +62,7 @@ export const AgentEventSchema = schema.object({ }); export const NewAgentActionSchema = schema.object({ - type: schema.oneOf([ - schema.literal('CONFIG_CHANGE'), - schema.literal('DATA_DUMP'), - schema.literal('RESUME'), - schema.literal('PAUSE'), - ]), + type: schema.oneOf([schema.literal('CONFIG_CHANGE'), schema.literal('UNENROLL')]), data: schema.maybe(schema.any()), sent_at: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json index 75e5e9b5d6c5..38d28fbba20b 100644 --- a/x-pack/plugins/ingest_pipelines/kibana.json +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -3,7 +3,7 @@ "version": "8.0.0", "server": true, "ui": true, - "requiredPlugins": ["licensing", "management"], + "requiredPlugins": ["licensing", "management", "features"], "optionalPlugins": ["security", "usageCollection"], "configPath": ["xpack", "ingest_pipelines"], "requiredBundles": ["esUiShared", "kibanaReact"] diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx index b12f32452816..38c652f41e5e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/__jest__/pipeline_processors_editor.test.tsx @@ -184,5 +184,14 @@ describe('Pipeline Editor', () => { expect(find('processors>0.moveItemButton').props().disabled).toBe(true); expect(find('processors>1.moveItemButton').props().disabled).toBe(true); }); + + it('can move a processor into an empty tree', () => { + const { actions } = testBed; + actions.moveProcessor('processors>0', 'onFailure.dropButtonEmptyTree'); + const [onUpdateResult2] = onUpdate.mock.calls[onUpdate.mock.calls.length - 1]; + const data = onUpdateResult2.getData(); + expect(data.processors).toEqual([testProcessors.processors[1]]); + expect(data.on_failure).toEqual([testProcessors.processors[0]]); + }); }); }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx index 276d684e3dca..4aabcc1d59d7 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/add_processor_button.tsx @@ -21,6 +21,7 @@ export const AddProcessorButton: FunctionComponent = (props) => { return ( = ({ processor }) if (type?.length) { const formDescriptor = getProcessorDescriptor(type as any); - if (formDescriptor?.FieldsComponent) { - const renderedFields = ( + if (formDescriptor) { + const renderedFields = formDescriptor.FieldsComponent ? ( - ); + ) : null; return ( <> {renderedFields ? ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/properties_field.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/properties_field.tsx new file mode 100644 index 000000000000..404a80161068 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/common_fields/properties_field.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiComboBoxOptionOption } from '@elastic/eui'; +import { ComboBoxField, FIELD_TYPES, UseField } from '../../../../../../../shared_imports'; + +import { FieldsConfig, to } from '../shared'; + +const fieldsConfig: FieldsConfig = { + properties: { + type: FIELD_TYPES.COMBO_BOX, + deserializer: to.arrayOfStrings, + serializer: (v: string[]) => (v.length ? v : undefined), + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.commonFields.propertiesFieldLabel', + { + defaultMessage: 'Properties (optional)', + } + ), + }, +}; + +interface Props { + helpText?: React.ReactNode; + propertyOptions?: EuiComboBoxOptionOption[]; +} + +export const PropertiesField: FunctionComponent = ({ helpText, propertyOptions }) => { + return ( + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx index 87b6cb76cdcc..7bc299532df9 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/drop.tsx @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FunctionComponent } from 'react'; - /** * This fields component has no unique fields */ -export const Drop: FunctionComponent = () => { - return null; -}; +export const Drop = undefined; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx index c0624c988061..937fa4d3c4d8 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/geoip.tsx @@ -9,18 +9,13 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCode } from '@elastic/eui'; -import { - FIELD_TYPES, - UseField, - Field, - ComboBoxField, - ToggleField, -} from '../../../../../../shared_imports'; +import { FIELD_TYPES, UseField, Field, ToggleField } from '../../../../../../shared_imports'; import { FieldNameField } from './common_fields/field_name_field'; import { IgnoreMissingField } from './common_fields/ignore_missing_field'; import { FieldsConfig, from, to } from './shared'; import { TargetField } from './common_fields/target_field'; +import { PropertiesField } from './common_fields/properties_field'; const fieldsConfig: FieldsConfig = { /* Optional field config */ @@ -42,21 +37,6 @@ const fieldsConfig: FieldsConfig = { ), }, - properties: { - type: FIELD_TYPES.COMBO_BOX, - deserializer: to.arrayOfStrings, - label: i18n.translate('xpack.ingestPipelines.pipelineEditor.geoIPForm.propertiesFieldLabel', { - defaultMessage: 'Properties (optional)', - }), - helpText: i18n.translate( - 'xpack.ingestPipelines.pipelineEditor.geoIPForm.propertiesFieldHelpText', - { - defaultMessage: - 'Properties added to the target field. Valid properties depend on the database file used.', - } - ), - }, - first_only: { type: FIELD_TYPES.TOGGLE, defaultValue: true, @@ -95,10 +75,14 @@ export const GeoIP: FunctionComponent = () => { - diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts index e83560b4a44c..e211d682ab0f 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/index.ts @@ -34,5 +34,9 @@ export { SetProcessor } from './set'; export { SetSecurityUser } from './set_security_user'; export { Split } from './split'; export { Sort } from './sort'; +export { Trim } from './trim'; +export { Uppercase } from './uppercase'; +export { UrlDecode } from './url_decode'; +export { UserAgent } from './user_agent'; export { FormFieldsComponent } from './shared'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/trim.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/trim.tsx new file mode 100644 index 000000000000..aca5a3b4121b --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/trim.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; + +export const Trim: FunctionComponent = () => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/uppercase.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/uppercase.tsx new file mode 100644 index 000000000000..336b68f8c2b7 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/uppercase.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; + +export const Uppercase: FunctionComponent = () => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/url_decode.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/url_decode.tsx new file mode 100644 index 000000000000..196645a89f70 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/url_decode.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; + +export const UrlDecode: FunctionComponent = () => { + return ( + <> + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/user_agent.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/user_agent.tsx new file mode 100644 index 000000000000..8395833c09f2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/manage_processor_form/processors/user_agent.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { EuiComboBoxOptionOption } from '@elastic/eui'; +import { FIELD_TYPES, UseField, Field } from '../../../../../../shared_imports'; + +import { FieldsConfig } from './shared'; +import { IgnoreMissingField } from './common_fields/ignore_missing_field'; +import { FieldNameField } from './common_fields/field_name_field'; +import { TargetField } from './common_fields/target_field'; +import { PropertiesField } from './common_fields/properties_field'; + +const propertyOptions: EuiComboBoxOptionOption[] = [ + { label: 'name' }, + { label: 'os' }, + { label: 'device' }, + { label: 'original' }, + { label: 'version' }, +]; + +const fieldsConfig: FieldsConfig = { + /* Optional fields config */ + regex_file: { + type: FIELD_TYPES.TEXT, + deserializer: String, + label: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.userAgentForm.regexFileFieldLabel', + { + defaultMessage: 'Regex file (optional)', + } + ), + helpText: i18n.translate( + 'xpack.ingestPipelines.pipelineEditor.userAgentForm.regexFileFieldHelpText', + { + defaultMessage: + 'A filename containing the regular expressions for parsing the user agent string.', + } + ), + }, +}; + +export const UserAgent: FunctionComponent = () => { + return ( + <> + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss index a54cc994ab73..25e4eb7320bf 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.scss @@ -51,6 +51,10 @@ } } + &__addProcessorButton { + width: fit-content; + } + &__onFailureHandlerContainer { margin-top: $euiSizeS; margin-bottom: $euiSizeS; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx index 4458bd66c88d..8b344a137f3a 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processors_tree/processors_tree.tsx @@ -7,12 +7,14 @@ import React, { FunctionComponent, memo, useRef, useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, keys } from '@elastic/eui'; import { List, WindowScroller } from 'react-virtualized'; +import { DropSpecialLocations } from '../../constants'; import { ProcessorInternal, ProcessorSelector } from '../../types'; import { selectorToDataTestSubject } from '../../utils'; +import { AddProcessorButton } from '../add_processor_button'; + +import { PrivateTree, DropZoneButton } from './components'; import './processors_tree.scss'; -import { AddProcessorButton } from '../add_processor_button'; -import { PrivateTree } from './components'; export interface ProcessorInfo { id: string; @@ -96,8 +98,25 @@ export const ProcessorsTree: FunctionComponent = memo((props) => { /> - - + + + {!processors.length && ( + { + event.preventDefault(); + onAction({ + type: 'move', + payload: { + destination: baseSelector.concat(DropSpecialLocations.top), + source: movingProcessor!.selector, + }, + }); + }} + /> + )} { onAction({ type: 'addProcessor', payload: { target: baseSelector } }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx index 9de371f8d002..95a8d35c119a 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/shared/map_processor_type_to_form.tsx @@ -41,6 +41,10 @@ import { SetSecurityUser, Split, Sort, + Trim, + Uppercase, + UrlDecode, + UserAgent, FormFieldsComponent, } from '../manage_processor_form/processors'; @@ -404,28 +408,28 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { }), }, trim: { - FieldsComponent: undefined, // TODO: Implement + FieldsComponent: Trim, docLinkPath: '/trim-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.trim', { defaultMessage: 'Trim', }), }, uppercase: { - FieldsComponent: undefined, // TODO: Implement + FieldsComponent: Uppercase, docLinkPath: '/uppercase-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.uppercase', { defaultMessage: 'Uppercase', }), }, urldecode: { - FieldsComponent: undefined, // TODO: Implement + FieldsComponent: UrlDecode, docLinkPath: '/urldecode-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.urldecode', { defaultMessage: 'URL decode', }), }, user_agent: { - FieldsComponent: undefined, // TODO: Implement + FieldsComponent: UserAgent, docLinkPath: '/user-agent-processor.html', label: i18n.translate('xpack.ingestPipelines.processors.label.userAgent', { defaultMessage: 'User agent', diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx index ccb50376dddb..88148f1bc574 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -51,7 +51,7 @@ export const PipelinesList: React.FunctionComponent = ({ const [pipelinesToDelete, setPipelinesToDelete] = useState([]); - const { data, isLoading, error, sendRequest } = services.api.useLoadPipelines(); + const { data, isLoading, error, resendRequest } = services.api.useLoadPipelines(); // Track component loaded useEffect(() => { @@ -98,7 +98,7 @@ export const PipelinesList: React.FunctionComponent = ({ } else if (data?.length) { content = ( = ({ defaultMessage="Unable to load pipelines. {reloadLink}" values={{ reloadLink: ( - + = ({ callback={(deleteResponse) => { if (deleteResponse?.hasDeletedPipelines) { // reload pipelines list - sendRequest(); + resendRequest(); setSelectedPipeline(undefined); goHome(); } diff --git a/x-pack/plugins/ingest_pipelines/server/plugin.ts b/x-pack/plugins/ingest_pipelines/server/plugin.ts index 7a78bf608b8e..12668e7c4ead 100644 --- a/x-pack/plugins/ingest_pipelines/server/plugin.ts +++ b/x-pack/plugins/ingest_pipelines/server/plugin.ts @@ -25,7 +25,7 @@ export class IngestPipelinesPlugin implements Plugin { this.apiRoutes = new ApiRoutes(); } - public setup({ http }: CoreSetup, { licensing, security }: Dependencies) { + public setup({ http }: CoreSetup, { licensing, security, features }: Dependencies) { this.logger.debug('ingest_pipelines: setup'); const router = http.createRouter(); @@ -44,6 +44,19 @@ export class IngestPipelinesPlugin implements Plugin { } ); + features.registerElasticsearchFeature({ + id: 'ingest_pipelines', + management: { + ingest: ['ingest_pipelines'], + }, + privileges: [ + { + ui: [], + requiredClusterPrivileges: ['manage_pipeline', 'cluster:monitor/nodes/info'], + }, + ], + }); + this.apiRoutes.setup({ router, license: this.license, diff --git a/x-pack/plugins/ingest_pipelines/server/types.ts b/x-pack/plugins/ingest_pipelines/server/types.ts index 261317daa26d..c5d9158caa56 100644 --- a/x-pack/plugins/ingest_pipelines/server/types.ts +++ b/x-pack/plugins/ingest_pipelines/server/types.ts @@ -7,11 +7,13 @@ import { IRouter } from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { License } from './services'; import { isEsError } from './shared_imports'; export interface Dependencies { security: SecurityPluginSetup; + features: FeaturesPluginSetup; licensing: LicensingPluginSetup; } diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index b8747fc1f0cd..67d9d5ef6448 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -8,7 +8,7 @@ "data", "expressions", "navigation", - "kibanaLegacy", + "urlForwarding", "visualizations", "dashboard", "charts" diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss index 5b968abd0c06..954fbfadf159 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_index.scss @@ -1,3 +1,2 @@ @import 'config_panel'; -@import 'dimension_popover'; @import 'layer_panel'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss index 62bc6d7ed7cc..ab53ff983ca2 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_layer_panel.scss @@ -43,6 +43,14 @@ min-height: $euiSizeXXL; } +.lnsLayerPanel__anchor { + width: 100%; +} + +.lnsLayerPanel__dndGrab { + padding: $euiSizeS; +} + .lnsLayerPanel__styleEditor { width: $euiSize * 30; padding: $euiSizeS; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss similarity index 51% rename from x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss rename to x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss index 691cda9ff0d7..98036c7f31bd 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/_dimension_popover.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.scss @@ -9,3 +9,10 @@ display: block; word-break: break-word; } + +// todo: remove after closing https://github.com/elastic/eui/issues/3548 +.lnsDimensionPopover__fixTranslateDnd { + // sass-lint:disable-block no-important + transform: none !important; +} + diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx index 8d31e1bcc2e6..a90bd8122d18 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_popover.tsx @@ -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 './dimension_popover.scss'; import React from 'react'; import { EuiPopover } from '@elastic/eui'; @@ -31,6 +32,7 @@ export function DimensionPopover({ = { + terms: i18n.translate('xpack.lens.indexPattern.groupingOverallTerms', { + defaultMessage: 'Overall top {field}', + values: { field: fieldName }, + }), + filters: i18n.translate('xpack.lens.indexPattern.groupingOverallFilters', { + defaultMessage: 'Top values for each custom query', + }), + date_histogram: i18n.translate('xpack.lens.indexPattern.groupingOverallDateHistogram', { + defaultMessage: 'Top values for each {field}', + values: { field: fieldName }, + }), + }; + + const bottomLevelCopy: Record = { + terms: i18n.translate('xpack.lens.indexPattern.groupingSecondTerms', { + defaultMessage: 'Top values for each {target}', + values: { target: target.fieldName }, + }), + filters: i18n.translate('xpack.lens.indexPattern.groupingSecondFilters', { + defaultMessage: 'Overall top {target}', + values: { target: target.fieldName }, + }), + date_histogram: i18n.translate('xpack.lens.indexPattern.groupingSecondDateHistogram', { + defaultMessage: 'Overall top {target}', + values: { target: target.fieldName }, + }), + }; + return ( <> @@ -73,34 +104,14 @@ export function BucketNestingEditor({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx index 038b51b92228..d5f0110f071f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/popover_editor.tsx @@ -160,6 +160,11 @@ export function PopoverEditor(props: PopoverEditorProps) { compatibleWithCurrentField ? '' : ' incompatible' }`, onClick() { + // todo: when moving from terms agg to filters, we want to create a filter `$field.name : *` + // it probably has to be re-thought when removing the field name. + const isTermsToFilters = + selectedColumn?.operationType === 'terms' && operationType === 'filters'; + if (!selectedColumn || !compatibleWithCurrentField) { const possibleFields = fieldByOperation[operationType] || []; @@ -186,7 +191,7 @@ export function PopoverEditor(props: PopoverEditorProps) { trackUiEvent(`indexpattern_dimension_operation_${operationType}`); return; } - if (incompatibleSelectedOperationType) { + if (incompatibleSelectedOperationType && !isTermsToFilters) { setInvalidOperationType(null); } if (selectedColumn.operationType === operationType) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index e2ca93350484..3b3750cf7c56 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -263,6 +263,7 @@ export function getIndexPatternDatasource({ data, savedObjects: core.savedObjects, docLinks: core.docLinks, + http: core.http, }} > ({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index 4e081da2c6dc..bb1aef856de7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -49,7 +49,11 @@ export const countOperation: OperationDefinition = { scale: 'ratio', sourceField: field.name, params: - previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined, + previousColumn?.dataType === 'number' && + previousColumn.params && + 'format' in previousColumn.params + ? previousColumn.params + : undefined, }; }, toEsAggsConfig: (column, columnId) => ({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.scss new file mode 100644 index 000000000000..6838812e4b99 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.scss @@ -0,0 +1,3 @@ +.lnsIndexPatternDimensionEditor__filtersEditor { + width: $euiSize * 60; +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx new file mode 100644 index 000000000000..4d4b4018d75a --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.test.tsx @@ -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 React, { MouseEventHandler } from 'react'; +import { shallow } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiPopover, EuiLink } from '@elastic/eui'; +import { createMockedIndexPattern } from '../../../mocks'; +import { FilterPopover, QueryInput, LabelInput } from './filter_popover'; + +jest.mock('.', () => ({ + isQueryValid: () => true, + defaultLabel: 'label', +})); + +const defaultProps = { + filter: { + input: { query: 'bytes >= 1', language: 'kuery' }, + label: 'More than one', + id: '1', + }, + setFilter: jest.fn(), + indexPattern: createMockedIndexPattern(), + Button: ({ onClick }: { onClick: MouseEventHandler }) => ( + trigger + ), + isOpenByCreation: true, + setIsOpenByCreation: jest.fn(), +}; + +describe('filter popover', () => { + jest.mock('../../../../../../../../src/plugins/data/public', () => ({ + QueryStringInput: () => { + return 'QueryStringInput'; + }, + })); + it('should be open if is open by creation', () => { + const setIsOpenByCreation = jest.fn(); + const instance = shallow( + + ); + expect(instance.find(EuiPopover).prop('isOpen')).toEqual(true); + act(() => { + instance.find(EuiPopover).prop('closePopover')!(); + }); + instance.update(); + expect(setIsOpenByCreation).toHaveBeenCalledWith(false); + }); + it('should call setFilter when modifying QueryInput', () => { + const setFilter = jest.fn(); + const instance = shallow(); + instance.find(QueryInput).prop('onChange')!({ + query: 'modified : query', + language: 'lucene', + }); + expect(setFilter).toHaveBeenCalledWith({ + input: { + language: 'lucene', + query: 'modified : query', + }, + label: 'More than one', + id: '1', + }); + }); + it('should call setFilter when modifying LabelInput', () => { + const setFilter = jest.fn(); + const instance = shallow(); + instance.find(LabelInput).prop('onChange')!('Modified label'); + expect(setFilter).toHaveBeenCalledWith({ + input: { + language: 'kuery', + query: 'bytes >= 1', + }, + label: 'Modified label', + id: '1', + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx new file mode 100644 index 000000000000..cdfa19f53a13 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filter_popover.tsx @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import './filter_popover.scss'; + +import React, { MouseEventHandler, useState } from 'react'; +import { useDebounce } from 'react-use'; +import { EuiPopover, EuiFieldText, EuiSpacer, keys } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FilterValue, defaultLabel, isQueryValid } from '.'; +import { IndexPattern } from '../../../types'; +import { QueryStringInput, Query } from '../../../../../../../../src/plugins/data/public'; + +export const FilterPopover = ({ + filter, + setFilter, + indexPattern, + Button, + isOpenByCreation, + setIsOpenByCreation, +}: { + filter: FilterValue; + setFilter: Function; + indexPattern: IndexPattern; + Button: React.FunctionComponent<{ onClick: MouseEventHandler }>; + isOpenByCreation: boolean; + setIsOpenByCreation: Function; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const inputRef = React.useRef(); + + const setPopoverOpen = (isOpen: boolean) => { + setIsPopoverOpen(isOpen); + setIsOpenByCreation(isOpen); + }; + + const setFilterLabel = (label: string) => setFilter({ ...filter, label }); + const setFilterQuery = (input: Query) => setFilter({ ...filter, input }); + + const getPlaceholder = (query: Query['query']) => { + if (query === '') { + return defaultLabel; + } + if (query === 'object') return JSON.stringify(query); + else { + return String(query); + } + }; + + return ( + { + setPopoverOpen(false); + }} + button={ +
`; @@ -108,7 +52,7 @@ exports[`ValidateJob renders the button and modal with a success message 1`] = ` iconSide="right" iconType="questionInCircle" isDisabled={false} - isLoading={false} + isLoading={true} onClick={[Function]} size="s" > @@ -118,52 +62,6 @@ exports[`ValidateJob renders the button and modal with a success message 1`] = ` values={Object {}} /> - - } - > - - - - - - - - , - } - } - /> - -
`; diff --git a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.d.ts b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.d.ts index 43e0a5f3eac7..35e4e189b432 100644 --- a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.d.ts +++ b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.d.ts @@ -8,7 +8,7 @@ import { FC } from 'react'; declare const ValidateJob: FC<{ getJobConfig: any; getDuration: any; - mlJobService: any; + ml: any; embedded?: boolean; setIsValid?: (valid: boolean) => void; idFilterList?: string[]; diff --git a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.js b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.js index dde6925631d3..0c079bc11cff 100644 --- a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.js +++ b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.js @@ -32,6 +32,8 @@ import { getDocLinks } from '../../util/dependency_cache'; import { VALIDATION_STATUS } from '../../../../common/constants/validation'; import { getMostSevereMessageStatus } from '../../../../common/util/validation_utils'; +import { toastNotificationServiceProvider } from '../../services/toast_notification_service'; +import { withKibana } from '../../../../../../../src/plugins/kibana_react/public'; const defaultIconType = 'questionInCircle'; const getDefaultState = () => ({ @@ -182,7 +184,7 @@ Modal.propType = { title: PropTypes.string, }; -export class ValidateJob extends Component { +export class ValidateJobUI extends Component { constructor(props) { super(props); this.state = getDefaultState(); @@ -209,25 +211,40 @@ export class ValidateJob extends Component { if (typeof job === 'object') { let shouldShowLoadingIndicator = true; - this.props.mlJobService.validateJob({ duration, fields, job }).then((data) => { - shouldShowLoadingIndicator = false; - this.setState({ - ...this.state, - ui: { - ...this.state.ui, - iconType: statusToEuiIconType(getMostSevereMessageStatus(data.messages)), - isLoading: false, - isModalVisible: true, - }, - data, - title: job.job_id, - }); - if (typeof this.props.setIsValid === 'function') { - this.props.setIsValid( - data.messages.some((m) => m.status === VALIDATION_STATUS.ERROR) === false + this.props.ml + .validateJob({ duration, fields, job }) + .then((messages) => { + shouldShowLoadingIndicator = false; + this.setState({ + ...this.state, + ui: { + ...this.state.ui, + iconType: statusToEuiIconType(getMostSevereMessageStatus(messages)), + isLoading: false, + isModalVisible: true, + }, + data: { + messages, + success: true, + }, + title: job.job_id, + }); + if (typeof this.props.setIsValid === 'function') { + this.props.setIsValid( + messages.some((m) => m.status === VALIDATION_STATUS.ERROR) === false + ); + } + }) + .catch((error) => { + const { toasts } = this.props.kibana.services.notifications; + const toastNotificationService = toastNotificationServiceProvider(toasts); + toastNotificationService.displayErrorToast( + error, + i18n.translate('xpack.ml.jobService.validateJobErrorTitle', { + defaultMessage: 'Job Validation Error', + }) ); - } - }); + }); // wait for 250ms before triggering the loading indicator // to avoid flickering when there's a loading time below @@ -335,15 +352,17 @@ export class ValidateJob extends Component { ); } } -ValidateJob.propTypes = { +ValidateJobUI.propTypes = { fields: PropTypes.object, fill: PropTypes.bool, getDuration: PropTypes.func, getJobConfig: PropTypes.func.isRequired, isCurrentJobConfig: PropTypes.bool, isDisabled: PropTypes.bool, - mlJobService: PropTypes.object.isRequired, + ml: PropTypes.object.isRequired, embedded: PropTypes.bool, setIsValid: PropTypes.func, idFilterList: PropTypes.array, }; + +export const ValidateJob = withKibana(ValidateJobUI); diff --git a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js index cc8a5abb4e9a..280dbd76d548 100644 --- a/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js +++ b/x-pack/plugins/ml/public/application/components/validate_job/validate_job_view.test.js @@ -16,6 +16,12 @@ jest.mock('../../util/dependency_cache', () => ({ }), })); +jest.mock('../../../../../../../src/plugins/kibana_react/public', () => ({ + withKibana: (comp) => { + return comp; + }, +})); + const job = { job_id: 'test-id', }; @@ -25,11 +31,16 @@ const getJobConfig = () => job; function prepareTest(messages) { const p = Promise.resolve(messages); - const mlJobService = { - validateJob: () => p, + const ml = { + validateJob: () => Promise.resolve(messages), + }; + const kibana = { + services: { + notifications: { toasts: { addDanger: jest.fn() } }, + }, }; - const component = ; + const component = ; const wrapper = shallowWithIntl(component); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 97098ea9e75c..60681fb6e7bb 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -10,7 +10,7 @@ import { distinctUntilChanged, filter } from 'rxjs/operators'; import { cloneDeep } from 'lodash'; import { ml } from '../../services/ml_api_service'; import { Dictionary } from '../../../../common/types/common'; -import { getErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '../../../../common/util/errors'; import { SavedSearchQuery } from '../../contexts/ml'; import { AnalysisConfig, @@ -486,7 +486,7 @@ export const loadEvalData = async ({ results.eval = evalResult; return results; } catch (e) { - results.error = getErrorMessage(e); + results.error = extractErrorMessage(e); return results; } }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts index c162cb2754c1..361a79d42214 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getErrorMessage } from '../../../../common/util/errors'; +import type { SearchResponse7 } from '../../../../common/types/es_client'; +import { extractErrorMessage } from '../../../../common/util/errors'; -import { EsSorting, SearchResponse7, UseDataGridReturnType } from '../../components/data_grid'; +import { EsSorting, UseDataGridReturnType } from '../../components/data_grid'; import { ml } from '../../services/ml_api_service'; import { isKeywordAndTextType } from '../common/fields'; @@ -62,7 +63,7 @@ export const getIndexData = async ( setTableItems(docs); setStatus(INDEX_STATUS.LOADED); } catch (e) { - setErrorMessage(getErrorMessage(e)); + setErrorMessage(extractErrorMessage(e)); setStatus(INDEX_STATUS.ERROR); } } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts index fde1b2610650..b0e73edff747 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts @@ -8,7 +8,7 @@ import { useEffect, useState } from 'react'; import { IndexPattern } from '../../../../../../../src/plugins/data/public'; -import { getErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '../../../../common/util/errors'; import { getIndexPatternIdFromName } from '../../util/index_utils'; import { ml } from '../../services/ml_api_service'; @@ -83,12 +83,12 @@ export const useResultsViewConfig = (jobId: string) => { setIsLoadingJobConfig(false); } } catch (e) { - setJobCapsServiceErrorMessage(getErrorMessage(e)); + setJobCapsServiceErrorMessage(extractErrorMessage(e)); setIsLoadingJobConfig(false); } } } catch (e) { - setJobConfigErrorMessage(getErrorMessage(e)); + setJobConfigErrorMessage(extractErrorMessage(e)); setIsLoadingJobConfig(false); } })(); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index eab5165a4213..74d45b86c8c4 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -22,10 +22,10 @@ import { useDataGrid, useRenderCellValue, EsSorting, - SearchResponse7, UseIndexDataReturnType, } from '../../../../components/data_grid'; -import { getErrorMessage } from '../../../../../../common/util/errors'; +import type { SearchResponse7 } from '../../../../../../common/types/es_client'; +import { extractErrorMessage } from '../../../../../../common/util/errors'; import { INDEX_STATUS } from '../../../common/analytics'; import { ml } from '../../../../services/ml_api_service'; @@ -94,7 +94,7 @@ export const useIndexData = ( setTableItems(docs); setStatus(INDEX_STATUS.LOADED); } catch (e) { - setErrorMessage(getErrorMessage(e)); + setErrorMessage(extractErrorMessage(e)); setStatus(INDEX_STATUS.ERROR); } }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx index ac1c710e1d10..f833cf4708ce 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.test.tsx @@ -11,7 +11,6 @@ import { MlContext } from '../../../../../contexts/ml'; import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value'; import { useCreateAnalyticsForm } from './use_create_analytics_form'; -import { getErrorMessage } from '../../../../../../../common/util/errors'; const getMountedHook = () => mountHook( @@ -21,28 +20,6 @@ const getMountedHook = () => ) ); -describe('getErrorMessage()', () => { - test('verify error message response formats', () => { - const customError1 = { - body: { statusCode: 403, error: 'Forbidden', message: 'the-error-message' }, - }; - const errorMessage1 = getErrorMessage(customError1); - expect(errorMessage1).toBe('Forbidden: the-error-message'); - - const customError2 = new Error('the-error-message'); - const errorMessage2 = getErrorMessage(customError2); - expect(errorMessage2).toBe('the-error-message'); - - const customError3 = { customErrorMessage: 'the-error-message' }; - const errorMessage3 = getErrorMessage(customError3); - expect(errorMessage3).toBe('{"customErrorMessage":"the-error-message"}'); - - const customError4 = { message: 'the-error-message' }; - const errorMessage4 = getErrorMessage(customError4); - expect(errorMessage4).toBe('the-error-message'); - }); -}); - describe('useCreateAnalyticsForm()', () => { test('initialization', () => { const { getLastHookValue } = getMountedHook(); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index 9612b9213d12..161dde51df43 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -8,7 +8,7 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; -import { getErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; @@ -115,7 +115,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { refresh(); } catch (e) { addRequestMessage({ - error: getErrorMessage(e), + error: extractErrorMessage(e), message: i18n.translate( 'xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob', { @@ -178,7 +178,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { }); } catch (e) { addRequestMessage({ - error: getErrorMessage(e), + error: extractErrorMessage(e), message: i18n.translate( 'xpack.ml.dataframe.analytics.create.createIndexPatternErrorMessage', { @@ -199,7 +199,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { ); } catch (e) { addRequestMessage({ - error: getErrorMessage(e), + error: extractErrorMessage(e), message: i18n.translate( 'xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList', { @@ -225,7 +225,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { }); } catch (e) { addRequestMessage({ - error: getErrorMessage(e), + error: extractErrorMessage(e), message: i18n.translate( 'xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles', { @@ -260,7 +260,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { refresh(); } catch (e) { addRequestMessage({ - error: getErrorMessage(e), + error: extractErrorMessage(e), message: i18n.translate( 'xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob', { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 9de859742438..a21be8373261 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -85,12 +85,11 @@ export const deleteAnalyticsAndDestIndex = async ( ); } if (status.destIndexDeleted?.error) { - const error = extractErrorMessage(status.destIndexDeleted.error); - toastNotificationService.displayDangerToast( + toastNotificationService.displayErrorToast( + status.destIndexDeleted.error, i18n.translate('xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage', { - defaultMessage: - 'An error occurred deleting destination index {destinationIndex}: {error}', - values: { destinationIndex, error }, + defaultMessage: 'An error occurred deleting destination index {destinationIndex}', + values: { destinationIndex }, }) ); } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.test.js index d705e47a5e90..58adf3d892f6 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_config_builder.test.js @@ -8,8 +8,6 @@ import mockAnomalyRecord from './__mocks__/mock_anomaly_record.json'; import mockDetectorsByJob from './__mocks__/mock_detectors_by_job.json'; import mockJobConfig from './__mocks__/mock_job_config.json'; -jest.mock('../../util/ml_error', () => class MLRequestFailure {}); - jest.mock('../../services/job_service', () => ({ mlJobService: { getJob() { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js index 9d0082ffcb56..bd781d32a6b0 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_job_flyout.js @@ -7,6 +7,8 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { cloneDeep, isEqual, pick } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, EuiButtonEmpty, @@ -28,8 +30,6 @@ import { loadFullJob } from '../utils'; import { validateModelMemoryLimit, validateGroupNames, isValidCustomUrls } from '../validate_job'; import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; import { withKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; import { collapseLiteralStrings } from '../../../../../../shared_imports'; import { DATAFEED_STATE } from '../../../../../../common/constants/states'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_utils.js index 5030c48a4e36..adcc576c5e35 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/edit_utils.js @@ -6,9 +6,9 @@ import { difference } from 'lodash'; import { getNewJobLimits } from '../../../../services/ml_server_info'; -import { mlJobService } from '../../../../services/job_service'; import { processCreatedBy } from '../../../../../../common/util/job_utils'; import { getSavedObjectsClient } from '../../../../util/dependency_cache'; +import { ml } from '../../../../services/ml_api_service'; export function saveJob(job, newJobData, finish) { return new Promise((resolve, reject) => { @@ -41,14 +41,9 @@ export function saveJob(job, newJobData, finish) { // if anything has changed, post the changes if (Object.keys(jobData).length) { - mlJobService - .updateJob(job.job_id, jobData) - .then((resp) => { - if (resp.success) { - saveDatafeedWrapper(); - } else { - reject(resp); - } + ml.updateJob({ jobId: job.job_id, job: jobData }) + .then(() => { + saveDatafeedWrapper(); }) .catch((error) => { reject(error); @@ -59,17 +54,17 @@ export function saveJob(job, newJobData, finish) { }); } -function saveDatafeed(datafeedData, job) { +function saveDatafeed(datafeedConfig, job) { return new Promise((resolve, reject) => { - if (Object.keys(datafeedData).length) { + if (Object.keys(datafeedConfig).length) { const datafeedId = job.datafeed_config.datafeed_id; - mlJobService.updateDatafeed(datafeedId, datafeedData).then((resp) => { - if (resp.success) { + ml.updateDatafeed({ datafeedId, datafeedConfig }) + .then(() => { resolve(); - } else { - reject(resp); - } - }); + }) + .catch((error) => { + reject(error); + }); } else { resolve(); } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js index f73dde69a3d4..a379f49a8315 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js @@ -6,6 +6,8 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, @@ -25,9 +27,7 @@ import { ml } from '../../../../../services/ml_api_service'; import { checkPermission } from '../../../../../capabilities/check_capabilities'; import { GroupList } from './group_list'; import { NewGroupInput } from './new_group_input'; -import { mlMessageBarService } from '../../../../../components/messagebar'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { getToastNotificationService } from '../../../../../services/toast_notification_service'; function createSelectedGroups(jobs, groups) { const jobIds = jobs.map((j) => j.id); @@ -160,7 +160,7 @@ export class GroupSelector extends Component { // check success of each job update if (resp.hasOwnProperty(jobId)) { if (resp[jobId].success === false) { - mlMessageBarService.notify.error(resp[jobId].error); + getToastNotificationService().displayErrorToast(resp[jobId].error); success = false; } } @@ -175,7 +175,7 @@ export class GroupSelector extends Component { } }) .catch((error) => { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); console.error(error); }); }; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 913727bda67d..21824aac18cd 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -5,17 +5,19 @@ */ import { each } from 'lodash'; -import { mlMessageBarService } from '../../../components/messagebar'; +import { i18n } from '@kbn/i18n'; import rison from 'rison-node'; import { mlJobService } from '../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../services/toast_notification_service'; -import { ml } from '../../../services/ml_api_service'; +import { + getToastNotificationService, + toastNotificationServiceProvider, +} from '../../../services/toast_notification_service'; import { getToastNotifications } from '../../../util/dependency_cache'; +import { ml } from '../../../services/ml_api_service'; import { stringMatch } from '../../../util/string_utils'; import { JOB_STATE, DATAFEED_STATE } from '../../../../../common/constants/states'; import { parseInterval } from '../../../../../common/util/parse_interval'; -import { i18n } from '@kbn/i18n'; import { mlCalendarService } from '../../../services/calendar_service'; export function loadFullJob(jobId) { @@ -60,7 +62,6 @@ export function forceStartDatafeeds(jobs, start, end, finish = () => {}) { finish(); }) .catch((error) => { - mlMessageBarService.notify.error(error); const toastNotifications = getToastNotifications(); toastNotifications.addDanger( i18n.translate('xpack.ml.jobsList.startJobErrorMessage', { @@ -81,7 +82,6 @@ export function stopDatafeeds(jobs, finish = () => {}) { finish(); }) .catch((error) => { - mlMessageBarService.notify.error(error); const toastNotifications = getToastNotifications(); toastNotifications.addDanger( i18n.translate('xpack.ml.jobsList.stopJobErrorMessage', { @@ -219,9 +219,8 @@ export async function cloneJob(jobId) { window.location.href = '#/jobs/new_job'; } catch (error) { - mlMessageBarService.notify.error(error); - const toastNotifications = getToastNotifications(); - toastNotifications.addDanger( + getToastNotificationService().displayErrorToast( + error, i18n.translate('xpack.ml.jobsList.cloneJobErrorMessage', { defaultMessage: 'Could not clone {jobId}. Job could not be found', values: { jobId }, @@ -239,13 +238,11 @@ export function closeJobs(jobs, finish = () => {}) { finish(); }) .catch((error) => { - mlMessageBarService.notify.error(error); - const toastNotifications = getToastNotifications(); - toastNotifications.addDanger( + getToastNotificationService().displayErrorToast( + error, i18n.translate('xpack.ml.jobsList.closeJobErrorMessage', { defaultMessage: 'Jobs failed to close', - }), - error + }) ); finish(); }); @@ -260,13 +257,11 @@ export function deleteJobs(jobs, finish = () => {}) { finish(); }) .catch((error) => { - mlMessageBarService.notify.error(error); - const toastNotifications = getToastNotifications(); - toastNotifications.addDanger( + getToastNotificationService().displayErrorToast( + error, i18n.translate('xpack.ml.jobsList.deleteJobErrorMessage', { defaultMessage: 'Jobs failed to delete', - }), - error + }) ); finish(); }); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts index 0011c88d2b52..6671aaa83abe 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts @@ -23,7 +23,7 @@ import { useEffect, useMemo } from 'react'; import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job'; import { ml } from '../../../../../services/ml_api_service'; import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator'; -import { ErrorResponse } from '../../../../../../../common/types/errors'; +import { MLHttpFetchError, MLResponseError } from '../../../../../../../common/util/errors'; import { useMlKibana } from '../../../../../contexts/kibana'; import { JobCreator } from '../job_creator'; @@ -36,10 +36,10 @@ export const modelMemoryEstimatorProvider = ( jobValidator: JobValidator ) => { const modelMemoryCheck$ = new Subject(); - const error$ = new Subject(); + const error$ = new Subject>(); return { - get error$(): Observable { + get error$(): Observable> { return error$.asObservable(); }, get updates$(): Observable { @@ -64,7 +64,7 @@ export const modelMemoryEstimatorProvider = ( catchError((error) => { // eslint-disable-next-line no-console console.error('Model memory limit could not be calculated', error.body); - error$.next(error.body); + error$.next(error); // fallback to the default in case estimation failed return of(DEFAULT_MODEL_MEMORY_LIMIT); }) @@ -120,7 +120,8 @@ export const useModelMemoryEstimator = ( title: i18n.translate('xpack.ml.newJob.wizard.estimateModelMemoryError', { defaultMessage: 'Model memory limit could not be calculated', }), - text: error.message, + text: + error.body.attributes?.body.error.caused_by?.reason || error.body.message || undefined, }); }) ); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/estimate_bucket_span.ts b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/estimate_bucket_span.ts index 0ec3b609b604..a87ba4c29baa 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/estimate_bucket_span.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/bucket_span_estimator/estimate_bucket_span.ts @@ -15,7 +15,7 @@ import { } from '../../../../../common/job_creator'; import { ml, BucketSpanEstimatorData } from '../../../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../../../contexts/ml'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; export enum ESTIMATE_STATUS { NOT_RUNNING, @@ -68,7 +68,7 @@ export function useEstimateBucketSpan() { const { name, error, message } = await ml.estimateBucketSpan(data); setStatus(ESTIMATE_STATUS.NOT_RUNNING); if (error === true) { - mlMessageBarService.notify.error(message); + getToastNotificationService().displayErrorToast(message); } else { jobCreator.bucketSpan = name; jobCreatorUpdate(); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx index cbbddb5bbc5b..da2e5cc0e63d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx @@ -6,7 +6,7 @@ import React, { FC, useContext, useEffect, useState } from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; @@ -94,7 +94,7 @@ export const CategorizationDetectors: FC = ({ setIsValid }) => { setFieldExamples(null); setValidationChecks([]); setOverallValidStatus(CATEGORY_EXAMPLES_VALIDATION_STATUS.INVALID); - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); } } else { setFieldExamples(null); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx index 684cb5b4e0dd..762d18a5367f 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection.tsx @@ -15,7 +15,7 @@ import { AggFieldPair } from '../../../../../../../../../common/types/fields'; import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings'; import { MetricSelector } from './metric_selector'; import { ChartGrid } from './chart_grid'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; interface Props { setIsValid: (na: boolean) => void; @@ -109,7 +109,7 @@ export const MultiMetricDetectors: FC = ({ setIsValid }) => { .loadFieldExampleValues(splitField) .then(setFieldValues) .catch((error) => { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); }); } else { setFieldValues([]); @@ -138,7 +138,7 @@ export const MultiMetricDetectors: FC = ({ setIsValid }) => { ); setLineChartsData(resp); } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartsData([]); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection_summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection_summary.tsx index f39a316440e7..cc0fbf2fc0a0 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection_summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/multi_metric_view/metric_selection_summary.tsx @@ -12,7 +12,7 @@ import { Results, ModelItem, Anomaly } from '../../../../../common/results_loade import { LineChartData } from '../../../../../common/chart_loader'; import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings'; import { ChartGrid } from './chart_grid'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; export const MultiMetricDetectorsSummary: FC = () => { const { jobCreator: jc, chartLoader, resultsLoader, chartInterval } = useContext( @@ -43,7 +43,7 @@ export const MultiMetricDetectorsSummary: FC = () => { const tempFieldValues = await chartLoader.loadFieldExampleValues(jobCreator.splitField); setFieldValues(tempFieldValues); } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); } } })(); @@ -75,7 +75,7 @@ export const MultiMetricDetectorsSummary: FC = () => { ); setLineChartsData(resp); } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartsData({}); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx index e5f5ba48900d..46f91550f6e3 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx @@ -17,7 +17,7 @@ import { getChartSettings, defaultChartSettings } from '../../../charts/common/s import { MetricSelector } from './metric_selector'; import { SplitFieldSelector } from '../split_field'; import { ChartGrid } from './chart_grid'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; interface Props { setIsValid: (na: boolean) => void; @@ -159,7 +159,7 @@ export const PopulationDetectors: FC = ({ setIsValid }) => { setLineChartsData(resp); } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartsData([]); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection_summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection_summary.tsx index 06f7092e8ac0..c32cc6ecc445 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection_summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection_summary.tsx @@ -15,7 +15,7 @@ import { LineChartData } from '../../../../../common/chart_loader'; import { Field, AggFieldPair } from '../../../../../../../../../common/types/fields'; import { getChartSettings, defaultChartSettings } from '../../../charts/common/settings'; import { ChartGrid } from './chart_grid'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; type DetectorFieldValues = Record; @@ -81,7 +81,7 @@ export const PopulationDetectorsSummary: FC = () => { setLineChartsData(resp); } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartsData({}); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx index f04b63f47789..5844e59225ab 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection.tsx @@ -13,7 +13,7 @@ import { newJobCapsService } from '../../../../../../../services/new_job_capabil import { AggFieldPair } from '../../../../../../../../../common/types/fields'; import { AnomalyChart, CHART_TYPE } from '../../../charts/anomaly_chart'; import { getChartSettings } from '../../../charts/common/settings'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; interface Props { setIsValid: (na: boolean) => void; @@ -93,7 +93,7 @@ export const SingleMetricDetectors: FC = ({ setIsValid }) => { setLineChartData(resp); } } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartData({}); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection_summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection_summary.tsx index 85fb5890307b..ae019ee1bbf8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection_summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/metric_selection_summary.tsx @@ -11,7 +11,7 @@ import { Results, ModelItem, Anomaly } from '../../../../../common/results_loade import { LineChartData } from '../../../../../common/chart_loader'; import { AnomalyChart, CHART_TYPE } from '../../../charts/anomaly_chart'; import { getChartSettings } from '../../../charts/common/settings'; -import { mlMessageBarService } from '../../../../../../../components/messagebar'; +import { getToastNotificationService } from '../../../../../../../services/toast_notification_service'; const DTR_IDX = 0; @@ -63,7 +63,7 @@ export const SingleMetricDetectorsSummary: FC = () => { setLineChartData(resp); } } catch (error) { - mlMessageBarService.notify.error(error); + getToastNotificationService().displayErrorToast(error); setLineChartData({}); } setLoadingData(false); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx index 2e7cc9c413a2..82a023cd1779 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { JobRunner } from '../../../../../common/job_runner'; import { useMlKibana } from '../../../../../../../contexts/kibana'; -import { getErrorMessage } from '../../../../../../../../../common/util/errors'; +import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; // @ts-ignore import { CreateWatchFlyout } from '../../../../../../jobs_list/components/create_watch_flyout/index'; @@ -70,7 +70,7 @@ export const PostSaveOptions: FC = ({ jobRunner }) => { defaultMessage: `Error starting job`, } ), - text: getErrorMessage(error), + text: extractErrorMessage(error), }); } } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx index 24d7fb9fc2a4..3000ce844913 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx @@ -22,13 +22,13 @@ import { JobCreatorContext } from '../job_creator_context'; import { JobRunner } from '../../../common/job_runner'; import { mlJobService } from '../../../../../services/job_service'; import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout'; -import { getErrorMessage } from '../../../../../../../common/util/errors'; import { isSingleMetricJobCreator, isAdvancedJobCreator } from '../../../common/job_creator'; import { JobDetails } from './components/job_details'; import { DatafeedDetails } from './components/datafeed_details'; import { DetectorChart } from './components/detector_chart'; import { JobProgress } from './components/job_progress'; import { PostSaveOptions } from './components/post_save_options'; +import { toastNotificationServiceProvider } from '../../../../../services/toast_notification_service'; import { convertToAdvancedJob, resetJob, @@ -72,15 +72,7 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => const jr = await jobCreator.createAndStartJob(); setJobRunner(jr); } catch (error) { - // catch and display all job creation errors - const { toasts } = notifications; - toasts.addDanger({ - title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', { - defaultMessage: `Job creation error`, - }), - text: getErrorMessage(error), - }); - setCreatingJob(false); + handleJobCreationError(error); } } @@ -91,18 +83,21 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => await jobCreator.createDatafeed(); advancedStartDatafeed(jobCreator, navigateToPath); } catch (error) { - // catch and display all job creation errors - const { toasts } = notifications; - toasts.addDanger({ - title: i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', { - defaultMessage: `Job creation error`, - }), - text: getErrorMessage(error), - }); - setCreatingJob(false); + handleJobCreationError(error); } } + function handleJobCreationError(error: any) { + const { displayErrorToast } = toastNotificationServiceProvider(notifications.toasts); + displayErrorToast( + error, + i18n.translate('xpack.ml.newJob.wizard.summaryStep.createJobError', { + defaultMessage: `Job creation error`, + }) + ); + setCreatingJob(false); + } + function viewResults() { const url = mlJobService.createResultsUrl( [jobCreator.jobId], diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/validation_step/validation.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/validation_step/validation.tsx index 19b89ffec02a..3bde32f40eeb 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/validation_step/validation.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/validation_step/validation.tsx @@ -8,7 +8,7 @@ import React, { Fragment, FC, useContext, useState, useEffect } from 'react'; import { WizardNav } from '../wizard_nav'; import { WIZARD_STEPS, StepProps } from '../step_types'; import { JobCreatorContext } from '../job_creator_context'; -import { mlJobService } from '../../../../../services/job_service'; +import { ml } from '../../../../../services/ml_api_service'; import { ValidateJob } from '../../../../../components/validate_job'; import { JOB_TYPE } from '../../../../../../../common/constants/new_job'; @@ -66,7 +66,7 @@ export const ValidationStep: FC = ({ setCurrentStep, isCurrentStep }) ) { if (management !== undefined) { - management.sections.section.insightsAndAlerting.registerApp({ + return management.sections.section.insightsAndAlerting.registerApp({ id: 'jobsListLink', title: i18n.translate('xpack.ml.management.jobsListTitle', { defaultMessage: 'Machine Learning Jobs', 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 640f63617b7d..dfa1b5f4e68c 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -14,15 +14,13 @@ import { i18n } from '@kbn/i18n'; import { ml } from './ml_api_service'; -import { mlMessageBarService } from '../components/messagebar'; -import { getToastNotifications } from '../util/dependency_cache'; +import { getToastNotificationService } from '../services/toast_notification_service'; import { isWebUrl } from '../util/url_utils'; import { ML_DATA_PREVIEW_COUNT } from '../../../common/util/job_utils'; import { TIME_FORMAT } from '../../../common/constants/time_format'; import { parseInterval } from '../../../common/util/parse_interval'; -import { toastNotificationServiceProvider } from '../services/toast_notification_service'; import { validateTimeRange } from '../util/date_utils'; -const msgs = mlMessageBarService; + let jobs = []; let datafeedIds = {}; @@ -119,7 +117,6 @@ class JobService { return new Promise((resolve, reject) => { jobs = []; datafeedIds = {}; - ml.getJobs() .then((resp) => { jobs = resp.jobs; @@ -162,7 +159,6 @@ class JobService { } processBasicJobInfo(this, jobs); this.jobs = jobs; - createJobStats(this.jobs, this.jobStats); resolve({ jobs: this.jobs }); }); }) @@ -176,12 +172,7 @@ class JobService { function error(err) { console.log('jobService error getting list of jobs:', err); - msgs.notify.error( - i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', { - defaultMessage: 'Jobs list could not be retrieved', - }) - ); - msgs.notify.error('', err); + getToastNotificationService().displayErrorToast(err); reject({ jobs, err }); } }); @@ -248,7 +239,6 @@ class JobService { } } this.jobs = jobs; - createJobStats(this.jobs, this.jobStats); resolve({ jobs: this.jobs }); }); }) @@ -263,12 +253,7 @@ class JobService { function error(err) { console.log('JobService error getting list of jobs:', err); - msgs.notify.error( - i18n.translate('xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage', { - defaultMessage: 'Jobs list could not be retrieved', - }) - ); - msgs.notify.error('', err); + getToastNotificationService().displayErrorToast(err); reject({ jobs, err }); } }); @@ -280,9 +265,6 @@ class JobService { ml.getDatafeeds(sId) .then((resp) => { - // console.log('loadDatafeeds query response:', resp); - - // make deep copy of datafeeds const datafeeds = resp.datafeeds; // load datafeeds stats @@ -309,12 +291,7 @@ class JobService { function error(err) { console.log('loadDatafeeds error getting list of datafeeds:', err); - msgs.notify.error( - i18n.translate('xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage', { - defaultMessage: 'datafeeds list could not be retrieved', - }) - ); - msgs.notify.error('', err); + getToastNotificationService().displayErrorToast(err); reject({ jobs, err }); } }); @@ -415,62 +392,6 @@ class JobService { return tempJob; } - updateJob(jobId, job) { - // return the promise chain - return ml - .updateJob({ jobId, job }) - .then(() => { - return { success: true }; - }) - .catch((err) => { - // TODO - all the functions in here should just return the error and not - // display the toast, as currently both the component and this service display - // errors, so we end up with duplicate toasts. - const toastNotifications = getToastNotifications(); - const toastNotificationService = toastNotificationServiceProvider(toastNotifications); - toastNotificationService.displayErrorToast( - err, - i18n.translate('xpack.ml.jobService.updateJobErrorTitle', { - defaultMessage: 'Could not update job: {jobId}', - values: { jobId }, - }) - ); - - console.error('update job', err); - return { success: false, message: err }; - }); - } - - validateJob(obj) { - // return the promise chain - return ml - .validateJob(obj) - .then((messages) => { - return { success: true, messages }; - }) - .catch((err) => { - const toastNotifications = getToastNotifications(); - const toastNotificationService = toastNotificationServiceProvider(toastNotifications); - toastNotificationService.displayErrorToast( - err, - i18n.translate('xpack.ml.jobService.validateJobErrorTitle', { - defaultMessage: 'Job Validation Error', - }) - ); - - console.log('validate job', err); - return { - success: false, - messages: [ - { - status: 'error', - text: err.message, - }, - ], - }; - }); - } - // find a job based on the id getJob(jobId) { const job = find(jobs, (j) => { @@ -638,25 +559,6 @@ class JobService { }); } - updateDatafeed(datafeedId, datafeedConfig) { - return ml - .updateDatafeed({ datafeedId, datafeedConfig }) - .then((resp) => { - console.log('update datafeed', resp); - return { success: true }; - }) - .catch((err) => { - msgs.notify.error( - i18n.translate('xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage', { - defaultMessage: 'Could not update datafeed: {datafeedId}', - values: { datafeedId }, - }) - ); - console.log('update datafeed', err); - return { success: false, message: err.message }; - }); - } - // start the datafeed for a given job // refresh the job state on start success startDatafeed(datafeedId, jobId, start, end) { @@ -677,49 +579,6 @@ class JobService { }) .catch((err) => { console.log('jobService error starting datafeed:', err); - msgs.notify.error( - i18n.translate('xpack.ml.jobService.couldNotStartDatafeedErrorMessage', { - defaultMessage: 'Could not start datafeed for {jobId}', - values: { jobId }, - }), - err - ); - reject(err); - }); - }); - } - - // stop the datafeed for a given job - // refresh the job state on stop success - stopDatafeed(datafeedId, jobId) { - return new Promise((resolve, reject) => { - ml.stopDatafeed({ - datafeedId, - }) - .then((resp) => { - resolve(resp); - }) - .catch((err) => { - console.log('jobService error stopping datafeed:', err); - const couldNotStopDatafeedErrorMessage = i18n.translate( - 'xpack.ml.jobService.couldNotStopDatafeedErrorMessage', - { - defaultMessage: 'Could not stop datafeed for {jobId}', - values: { jobId }, - } - ); - - if (err.statusCode === 500) { - msgs.notify.error(couldNotStopDatafeedErrorMessage); - msgs.notify.error( - i18n.translate('xpack.ml.jobService.requestMayHaveTimedOutErrorMessage', { - defaultMessage: - 'Request may have timed out and may still be running in the background.', - }) - ); - } else { - msgs.notify.error(couldNotStopDatafeedErrorMessage, err); - } reject(err); }); }); @@ -887,51 +746,6 @@ function processBasicJobInfo(localJobService, jobsList) { return processedJobsList; } -// Loop through the jobs list and create basic stats -// stats are displayed along the top of the Jobs Management page -function createJobStats(jobsList, jobStats) { - jobStats.activeNodes.value = 0; - jobStats.total.value = 0; - jobStats.open.value = 0; - jobStats.closed.value = 0; - jobStats.failed.value = 0; - jobStats.activeDatafeeds.value = 0; - - // object to keep track of nodes being used by jobs - const mlNodes = {}; - let failedJobs = 0; - - each(jobsList, (job) => { - if (job.state === 'opened') { - jobStats.open.value++; - } else if (job.state === 'closed') { - jobStats.closed.value++; - } else if (job.state === 'failed') { - failedJobs++; - } - - if (job.datafeed_config && job.datafeed_config.state === 'started') { - jobStats.activeDatafeeds.value++; - } - - if (job.node && job.node.name) { - mlNodes[job.node.name] = {}; - } - }); - - jobStats.total.value = jobsList.length; - - // // Only show failed jobs if it is non-zero - if (failedJobs) { - jobStats.failed.value = failedJobs; - jobStats.failed.show = true; - } else { - jobStats.failed.show = false; - } - - jobStats.activeNodes.value = Object.keys(mlNodes).length; -} - function createResultsUrlForJobs(jobsList, resultsPage, userTimeRange) { let from = undefined; let to = undefined; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index 9d7ce4f3df59..0deda455df77 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -62,7 +62,7 @@ export interface BucketSpanEstimatorResponse { name: string; ms: number; error?: boolean; - message?: { msg: string } | string; + message?: string; } export interface GetTimeFieldRangeResponse { diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service.ts deleted file mode 100644 index 94381ae3f1e5..000000000000 --- a/x-pack/plugins/ml/public/application/services/toast_notification_service.ts +++ /dev/null @@ -1,88 +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 { ToastInput, ToastOptions, ToastsStart } from 'kibana/public'; -import { ResponseError } from 'kibana/server'; -import { useMemo } from 'react'; -import { useNotifications } from '../contexts/kibana'; -import { - BoomResponse, - extractErrorProperties, - MLCustomHttpResponseOptions, - MLErrorObject, - MLResponseError, -} from '../../../common/util/errors'; - -export type ToastNotificationService = ReturnType; - -export function toastNotificationServiceProvider(toastNotifications: ToastsStart) { - return { - displayDangerToast(toastOrTitle: ToastInput, options?: ToastOptions) { - toastNotifications.addDanger(toastOrTitle, options); - }, - - displaySuccessToast(toastOrTitle: ToastInput, options?: ToastOptions) { - toastNotifications.addSuccess(toastOrTitle, options); - }, - - displayErrorToast(error: any, toastTitle: string) { - const errorObj = this.parseErrorMessage(error); - if (errorObj.fullErrorMessage !== undefined) { - // Provide access to the full error message via the 'See full error' button. - toastNotifications.addError(new Error(errorObj.fullErrorMessage), { - title: toastTitle, - toastMessage: errorObj.message, - }); - } else { - toastNotifications.addDanger( - { - title: toastTitle, - text: errorObj.message, - }, - { toastLifeTimeMs: 30000 } - ); - } - }, - - parseErrorMessage( - error: - | MLCustomHttpResponseOptions - | undefined - | string - | MLResponseError - ): MLErrorObject { - if ( - typeof error === 'object' && - 'response' in error && - typeof error.response === 'string' && - error.statusCode !== undefined - ) { - // MLResponseError which has been received back as part of a 'successful' response - // where the error was passed in a separate property in the response. - const wrapMlResponseError = { - body: error, - statusCode: error.statusCode, - }; - return extractErrorProperties(wrapMlResponseError); - } - - return extractErrorProperties( - error as - | MLCustomHttpResponseOptions - | undefined - | string - ); - }, - }; -} - -/** - * Hook to use {@link ToastNotificationService} in React components. - */ -export function useToastNotificationService(): ToastNotificationService { - const { toasts } = useNotifications(); - return useMemo(() => toastNotificationServiceProvider(toasts), []); -} diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service/index.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service/index.ts new file mode 100644 index 000000000000..1259f3b47d8e --- /dev/null +++ b/x-pack/plugins/ml/public/application/services/toast_notification_service/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { + ToastNotificationService, + toastNotificationServiceProvider, + useToastNotificationService, + getToastNotificationService, +} from './toast_notification_service'; diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts new file mode 100644 index 000000000000..61e0480313eb --- /dev/null +++ b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { ToastInput, ToastOptions, ToastsStart } from 'kibana/public'; +import { useMemo } from 'react'; +import { getToastNotifications } from '../../util/dependency_cache'; +import { useNotifications } from '../../contexts/kibana'; +import { + ErrorType, + extractErrorProperties, + MLRequestFailure, +} from '../../../../common/util/errors'; + +export type ToastNotificationService = ReturnType; + +export function toastNotificationServiceProvider(toastNotifications: ToastsStart) { + function displayDangerToast(toastOrTitle: ToastInput, options?: ToastOptions) { + toastNotifications.addDanger(toastOrTitle, options); + } + + function displayWarningToast(toastOrTitle: ToastInput, options?: ToastOptions) { + toastNotifications.addWarning(toastOrTitle, options); + } + + function displaySuccessToast(toastOrTitle: ToastInput, options?: ToastOptions) { + toastNotifications.addSuccess(toastOrTitle, options); + } + + function displayErrorToast(error: ErrorType, title?: string) { + const errorObj = extractErrorProperties(error); + toastNotifications.addError(new MLRequestFailure(errorObj, error), { + title: + title ?? + i18n.translate('xpack.ml.toastNotificationService.errorTitle', { + defaultMessage: 'An error has occurred', + }), + }); + } + + return { displayDangerToast, displayWarningToast, displaySuccessToast, displayErrorToast }; +} + +export function getToastNotificationService() { + const toastNotifications = getToastNotifications(); + return toastNotificationServiceProvider(toastNotifications); +} + +/** + * Hook to use {@link ToastNotificationService} in React components. + */ +export function useToastNotificationService(): ToastNotificationService { + const { toasts } = useNotifications(); + return useMemo(() => toastNotificationServiceProvider(toasts), []); +} diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js index 50777485903d..e0c7a4db6e89 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js @@ -7,7 +7,7 @@ import { getToastNotifications } from '../../../util/dependency_cache'; import { ml } from '../../../services/ml_api_service'; import { i18n } from '@kbn/i18n'; -import { getErrorMessage } from '../../../../../common/util/errors'; +import { extractErrorMessage } from '../../../../../common/util/errors'; export async function deleteCalendars(calendarsToDelete, callback) { if (calendarsToDelete === undefined || calendarsToDelete.length === 0) { @@ -47,7 +47,7 @@ export async function deleteCalendars(calendarsToDelete, callback) { }, } ), - text: getErrorMessage(error), + text: extractErrorMessage(error), }); } } diff --git a/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts b/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts index 2912aad6819c..6a5583ecbb8a 100644 --- a/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts +++ b/x-pack/plugins/ml/public/application/util/custom_url_utils.test.ts @@ -61,8 +61,13 @@ describe('ML - custom URL utils', () => { influencer_field_name: 'airline', influencer_field_values: ['<>:;[}")'], }, + { + influencer_field_name: 'odd:field,name', + influencer_field_values: [">:&12<'"], + }, ], airline: ['<>:;[}")'], + 'odd:field,name': [">:&12<'"], }; const TEST_RECORD_MULTIPLE_INFLUENCER_VALUES: CustomUrlAnomalyRecordDoc = { @@ -98,7 +103,7 @@ describe('ML - custom URL utils', () => { url_name: 'Raw data', time_range: 'auto', url_value: - "discover#/?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(index:bf6e5860-9404-11e8-8d4c-593f69c47267,query:(language:kuery,query:'airline:\"$airline$\"'))", + "discover#/?_g=(time:(from:'$earliest$',mode:absolute,to:'$latest$'))&_a=(index:bf6e5860-9404-11e8-8d4c-593f69c47267,query:(language:kuery,query:'airline:\"$airline$\" and odd:field,name : $odd:field,name$'))", }; const TEST_DASHBOARD_LUCENE_URL: KibanaUrlConfig = { @@ -263,9 +268,55 @@ describe('ML - custom URL utils', () => { ); }); - test('returns expected URL for a Kibana Discover type URL when record field contains special characters', () => { + test.skip('returns expected URL for a Kibana Discover type URL when record field contains special characters', () => { expect(getUrlForRecord(TEST_DISCOVER_URL, TEST_RECORD_SPECIAL_CHARS)).toBe( - "discover#/?_g=(time:(from:'2017-02-09T15:10:00.000Z',mode:absolute,to:'2017-02-09T17:15:00.000Z'))&_a=(index:bf6e5860-9404-11e8-8d4c-593f69c47267,query:(language:kuery,query:'airline:\"%3C%3E%3A%3B%5B%7D%5C%22)\"'))" + "discover#/?_g=(time:(from:'2017-02-09T15:10:00.000Z',mode:absolute,to:'2017-02-09T17:15:00.000Z'))&_a=(index:bf6e5860-9404-11e8-8d4c-593f69c47267,query:(language:kuery,query:'airline:\"%3C%3E%3A%3B%5B%7D%5C%22)\" and odd:field,name:>:&12<''))" + ); + }); + + test('correctly encodes special characters inside of a query string', () => { + const testUrl = { + url_name: 'Show dashboard', + time_range: 'auto', + url_value: `dashboards#/view/351de820-f2bb-11ea-ab06-cb93221707e9?_a=(filters:!(),query:(language:kuery,query:'at@name:"$at@name$" and singlequote!'name:"$singlequote!'name$"'))&_g=(filters:!(),time:(from:'$earliest$',mode:absolute,to:'$latest$'))`, + }; + + const testRecord = { + job_id: 'spec-char', + result_type: 'record', + probability: 0.0028099428534745633, + multi_bucket_impact: 5, + record_score: 49.00785814424704, + initial_record_score: 49.00785814424704, + bucket_span: 900, + detector_index: 0, + is_interim: false, + timestamp: 1549593000000, + partition_field_name: 'at@name', + partition_field_value: "contains a ' quote", + function: 'mean', + function_description: 'mean', + typical: [1993.2657340111837], + actual: [1808.3334418402778], + field_name: 'metric%$£&!{(]field', + influencers: [ + { + influencer_field_name: "singlequote'name", + influencer_field_values: ["contains a ' quote"], + }, + { + influencer_field_name: 'at@name', + influencer_field_values: ["contains a ' quote"], + }, + ], + "singlequote'name": ["contains a ' quote"], + 'at@name': ["contains a ' quote"], + earliest: '2019-02-08T00:00:00.000Z', + latest: '2019-02-08T23:59:59.999Z', + }; + + expect(getUrlForRecord(testUrl, testRecord)).toBe( + `dashboards#/view/351de820-f2bb-11ea-ab06-cb93221707e9?_a=(filters:!(),query:(language:kuery,query:'at@name:"contains%20a%20!'%20quote" AND singlequote!'name:"contains%20a%20!'%20quote"'))&_g=(filters:!(),time:(from:'2019-02-08T00:00:00.000Z',mode:absolute,to:'2019-02-08T23:59:59.999Z'))` ); }); @@ -405,6 +456,58 @@ describe('ML - custom URL utils', () => { ); }); + test('return expected url for Security app', () => { + const urlConfig = { + url_name: 'Hosts Details by process name', + url_value: + "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))", + }; + + const testRecords = { + job_id: 'rare_process_by_host_linux_ecs', + result_type: 'record', + probability: 0.018122957282324745, + multi_bucket_impact: 0, + record_score: 20.513469583273547, + initial_record_score: 20.513469583273547, + bucket_span: 900, + detector_index: 0, + is_interim: false, + timestamp: 1549043100000, + by_field_name: 'process.name', + by_field_value: 'seq', + partition_field_name: 'host.name', + partition_field_value: 'showcase', + function: 'rare', + function_description: 'rare', + typical: [0.018122957282324745], + actual: [1], + influencers: [ + { + influencer_field_name: 'user.name', + influencer_field_values: ['sophie'], + }, + { + influencer_field_name: 'process.name', + influencer_field_values: ['seq'], + }, + { + influencer_field_name: 'host.name', + influencer_field_values: ['showcase'], + }, + ], + 'process.name': ['seq'], + 'user.name': ['sophie'], + 'host.name': ['showcase'], + earliest: '2019-02-01T16:00:00.000Z', + latest: '2019-02-01T18:59:59.999Z', + }; + + expect(getUrlForRecord(urlConfig, testRecords)).toBe( + "security/hosts/ml-hosts/showcase?_g=()&query=(language:kuery,query:'process.name:\"seq\"')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-02-01T16:00:00.000Z',kind:absolute,to:'2019-02-01T18:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-02-01T16%3A00%3A00.000Z',kind:absolute,to:'2019-02-01T18%3A59%3A59.999Z')))" + ); + }); + test('removes an empty path component with a trailing slash', () => { const urlConfig = { url_name: 'APM', diff --git a/x-pack/plugins/ml/public/application/util/custom_url_utils.ts b/x-pack/plugins/ml/public/application/util/custom_url_utils.ts index 8263def2034a..18ba1e4ee337 100644 --- a/x-pack/plugins/ml/public/application/util/custom_url_utils.ts +++ b/x-pack/plugins/ml/public/application/util/custom_url_utils.ts @@ -8,6 +8,7 @@ import { get, flow } from 'lodash'; import moment from 'moment'; +import rison, { RisonObject, RisonValue } from 'rison-node'; import { parseInterval } from '../../../common/util/parse_interval'; import { escapeForElasticsearchQuery, replaceStringTokens } from './string_utils'; @@ -131,13 +132,70 @@ function escapeForKQL(value: string | number): string { type GetResultTokenValue = (v: string) => string; +export const isRisonObject = (value: RisonValue): value is RisonObject => { + return value !== null && typeof value === 'object'; +}; + +const getQueryStringResultProvider = ( + record: CustomUrlAnomalyRecordDoc, + getResultTokenValue: GetResultTokenValue +) => (resultPrefix: string, queryString: string, resultPostfix: string): string => { + const URL_LENGTH_LIMIT = 2000; + + let availableCharactersLeft = URL_LENGTH_LIMIT - resultPrefix.length - resultPostfix.length; + + // URL template might contain encoded characters + const queryFields = queryString + // Split query string by AND operator. + .split(/\sand\s/i) + // Get property name from `influencerField:$influencerField$` string. + .map((v) => String(v.split(/:(.+)?\$/)[0]).trim()); + + const queryParts: string[] = []; + const joinOperator = ' AND '; + + fieldsLoop: for (let i = 0; i < queryFields.length; i++) { + const field = queryFields[i]; + // Use lodash get to allow nested JSON fields to be retrieved. + let tokenValues: string[] | string | null = get(record, field) || null; + if (tokenValues === null) { + continue; + } + tokenValues = Array.isArray(tokenValues) ? tokenValues : [tokenValues]; + + // Create a pair `influencerField:value`. + // In cases where there are multiple influencer field values for an anomaly + // combine values with OR operator e.g. `(influencerField:value or influencerField:another_value)`. + let result = ''; + for (let j = 0; j < tokenValues.length; j++) { + const part = `${j > 0 ? ' OR ' : ''}${field}:"${getResultTokenValue(tokenValues[j])}"`; + + // Build up a URL string which is not longer than the allowed length and isn't corrupted by invalid query. + if (availableCharactersLeft < part.length) { + if (result.length > 0) { + queryParts.push(j > 0 ? `(${result})` : result); + } + break fieldsLoop; + } + + result += part; + + availableCharactersLeft -= result.length; + } + + if (result.length > 0) { + queryParts.push(tokenValues.length > 1 ? `(${result})` : result); + } + } + return queryParts.join(joinOperator); +}; + /** * Builds a Kibana dashboard or Discover URL from the supplied config, with any * dollar delimited tokens substituted from the supplied anomaly record. */ function buildKibanaUrl(urlConfig: UrlConfig, record: CustomUrlAnomalyRecordDoc) { const urlValue = urlConfig.url_value; - const URL_LENGTH_LIMIT = 2000; const isLuceneQueryLanguage = urlValue.includes('language:lucene'); @@ -145,11 +203,7 @@ function buildKibanaUrl(urlConfig: UrlConfig, record: CustomUrlAnomalyRecordDoc) ? escapeForElasticsearchQuery : escapeForKQL; - const commonEscapeCallback = flow( - // Kibana URLs used rison encoding, so escape with ! any ! or ' characters - (v: string): string => v.replace(/[!']/g, '!$&'), - encodeURIComponent - ); + const commonEscapeCallback = flow(encodeURIComponent); const replaceSingleTokenValues = (str: string) => { const getResultTokenValue: GetResultTokenValue = flow( @@ -172,65 +226,34 @@ function buildKibanaUrl(urlConfig: UrlConfig, record: CustomUrlAnomalyRecordDoc) return flow( (str: string) => str.replace('$earliest$', record.earliest).replace('$latest$', record.latest), // Process query string content of the URL + decodeURIComponent, (str: string) => { const getResultTokenValue: GetResultTokenValue = flow( queryLanguageEscapeCallback, commonEscapeCallback ); + + const getQueryStringResult = getQueryStringResultProvider(record, getResultTokenValue); + + const match = str.match(/(.+)(\(.*\blanguage:(?:lucene|kuery)\b.*?\))(.+)/); + + if (match !== null && match[2] !== undefined) { + const [, prefix, queryDef, postfix] = match; + + const q = rison.decode(queryDef); + + if (isRisonObject(q) && q.hasOwnProperty('query')) { + const [resultPrefix, resultPostfix] = [prefix, postfix].map(replaceSingleTokenValues); + const resultQuery = getQueryStringResult(resultPrefix, q.query as string, resultPostfix); + return `${resultPrefix}${rison.encode({ ...q, query: resultQuery })}${resultPostfix}`; + } + } + return str.replace( - /(.+query:'|.+&kuery=)([^']*)(['&].+)/, + /(.+&kuery=)(.*?)[^!](&.+)/, (fullMatch, prefix: string, queryString: string, postfix: string) => { const [resultPrefix, resultPostfix] = [prefix, postfix].map(replaceSingleTokenValues); - - let availableCharactersLeft = - URL_LENGTH_LIMIT - resultPrefix.length - resultPostfix.length; - const queryFields = queryString - // Split query string by AND operator. - .split(/\sand\s/i) - // Get property name from `influencerField:$influencerField$` string. - .map((v) => v.split(':')[0]); - - const queryParts: string[] = []; - const joinOperator = ' AND '; - - fieldsLoop: for (let i = 0; i < queryFields.length; i++) { - const field = queryFields[i]; - // Use lodash get to allow nested JSON fields to be retrieved. - let tokenValues: string[] | string | null = get(record, field) || null; - if (tokenValues === null) { - continue; - } - tokenValues = Array.isArray(tokenValues) ? tokenValues : [tokenValues]; - - // Create a pair `influencerField:value`. - // In cases where there are multiple influencer field values for an anomaly - // combine values with OR operator e.g. `(influencerField:value or influencerField:another_value)`. - let result = ''; - for (let j = 0; j < tokenValues.length; j++) { - const part = `${j > 0 ? ' OR ' : ''}${field}:"${getResultTokenValue( - tokenValues[j] - )}"`; - - // Build up a URL string which is not longer than the allowed length and isn't corrupted by invalid query. - if (availableCharactersLeft < part.length) { - if (result.length > 0) { - queryParts.push(j > 0 ? `(${result})` : result); - } - break fieldsLoop; - } - - result += part; - - availableCharactersLeft -= result.length; - } - - if (result.length > 0) { - queryParts.push(tokenValues.length > 1 ? `(${result})` : result); - } - } - - const resultQuery = queryParts.join(joinOperator); - + const resultQuery = getQueryStringResult(resultPrefix, queryString, resultPostfix); return `${resultPrefix}${resultQuery}${resultPostfix}`; } ); diff --git a/x-pack/plugins/ml/public/application/util/ml_error.ts b/x-pack/plugins/ml/public/application/util/ml_error.ts deleted file mode 100644 index 2a0280404c18..000000000000 --- a/x-pack/plugins/ml/public/application/util/ml_error.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 { KbnError } from '../../../../../../src/plugins/kibana_utils/public'; - -export class MLRequestFailure extends KbnError { - origError: any; - resp: any; - // takes an Error object and and optional response object - // if error is falsy (null) the response object will be used - // notify will show the full expandable stack trace of the response if a response object is used and no error is passed in. - constructor(error: any, resp: any) { - error = error || {}; - super(error.message || JSON.stringify(resp)); - - this.origError = error; - this.resp = typeof resp === 'string' ? JSON.parse(resp) : resp; - } -} diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 3e8ab99e341a..fc0d21e9353c 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -101,6 +101,8 @@ export class MlPlugin implements Plugin { }, }); + const managementApp = registerManagementSection(pluginsSetup.management, core); + const licensing = pluginsSetup.licensing.license$.pipe(take(1)); licensing.subscribe(async (license) => { const [coreStart] = await core.getStartServices(); @@ -110,26 +112,35 @@ export class MlPlugin implements Plugin { registerFeature(pluginsSetup.home); } + const { capabilities } = coreStart.application; + // register ML for the index pattern management no data screen. pluginsSetup.indexPatternManagement.environment.update({ ml: () => - coreStart.application.capabilities.ml.canFindFileStructure - ? MlCardState.ENABLED - : MlCardState.HIDDEN, + capabilities.ml.canFindFileStructure ? MlCardState.ENABLED : MlCardState.HIDDEN, }); + const canManageMLJobs = capabilities.management?.insightsAndAlerting?.jobsListLink ?? false; + // register various ML plugin features which require a full license if (isFullLicense(license)) { - registerManagementSection(pluginsSetup.management, core); + if (canManageMLJobs && managementApp) { + managementApp.enable(); + } registerEmbeddables(pluginsSetup.embeddable, core); registerMlUiActions(pluginsSetup.uiActions, core); registerUrlGenerator(pluginsSetup.share, core); + } else if (managementApp) { + managementApp.disable(); } } else { // if ml is disabled in elasticsearch, disable ML in kibana this.appUpdater.next(() => ({ status: AppStatus.inaccessible, })); + if (managementApp) { + managementApp.disable(); + } } }); diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/manifest.json index 1e7fcdd4320f..36d1df6db4c9 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/manifest.json @@ -40,6 +40,46 @@ { "id": "linux_anomalous_user_name_ecs", "file": "linux_anomalous_user_name_ecs.json" + }, + { + "id": "linux_rare_metadata_process", + "file": "linux_rare_metadata_process.json" + }, + { + "id": "linux_rare_metadata_user", + "file": "linux_rare_metadata_user.json" + }, + { + "id": "linux_rare_user_compiler", + "file": "linux_rare_user_compiler.json" + }, + { + "id": "linux_rare_kernel_module_arguments", + "file": "linux_rare_kernel_module_arguments.json" + }, + { + "id": "linux_rare_sudo_user", + "file": "linux_rare_sudo_user.json" + }, + { + "id": "linux_system_user_discovery", + "file": "linux_system_user_discovery.json" + }, + { + "id": "linux_system_information_discovery", + "file": "linux_system_information_discovery.json" + }, + { + "id": "linux_system_process_discovery", + "file": "linux_system_process_discovery.json" + }, + { + "id": "linux_network_connection_discovery", + "file": "linux_network_connection_discovery.json" + }, + { + "id": "linux_network_configuration_discovery", + "file": "linux_network_configuration_discovery.json" } ], "datafeeds": [ @@ -77,6 +117,56 @@ "id": "datafeed-linux_anomalous_user_name_ecs", "file": "datafeed_linux_anomalous_user_name_ecs.json", "job_id": "linux_anomalous_user_name_ecs" + }, + { + "id": "datafeed-linux_rare_metadata_process", + "file": "datafeed_linux_rare_metadata_process.json", + "job_id": "linux_rare_metadata_process" + }, + { + "id": "datafeed-linux_rare_metadata_user", + "file": "datafeed_linux_rare_metadata_user.json", + "job_id": "linux_rare_metadata_user" + }, + { + "id": "datafeed-linux_rare_user_compiler", + "file": "datafeed_linux_rare_user_compiler.json", + "job_id": "linux_rare_user_compiler" + }, + { + "id": "datafeed-linux_rare_kernel_module_arguments", + "file": "datafeed_linux_rare_kernel_module_arguments.json", + "job_id": "linux_rare_kernel_module_arguments" + }, + { + "id": "datafeed-linux_rare_sudo_user", + "file": "datafeed_linux_rare_sudo_user.json", + "job_id": "linux_rare_sudo_user" + }, + { + "id": "datafeed-linux_system_information_discovery", + "file": "datafeed_linux_system_information_discovery.json", + "job_id": "linux_system_information_discovery" + }, + { + "id": "datafeed-linux_system_process_discovery", + "file": "datafeed_linux_system_process_discovery.json", + "job_id": "linux_system_process_discovery" + }, + { + "id": "datafeed-linux_system_user_discovery", + "file": "datafeed_linux_system_user_discovery.json", + "job_id": "linux_system_user_discovery" + }, + { + "id": "datafeed-linux_network_configuration_discovery", + "file": "datafeed_linux_network_configuration_discovery.json", + "job_id": "linux_network_configuration_discovery" + }, + { + "id": "datafeed-linux_network_connection_discovery", + "file": "datafeed_linux_network_connection_discovery.json", + "job_id": "linux_network_connection_discovery" } ] } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_configuration_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_configuration_discovery.json new file mode 100644 index 000000000000..d4a130770c92 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_configuration_discovery.json @@ -0,0 +1,26 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "must": [ + { + "bool": { + "should": [ + {"term": {"process.name": "arp"}}, + {"term": {"process.name": "echo"}}, + {"term": {"process.name": "ethtool"}}, + {"term": {"process.name": "ifconfig"}}, + {"term": {"process.name": "ip"}}, + {"term": {"process.name": "iptables"}}, + {"term": {"process.name": "ufw"}} + ] + } + } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_connection_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_connection_discovery.json new file mode 100644 index 000000000000..0ae80df4bd47 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_network_connection_discovery.json @@ -0,0 +1,23 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "must": [ + { + "bool": { + "should": [ + {"term": {"process.name": "netstat"}}, + {"term": {"process.name": "ss"}}, + {"term": {"process.name": "route"}}, + {"term": {"process.name": "showmount"}} + ] + } + } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_kernel_module_arguments.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_kernel_module_arguments.json new file mode 100644 index 000000000000..99bb690c8d73 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_kernel_module_arguments.json @@ -0,0 +1,22 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"exists": {"field": "process.title"}}], + "must": [ + {"bool": { + "should": [ + {"term": {"process.name": "insmod"}}, + {"term": {"process.name": "kmod"}}, + {"term": {"process.name": "modprobe"}}, + {"term": {"process.name": "rmod"}} + ] + }} + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_process.json new file mode 100644 index 000000000000..dc0f6c4e81b3 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_process.json @@ -0,0 +1,12 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"term": {"destination.ip": "169.254.169.254"}}] + } + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_user.json new file mode 100644 index 000000000000..dc0f6c4e81b3 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_metadata_user.json @@ -0,0 +1,12 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"term": {"destination.ip": "169.254.169.254"}}] + } + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_sudo_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_sudo_user.json new file mode 100644 index 000000000000..544675f3d48d --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_sudo_user.json @@ -0,0 +1,15 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [ + {"term": {"event.action": "executed"}}, + {"term": {"process.name": "sudo"}} + ] + } + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_user_compiler.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_user_compiler.json new file mode 100644 index 000000000000..027b12401000 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_rare_user_compiler.json @@ -0,0 +1,22 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"term": {"event.action": "executed"}}], + "must": [ + {"bool": { + "should": [ + {"term": {"process.name": "compile"}}, + {"term": {"process.name": "gcc"}}, + {"term": {"process.name": "make"}}, + {"term": {"process.name": "yasm"}} + ] + }} + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_information_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_information_discovery.json new file mode 100644 index 000000000000..6e7ce26763f7 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_information_discovery.json @@ -0,0 +1,31 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "must": [ + { + "bool": { + "should": [ + {"term": {"process.name": "cat"}}, + {"term": {"process.name": "grep"}}, + {"term": {"process.name": "head"}}, + {"term": {"process.name": "hostname"}}, + {"term": {"process.name": "less"}}, + {"term": {"process.name": "ls"}}, + {"term": {"process.name": "lsmod"}}, + {"term": {"process.name": "more"}}, + {"term": {"process.name": "strings"}}, + {"term": {"process.name": "tail"}}, + {"term": {"process.name": "uptime"}}, + {"term": {"process.name": "uname"}} + ] + } + } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_process_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_process_discovery.json new file mode 100644 index 000000000000..dbd8f54ff971 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_process_discovery.json @@ -0,0 +1,21 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "must": [ + { + "bool": { + "should": [ + {"term": {"process.name": "ps"}}, + {"term": {"process.name": "top"}} + ] + } + } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_user_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_user_discovery.json new file mode 100644 index 000000000000..24230094a47d --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/datafeed_linux_system_user_discovery.json @@ -0,0 +1,23 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "must": [ + { + "bool": { + "should": [ + {"term": {"process.name": "users"}}, + {"term": {"process.name": "w"}}, + {"term": {"process.name": "who"}}, + {"term": {"process.name": "whoami"}} + ] + } + } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_configuration_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_configuration_discovery.json new file mode 100644 index 000000000000..6d687764085e --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_configuration_discovery.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for commands related to system network configuration discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used by a threat actor to engage in system network configuration discovery in order to increase their understanding of connected networks and hosts. This information may be used to shape follow-up behaviors such as lateral movement or additional discovery.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "64mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_connection_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_connection_discovery.json new file mode 100644 index 000000000000..b41439548dd5 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_network_connection_discovery.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for commands related to system network connection discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used by a threat actor to engage in system network connection discovery in order to increase their understanding of connected services and systems. This information may be used to shape follow-up behaviors such as lateral movement or additional discovery.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "64mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_kernel_module_arguments.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_kernel_module_arguments.json new file mode 100644 index 000000000000..1b79e8305425 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_kernel_module_arguments.json @@ -0,0 +1,45 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for unusual kernel modules which are often used for stealth.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"process.title\"", + "function": "rare", + "by_field_name": "process.title" + } + ], + "influencers": [ + "process.title", + "process.working_directory", + "host.name", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_process.json new file mode 100644 index 000000000000..7295f11e600d --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_process.json @@ -0,0 +1,52 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for anomalous access to the metadata service by an unusual process. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"process.name\"", + "function": "rare", + "by_field_name": "process.name" + } + ], + "influencers": [ + "host.name", + "user.name", + "process.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_user.json new file mode 100644 index 000000000000..049d10920de0 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_metadata_user.json @@ -0,0 +1,43 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for anomalous access to the metadata service by an unusual user. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "host.name", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_sudo_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_sudo_user.json new file mode 100644 index 000000000000..654f5c76e569 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_sudo_user.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for sudo activity from an unusual user context.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_user_compiler.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_user_compiler.json new file mode 100644 index 000000000000..245b7e0819c7 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_rare_user_compiler.json @@ -0,0 +1,45 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for compiler activity by a user context which does not normally run compilers. This can be ad-hoc software changes or unauthorized software deployment. This can also be due to local privliege elevation via locally run exploits or malware activity.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.title", + "host.name", + "process.working_directory", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "256mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_information_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_information_discovery.json new file mode 100644 index 000000000000..3a51223b4899 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_information_discovery.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for commands related to system information discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system information discovery in order to gather detailed information about system configuration and software versions. This may be a precursor to selection of a persistence mechanism or a method of privilege elevation.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "16mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_process_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_process_discovery.json new file mode 100644 index 000000000000..592bb5a717fc --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_process_discovery.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for commands related to system process discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system process discovery in order to increase their understanding of software applications running on a target host or network. This may be a precursor to selection of a persistence mechanism or a method of privilege elevation.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "16mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_user_discovery.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_user_discovery.json new file mode 100644 index 000000000000..33f42c274b33 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_auditbeat/ml/linux_system_user_discovery.json @@ -0,0 +1,53 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Auditbeat - Looks for commands related to system user or owner discovery from an unusual user context. This can be due to uncommon troubleshooting activity or due to a compromised account. A compromised account may be used to engage in system owner or user discovery in order to identify currently active or primary users of a system. This may be a precursor to additional discovery, credential dumping or privilege elevation activity.", + "groups": [ + "security", + "auditbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "process.args", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "16mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-auditbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/manifest.json index ffbf5aa7d8bb..969873ead6d9 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/manifest.json @@ -48,6 +48,14 @@ { "id": "windows_rare_user_runas_event", "file": "windows_rare_user_runas_event.json" + }, + { + "id": "windows_rare_metadata_process", + "file": "windows_rare_metadata_process.json" + }, + { + "id": "windows_rare_metadata_user", + "file": "windows_rare_metadata_user.json" } ], "datafeeds": [ @@ -95,6 +103,16 @@ "id": "datafeed-windows_rare_user_runas_event", "file": "datafeed_windows_rare_user_runas_event.json", "job_id": "windows_rare_user_runas_event" + }, + { + "id": "datafeed-windows_rare_metadata_process", + "file": "datafeed_windows_rare_metadata_process.json", + "job_id": "windows_rare_metadata_process" + }, + { + "id": "datafeed-windows_rare_metadata_user", + "file": "datafeed_windows_rare_metadata_user.json", + "job_id": "windows_rare_metadata_user" } ] -} +} \ No newline at end of file diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_process.json new file mode 100644 index 000000000000..dc0f6c4e81b3 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_process.json @@ -0,0 +1,12 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"term": {"destination.ip": "169.254.169.254"}}] + } + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_user.json new file mode 100644 index 000000000000..dc0f6c4e81b3 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/datafeed_windows_rare_metadata_user.json @@ -0,0 +1,12 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "max_empty_searches": 10, + "query": { + "bool": { + "filter": [{"term": {"destination.ip": "169.254.169.254"}}] + } + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_process.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_process.json new file mode 100644 index 000000000000..85fddbcc53e0 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_process.json @@ -0,0 +1,52 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Winlogbeat - Looks for anomalous access to the metadata service by an unusual process. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", + "groups": [ + "security", + "winlogbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"process.name\"", + "function": "rare", + "by_field_name": "process.name" + } + ], + "influencers": [ + "process.name", + "host.name", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "64mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-winlogbeat", + "custom_urls": [ + { + "url_name": "Host Details by process name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by process name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'process.name%20:%20%22$process.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_user.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_user.json new file mode 100644 index 000000000000..767c2d5b30ad --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/siem_winlogbeat/ml/windows_rare_metadata_user.json @@ -0,0 +1,43 @@ +{ + "job_type": "anomaly_detector", + "description": "Security: Winlogbeat - Looks for anomalous access to the metadata service by an unusual user. The metadata service may be targeted in order to harvest credentials or user data scripts containing secrets.", + "groups": [ + "security", + "winlogbeat", + "process" + ], + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "rare by \"user.name\"", + "function": "rare", + "by_field_name": "user.name" + } + ], + "influencers": [ + "host.name", + "user.name" + ] + }, + "allow_lazy_open": true, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "custom_settings": { + "created_by": "ml-module-siem-winlogbeat", + "custom_urls": [ + { + "url_name": "Host Details by user name", + "url_value": "security/hosts/ml-hosts/$host.name$?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + }, + { + "url_name": "Hosts Overview by user name", + "url_value": "security/hosts/ml-hosts?_g=()&query=(query:'user.name%20:%20%22$user.name$%22',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')),timeline:(linkTo:!(global),timerange:(from:'$earliest$',kind:absolute,to:'$latest$')))" + } + ] + } + } diff --git a/x-pack/plugins/ml/server/models/job_service/datafeeds.ts b/x-pack/plugins/ml/server/models/job_service/datafeeds.ts index c0eb1b72825d..62ef9b362161 100644 --- a/x-pack/plugins/ml/server/models/job_service/datafeeds.ts +++ b/x-pack/plugins/ml/server/models/job_service/datafeeds.ts @@ -118,6 +118,11 @@ export function datafeedsProvider({ asInternalUser }: IScopedClusterClient) { } catch (error) { if (isRequestTimeout(error)) { return fillResultsWithTimeouts(results, datafeedId, datafeedIds, DATAFEED_STATE.STOPPED); + } else { + results[datafeedId] = { + started: false, + error: error.body, + }; } } } diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index e047d31ba6eb..f4378e29ef82 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -407,28 +407,21 @@ export function jobsProvider(client: IScopedClusterClient) { // Job IDs in supplied array may contain wildcard '*' characters // e.g. *_low_request_rate_ecs async function jobsExist(jobIds: string[] = []) { - // Get the list of job IDs. - const { body } = await asInternalUser.ml.getJobs({ - job_id: jobIds.join(), - }); - const results: { [id: string]: boolean } = {}; - if (body.count > 0) { - const allJobIds = body.jobs.map((job) => job.job_id); - - // Check if each of the supplied IDs match existing jobs. - jobIds.forEach((jobId) => { - // Create a Regex for each supplied ID as wildcard * is allowed. - const regexp = new RegExp(`^${jobId.replace(/\*+/g, '.*')}$`); - const exists = allJobIds.some((existsJobId) => regexp.test(existsJobId)); - results[jobId] = exists; - }); - } else { - jobIds.forEach((jobId) => { + for (const jobId of jobIds) { + try { + const { body } = await asInternalUser.ml.getJobs({ + job_id: jobId, + }); + results[jobId] = body.count > 0; + } catch (e) { + // if a non-wildcarded job id is supplied, the get jobs endpoint will 404 + if (e.body?.status !== 404) { + throw e; + } results[jobId] = false; - }); + } } - return results; } diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 39672f5b188b..cf248fcc6089 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -67,7 +67,7 @@ export class MlServerPlugin implements Plugin 0) { - destinationIndex = body.data_frame_analytics[0].dest.index; - } - } catch (e) { - return response.customError(wrapError(e)); + try { + // Check if analyticsId is valid and get destination index + const { body } = await client.asInternalUser.ml.getDataFrameAnalytics({ + id: analyticsId, + }); + if (Array.isArray(body.data_frame_analytics) && body.data_frame_analytics.length > 0) { + destinationIndex = body.data_frame_analytics[0].dest.index; } + } catch (e) { + // exist early if the job doesn't exist + return response.customError(wrapError(e)); + } + if (deleteDestIndex || deleteDestIndexPattern) { // If user checks box to delete the destinationIndex associated with the job if (destinationIndex && deleteDestIndex) { // Verify if user has privilege to delete the destination index @@ -349,8 +350,8 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat index: destinationIndex, }); destIndexDeleted.success = true; - } catch (deleteIndexError) { - destIndexDeleted.error = wrapError(deleteIndexError); + } catch ({ body }) { + destIndexDeleted.error = body; } } else { return response.forbidden(); @@ -366,7 +367,7 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat } destIndexPatternDeleted.success = true; } catch (deleteDestIndexPatternError) { - destIndexPatternDeleted.error = wrapError(deleteDestIndexPatternError); + destIndexPatternDeleted.error = deleteDestIndexPatternError; } } } @@ -378,11 +379,8 @@ export function dataFrameAnalyticsRoutes({ router, mlLicense }: RouteInitializat id: analyticsId, }); analyticsJobDeleted.success = true; - } catch (deleteDFAError) { - analyticsJobDeleted.error = wrapError(deleteDFAError); - if (analyticsJobDeleted.error.statusCode === 404) { - return response.notFound(); - } + } catch ({ body }) { + analyticsJobDeleted.error = body; } const results = { analyticsJobDeleted, diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index 56cba83813a6..c9f82eb52143 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -23,6 +23,6 @@ export function createCpuUsageAlertType(): AlertTypeModel { ), validate, defaultActionMessage: '{{context.internalFullMessage}}', - requiresAppContext: false, + requiresAppContext: true, }; } diff --git a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx index 58b37e43085f..f6223d41ab30 100644 --- a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx @@ -33,7 +33,7 @@ export function createLegacyAlertTypes(): AlertTypeModel[] { ), defaultActionMessage: '{{context.internalFullMessage}}', validate: () => ({ errors: {} }), - requiresAppContext: false, + requiresAppContext: true, }; }); } diff --git a/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js index deaa4fd152cc..c4faf51dc000 100644 --- a/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js +++ b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js @@ -11,8 +11,8 @@ import { getColor } from './get_color'; import { TimeseriesVisualization } from './timeseries_visualization'; function formatTicksFor(series) { - const format = get(series, '.metric.format', '0,0.0'); - const units = get(series, '.metric.units', ''); + const format = get(series, 'metric.format', '0,0.0'); + const units = get(series, 'metric.units', ''); return function formatTicks(val) { let formatted = numeral(val).format(format); diff --git a/x-pack/plugins/monitoring/public/views/access_denied/index.js b/x-pack/plugins/monitoring/public/views/access_denied/index.js index 2db34842b932..9f1303f5be52 100644 --- a/x-pack/plugins/monitoring/public/views/access_denied/index.js +++ b/x-pack/plugins/monitoring/public/views/access_denied/index.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { kbnBaseUrl } from '../../../../../../src/plugins/kibana_legacy/common/kbn_base_url'; import { uiRoutes } from '../../angular/helpers/routes'; import template from './index.html'; @@ -35,7 +34,7 @@ uiRoutes.when('/access-denied', { const $interval = $injector.get('$interval'); // The template's "Back to Kibana" button click handler - this.goToKibanaURL = kbnBaseUrl; + this.goToKibanaURL = '/app/home'; // keep trying to load data in the background const accessPoller = $interval(() => tryPrivilege($http), 5 * 1000); // every 5 seconds diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js index 16d42d896ca1..e91679eff281 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js @@ -119,67 +119,75 @@ export async function getClustersFromRequest( // add alerts data if (isInCodePath(codePaths, [CODE_PATH_ALERTS])) { const alertsClient = req.getAlertsClient(); - if (alertsClient) { - for (const cluster of clusters) { - const verification = verifyMonitoringLicense(req.server); - if (!verification.enabled) { - // return metadata detailing that alerts is disabled because of the monitoring cluster license - cluster.alerts = { - alertsMeta: { - enabled: verification.enabled, - message: verification.message, // NOTE: this is only defined when the alert feature is disabled - }, - list: {}, - }; - continue; - } - - // check the license type of the production cluster for alerts feature support - const license = cluster.license || {}; - const prodLicenseInfo = checkLicenseForAlerts( - license.type, - license.status === 'active', - 'production' - ); - if (prodLicenseInfo.clusterAlerts.enabled) { - cluster.alerts = { - list: await fetchStatus( - alertsClient, - req.server.plugins.monitoring.info, - undefined, - cluster.cluster_uuid, - start, - end, - [] - ), - alertsMeta: { - enabled: true, - }, - }; - continue; - } + for (const cluster of clusters) { + const verification = verifyMonitoringLicense(req.server); + if (!verification.enabled) { + // return metadata detailing that alerts is disabled because of the monitoring cluster license + cluster.alerts = { + alertsMeta: { + enabled: verification.enabled, + message: verification.message, // NOTE: this is only defined when the alert feature is disabled + }, + list: {}, + }; + continue; + } + if (!alertsClient) { cluster.alerts = { list: {}, alertsMeta: { - enabled: true, - }, - clusterMeta: { enabled: false, - message: i18n.translate( - 'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription', - { - defaultMessage: - 'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts', - values: { - clusterName: cluster.cluster_name, - licenseType: `${license.type}`, - }, - } - ), }, }; + continue; + } + + // check the license type of the production cluster for alerts feature support + const license = cluster.license || {}; + const prodLicenseInfo = checkLicenseForAlerts( + license.type, + license.status === 'active', + 'production' + ); + if (prodLicenseInfo.clusterAlerts.enabled) { + cluster.alerts = { + list: await fetchStatus( + alertsClient, + req.server.plugins.monitoring.info, + undefined, + cluster.cluster_uuid, + start, + end, + [] + ), + alertsMeta: { + enabled: true, + }, + }; + continue; } + + cluster.alerts = { + list: {}, + alertsMeta: { + enabled: false, + }, + clusterMeta: { + enabled: false, + message: i18n.translate( + 'xpack.monitoring.clusterAlerts.unsupportedClusterAlertsDescription', + { + defaultMessage: + 'Cluster [{clusterName}] license type [{licenseType}] does not support Cluster Alerts', + values: { + clusterName: cluster.cluster_name, + licenseType: `${license.type}`, + }, + } + ), + }, + }; } } } diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index f5cbadb523a8..d874c868ae8e 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -239,7 +239,7 @@ export class Plugin { } registerPluginInUI(plugins: PluginsSetup) { - plugins.features.registerFeature({ + plugins.features.registerKibanaFeature({ id: 'monitoring', name: i18n.translate('xpack.monitoring.featureRegistry.monitoringFeatureName', { defaultMessage: 'Stack Monitoring', diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/observability/public/components/app/header/index.tsx index 0e35fbb008be..e8bd229265e3 100644 --- a/x-pack/plugins/observability/public/components/app/header/index.tsx +++ b/x-pack/plugins/observability/public/components/app/header/index.tsx @@ -5,7 +5,6 @@ */ import { - EuiBetaBadge, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, @@ -58,12 +57,7 @@ export function Header({

{i18n.translate('xpack.observability.home.title', { defaultMessage: 'Observability', - })}{' '} - + })}

diff --git a/x-pack/plugins/observability/public/components/app/news_feed/index.tsx b/x-pack/plugins/observability/public/components/app/news_feed/index.tsx index 625ae94c90aa..86466baa4541 100644 --- a/x-pack/plugins/observability/public/components/app/news_feed/index.tsx +++ b/x-pack/plugins/observability/public/components/app/news_feed/index.tsx @@ -70,13 +70,13 @@ function NewsItem({ item }: { item: INewsItem }) { - - + + {i18n.translate('xpack.observability.news.readFullStory', { defaultMessage: 'Read full story', })} - - + + diff --git a/x-pack/plugins/observability/public/hooks/use_chart_theme.tsx b/x-pack/plugins/observability/public/hooks/use_chart_theme.tsx index 13f7159ba604..b5bfe3eec7d3 100644 --- a/x-pack/plugins/observability/public/hooks/use_chart_theme.tsx +++ b/x-pack/plugins/observability/public/hooks/use_chart_theme.tsx @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; -import { useContext } from 'react'; -import { ThemeContext } from 'styled-components'; +import { useTheme } from './use_theme'; export function useChartTheme() { - const theme = useContext(ThemeContext); + const theme = useTheme(); return theme.darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme; } diff --git a/x-pack/plugins/observability/public/hooks/use_theme.tsx b/x-pack/plugins/observability/public/hooks/use_theme.tsx new file mode 100644 index 000000000000..d0449a4432d9 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_theme.tsx @@ -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 { useContext } from 'react'; +import { ThemeContext } from 'styled-components'; +import { EuiTheme } from '../../../../legacy/common/eui_styled_components'; + +export function useTheme() { + const theme: EuiTheme = useContext(ThemeContext); + return theme; +} diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 03939736b64a..0aecea59ad01 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -26,3 +26,6 @@ export { } from './hooks/use_track_metric'; export * from './typings'; + +export { useChartTheme } from './hooks/use_chart_theme'; +export { useTheme } from './hooks/use_theme'; diff --git a/x-pack/plugins/observability/public/pages/overview/index.tsx b/x-pack/plugins/observability/public/pages/overview/index.tsx index 8870bcbc9fa3..10bbdaaae34a 100644 --- a/x-pack/plugins/observability/public/pages/overview/index.tsx +++ b/x-pack/plugins/observability/public/pages/overview/index.tsx @@ -200,7 +200,7 @@ export function OverviewPage({ routeParams }: Props) { {!!newsFeed?.items?.length && ( - + )} diff --git a/x-pack/plugins/oss_telemetry/constants.ts b/x-pack/plugins/oss_telemetry/constants.ts deleted file mode 100644 index 1e83bff092f2..000000000000 --- a/x-pack/plugins/oss_telemetry/constants.ts +++ /dev/null @@ -1,9 +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 PLUGIN_ID = 'oss_telemetry'; // prefix used for registering properties with services from this plugin -export const VIS_TELEMETRY_TASK = 'vis_telemetry'; // suffix for the _id of our task instance, which must be `get`-able -export const VIS_USAGE_TYPE = 'visualization_types'; // suffix for the properties of data registered with the usage service diff --git a/x-pack/plugins/oss_telemetry/kibana.json b/x-pack/plugins/oss_telemetry/kibana.json deleted file mode 100644 index 0defee0881e0..000000000000 --- a/x-pack/plugins/oss_telemetry/kibana.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "ossTelemetry", - "server": true, - "version": "8.0.0", - "kibanaVersion": "kibana", - "requiredPlugins": ["usageCollection", "taskManager"], - "configPath": ["xpack", "oss_telemetry"], - "ui": false -} diff --git a/x-pack/plugins/oss_telemetry/server/index.ts b/x-pack/plugins/oss_telemetry/server/index.ts deleted file mode 100644 index 64527ca6daa7..000000000000 --- a/x-pack/plugins/oss_telemetry/server/index.ts +++ /dev/null @@ -1,12 +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 { PluginInitializerContext } from 'src/core/server'; -import { OssTelemetryPlugin } from './plugin'; - -export const plugin = (context: PluginInitializerContext) => new OssTelemetryPlugin(context); - -export * from './plugin'; diff --git a/x-pack/plugins/oss_telemetry/server/lib/collectors/index.ts b/x-pack/plugins/oss_telemetry/server/lib/collectors/index.ts deleted file mode 100644 index 845e11b80af0..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/collectors/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { registerVisualizationsCollector } from './visualizations/register_usage_collector'; -import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server'; -import { TaskManagerStartContract } from '../../../../task_manager/server'; - -export function registerCollectors( - usageCollection: UsageCollectionSetup, - taskManager: Promise -) { - registerVisualizationsCollector(usageCollection, taskManager); -} diff --git a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.test.ts b/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.test.ts deleted file mode 100644 index 43114787b40e..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.test.ts +++ /dev/null @@ -1,69 +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 { - getMockTaskFetch, - getMockThrowingTaskFetch, - getMockTaskInstance, -} from '../../../test_utils'; -import { taskManagerMock } from '../../../../../task_manager/server/task_manager.mock'; -import { getUsageCollector } from './get_usage_collector'; - -describe('getVisualizationsCollector#fetch', () => { - test('can return empty stats', async () => { - const { type, fetch } = getUsageCollector( - Promise.resolve(taskManagerMock.start(getMockTaskFetch())) - ); - expect(type).toBe('visualization_types'); - const fetchResult = await fetch(); - expect(fetchResult).toEqual({}); - }); - - test('provides known stats', async () => { - const { type, fetch } = getUsageCollector( - Promise.resolve( - taskManagerMock.start( - getMockTaskFetch([ - getMockTaskInstance({ - state: { - runs: 1, - stats: { comic_books: { total: 16, max: 12, min: 2, avg: 6 } }, - }, - taskType: 'test', - params: {}, - }), - ]) - ) - ) - ); - expect(type).toBe('visualization_types'); - const fetchResult = await fetch(); - expect(fetchResult).toEqual({ comic_books: { avg: 6, max: 12, min: 2, total: 16 } }); - }); - - describe('Error handling', () => { - test('Silently handles Task Manager NotInitialized', async () => { - const { fetch } = getUsageCollector( - Promise.resolve( - taskManagerMock.start( - getMockThrowingTaskFetch( - new Error('NotInitialized taskManager is still waiting for plugins to load') - ) - ) - ) - ); - const result = await fetch(); - expect(result).toBe(undefined); - }); - // In real life, the CollectorSet calls fetch and handles errors - test('defers the errors', async () => { - const { fetch } = getUsageCollector( - Promise.resolve(taskManagerMock.start(getMockThrowingTaskFetch(new Error('BOOM')))) - ); - await expect(fetch()).rejects.toThrowErrorMatchingInlineSnapshot(`"BOOM"`); - }); - }); -}); diff --git a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.ts b/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.ts deleted file mode 100644 index 9828dea4c939..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/get_usage_collector.ts +++ /dev/null @@ -1,42 +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 { get } from 'lodash'; -import { PLUGIN_ID, VIS_TELEMETRY_TASK, VIS_USAGE_TYPE } from '../../../../constants'; -import { TaskManagerStartContract } from '../../../../../task_manager/server'; - -async function fetch(taskManager: TaskManagerStartContract) { - let docs; - try { - ({ docs } = await taskManager.fetch({ - query: { bool: { filter: { term: { _id: `task:${PLUGIN_ID}-${VIS_TELEMETRY_TASK}` } } } }, - })); - } catch (err) { - const errMessage = err && err.message ? err.message : err.toString(); - /* - The usage service WILL to try to fetch from this collector before the task manager has been initialized, because the task manager has to wait for all plugins to initialize first. It's fine to ignore it as next time around it will be initialized (or it will throw a different type of error) - */ - if (errMessage.includes('NotInitialized')) { - docs = null; - } else { - throw err; - } - } - - return docs; -} - -export function getUsageCollector(taskManager: Promise) { - return { - type: VIS_USAGE_TYPE, - isReady: () => true, - fetch: async () => { - const docs = await fetch(await taskManager); - // get the accumulated state from the recurring task - return get(docs, '[0].state.stats'); - }, - }; -} diff --git a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/register_usage_collector.ts b/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/register_usage_collector.ts deleted file mode 100644 index 667e8b9b875f..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/collectors/visualizations/register_usage_collector.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { TaskManagerStartContract } from '../../../../../task_manager/server'; -import { getUsageCollector } from './get_usage_collector'; - -export function registerVisualizationsCollector( - collectorSet: UsageCollectionSetup, - taskManager: Promise -): void { - const collector = collectorSet.makeUsageCollector(getUsageCollector(taskManager)); - collectorSet.registerCollector(collector); -} diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.test.ts b/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.test.ts deleted file mode 100644 index 3bafb84d6115..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import moment from 'moment'; -import { getNextMidnight } from './get_next_midnight'; - -describe('getNextMidnight', () => { - test('Returns the next time and date of midnight as an iso string', () => { - const nextMidnightMoment = moment().add(1, 'days').startOf('day').toDate(); - - expect(getNextMidnight()).toEqual(nextMidnightMoment); - }); -}); diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.ts b/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.ts deleted file mode 100644 index a5ee8d572343..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/get_next_midnight.ts +++ /dev/null @@ -1,12 +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 function getNextMidnight() { - const nextMidnight = new Date(); - nextMidnight.setHours(0, 0, 0, 0); - nextMidnight.setDate(nextMidnight.getDate() + 1); - return nextMidnight; -} diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.ts deleted file mode 100644 index 28909779343a..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.test.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 moment from 'moment'; -import { getPastDays } from './get_past_days'; - -describe('getPastDays', () => { - test('Returns 2 days that have passed from the current date', () => { - const pastDate = moment().subtract(2, 'days').startOf('day').toString(); - - expect(getPastDays(pastDate)).toEqual(2); - }); - - test('Returns 30 days that have passed from the current date', () => { - const pastDate = moment().subtract(30, 'days').startOf('day').toString(); - - expect(getPastDays(pastDate)).toEqual(30); - }); -}); diff --git a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts b/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts deleted file mode 100644 index 4f25ef147ad4..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/get_past_days.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -export const getPastDays = (dateString: string): number => { - const date = new Date(dateString); - const today = new Date(); - const diff = Math.abs(date.getTime() - today.getTime()); - return Math.trunc(diff / (1000 * 60 * 60 * 24)); -}; diff --git a/x-pack/plugins/oss_telemetry/server/lib/tasks/index.ts b/x-pack/plugins/oss_telemetry/server/lib/tasks/index.ts deleted file mode 100644 index 415aeb2791d9..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/tasks/index.ts +++ /dev/null @@ -1,73 +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 { Observable } from 'rxjs'; -import { CoreSetup, Logger } from 'kibana/server'; -import { PLUGIN_ID, VIS_TELEMETRY_TASK } from '../../../constants'; -import { visualizationsTaskRunner } from './visualizations/task_runner'; -import { - TaskInstance, - TaskManagerStartContract, - TaskManagerSetupContract, -} from '../../../../task_manager/server'; - -export function registerTasks({ - taskManager, - logger, - getStartServices, - config, -}: { - taskManager?: TaskManagerSetupContract; - logger: Logger; - getStartServices: CoreSetup['getStartServices']; - config: Observable<{ kibana: { index: string } }>; -}) { - if (!taskManager) { - logger.debug('Task manager is not available'); - return; - } - - const esClientPromise = getStartServices().then( - ([{ elasticsearch }]) => elasticsearch.legacy.client - ); - - taskManager.registerTaskDefinitions({ - [VIS_TELEMETRY_TASK]: { - title: 'X-Pack telemetry calculator for Visualizations', - type: VIS_TELEMETRY_TASK, - createTaskRunner({ taskInstance }: { taskInstance: TaskInstance }) { - return { - run: visualizationsTaskRunner(taskInstance, config, esClientPromise), - cancel: async () => {}, - }; - }, - }, - }); -} - -export async function scheduleTasks({ - taskManager, - logger, -}: { - taskManager?: TaskManagerStartContract; - logger: Logger; -}) { - if (!taskManager) { - logger.debug('Task manager is not available'); - return; - } - - try { - await taskManager.ensureScheduled({ - id: `${PLUGIN_ID}-${VIS_TELEMETRY_TASK}`, - taskType: VIS_TELEMETRY_TASK, - state: { stats: {}, runs: 0 }, - params: {}, - }); - } catch (e) { - logger.debug(`Error scheduling task, received ${e.message}`); - } -} diff --git a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts deleted file mode 100644 index c064f39f4bc6..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.test.ts +++ /dev/null @@ -1,211 +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 { - getMockCallWithInternal, - getMockConfig, - getMockEs, - getMockTaskInstance, -} from '../../../test_utils'; -import { visualizationsTaskRunner } from './task_runner'; -import { TaskInstance } from '../../../../../task_manager/server'; -import { getNextMidnight } from '../../get_next_midnight'; -import moment from 'moment'; - -describe('visualizationsTaskRunner', () => { - let mockTaskInstance: TaskInstance; - beforeEach(() => { - mockTaskInstance = getMockTaskInstance(); - }); - - describe('Error handling', () => { - test('catches its own errors', async () => { - const mockCallWithInternal = () => Promise.reject(new Error('Things did not go well!')); - - const runner = visualizationsTaskRunner( - mockTaskInstance, - getMockConfig(), - getMockEs(mockCallWithInternal) - ); - const result = await runner(); - expect(result).toMatchObject({ - error: 'Things did not go well!', - state: { - runs: 1, - stats: undefined, - }, - }); - }); - }); - - test('Summarizes visualization response data', async () => { - const runner = visualizationsTaskRunner(mockTaskInstance, getMockConfig(), getMockEs()); - const result = await runner(); - - expect(result).toMatchObject({ - error: undefined, - runAt: getNextMidnight(), - state: { - runs: 1, - stats: { - shell_beads: { - spaces_avg: 1, - spaces_max: 1, - spaces_min: 1, - total: 1, - saved_7_days_total: 1, - saved_30_days_total: 1, - saved_90_days_total: 1, - }, - }, - }, - }); - }); - - test('Summarizes visualization response data per Space', async () => { - const mockCallWithInternal = getMockCallWithInternal([ - // default space - { - _id: 'visualization:coolviz-123', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "cave_painting"}' }, - updated_at: moment().subtract(7, 'days').startOf('day').toString(), - }, - }, - { - _id: 'visualization:coolviz-456', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "printing_press"}' }, - updated_at: moment().subtract(20, 'days').startOf('day').toString(), - }, - }, - { - _id: 'meat:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "floppy_disk"}' }, - updated_at: moment().subtract(2, 'months').startOf('day').toString(), - }, - }, - // meat space - { - _id: 'meat:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "cave_painting"}' }, - updated_at: moment().subtract(89, 'days').startOf('day').toString(), - }, - }, - { - _id: 'meat:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "cuneiform"}' }, - updated_at: moment().subtract(5, 'months').startOf('day').toString(), - }, - }, - { - _id: 'meat:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "cuneiform"}' }, - updated_at: moment().subtract(2, 'days').startOf('day').toString(), - }, - }, - { - _id: 'meat:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "floppy_disk"}' }, - updated_at: moment().subtract(7, 'days').startOf('day').toString(), - }, - }, - // cyber space - { - _id: 'cyber:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "floppy_disk"}' }, - updated_at: moment().subtract(7, 'months').startOf('day').toString(), - }, - }, - { - _id: 'cyber:visualization:coolviz-789', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "floppy_disk"}' }, - updated_at: moment().subtract(3, 'days').startOf('day').toString(), - }, - }, - { - _id: 'cyber:visualization:coolviz-123', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "cave_painting"}' }, - updated_at: moment().subtract(15, 'days').startOf('day').toString(), - }, - }, - ]); - - const expectedStats = { - cave_painting: { - total: 3, - spaces_min: 1, - spaces_max: 1, - spaces_avg: 1, - saved_7_days_total: 1, - saved_30_days_total: 2, - saved_90_days_total: 3, - }, - printing_press: { - total: 1, - spaces_min: 1, - spaces_max: 1, - spaces_avg: 1, - saved_7_days_total: 0, - saved_30_days_total: 1, - saved_90_days_total: 1, - }, - cuneiform: { - total: 2, - spaces_min: 2, - spaces_max: 2, - spaces_avg: 2, - saved_7_days_total: 1, - saved_30_days_total: 1, - saved_90_days_total: 1, - }, - floppy_disk: { - total: 4, - spaces_min: 2, - spaces_max: 2, - spaces_avg: 2, - saved_7_days_total: 2, - saved_30_days_total: 2, - saved_90_days_total: 3, - }, - }; - - const runner = visualizationsTaskRunner( - mockTaskInstance, - getMockConfig(), - getMockEs(mockCallWithInternal) - ); - const result = await runner(); - - expect(result).toMatchObject({ - error: undefined, - state: { - runs: 1, - stats: expectedStats, - }, - }); - - expect(result.state.stats).toMatchObject(expectedStats); - }); -}); diff --git a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts b/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts deleted file mode 100644 index 27913fafe325..000000000000 --- a/x-pack/plugins/oss_telemetry/server/lib/tasks/visualizations/task_runner.ts +++ /dev/null @@ -1,114 +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 { Observable } from 'rxjs'; -import _, { countBy, groupBy, mapValues } from 'lodash'; -import { first } from 'rxjs/operators'; - -import { LegacyAPICaller, ILegacyClusterClient } from 'src/core/server'; -import { getNextMidnight } from '../../get_next_midnight'; -import { getPastDays } from '../../get_past_days'; -import { TaskInstance } from '../../../../../task_manager/server'; -import { ESSearchHit } from '../../../../../apm/typings/elasticsearch'; - -interface VisSummary { - type: string; - space: string; - past_days: number; -} - -/* - * Parse the response data into telemetry payload - */ -async function getStats(callCluster: LegacyAPICaller, index: string) { - const searchParams = { - size: 10000, // elasticsearch index.max_result_window default value - index, - ignoreUnavailable: true, - filterPath: [ - 'hits.hits._id', - 'hits.hits._source.visualization', - 'hits.hits._source.updated_at', - ], - body: { - query: { - bool: { filter: { term: { type: 'visualization' } } }, - }, - }, - }; - const esResponse = await callCluster('search', searchParams); - const size = _.get(esResponse, 'hits.hits.length') as number; - if (size < 1) { - return; - } - - // `map` to get the raw types - const visSummaries: VisSummary[] = esResponse.hits.hits.map( - (hit: ESSearchHit<{ visState: string }>) => { - const spacePhrases: string[] = hit._id.split(':'); - const lastUpdated: string = _.get(hit, '_source.updated_at'); - const space = spacePhrases.length === 3 ? spacePhrases[0] : 'default'; // if in a custom space, the format of a saved object ID is space:type:id - const visualization = _.get(hit, '_source.visualization', { visState: '{}' }); - const visState: { type?: string } = JSON.parse(visualization.visState); - return { - type: visState.type || '_na_', - space, - past_days: getPastDays(lastUpdated), - }; - } - ); - - // organize stats per type - const visTypes = groupBy(visSummaries, 'type'); - - // get the final result - return mapValues(visTypes, (curr) => { - const total = curr.length; - const spacesBreakdown = countBy(curr, 'space'); - const spaceCounts: number[] = _.values(spacesBreakdown); - - return { - total, - spaces_min: _.min(spaceCounts), - spaces_max: _.max(spaceCounts), - spaces_avg: total / spaceCounts.length, - saved_7_days_total: curr.filter((c) => c.past_days <= 7).length, - saved_30_days_total: curr.filter((c) => c.past_days <= 30).length, - saved_90_days_total: curr.filter((c) => c.past_days <= 90).length, - }; - }); -} - -export function visualizationsTaskRunner( - taskInstance: TaskInstance, - config: Observable<{ kibana: { index: string } }>, - esClientPromise: Promise -) { - return async () => { - let stats; - let error; - - try { - const index = (await config.pipe(first()).toPromise()).kibana.index; - stats = await getStats((await esClientPromise).callAsInternalUser, index); - } catch (err) { - if (err.constructor === Error) { - error = err.message; - } else { - error = err; - } - } - - return { - runAt: getNextMidnight(), - state: { - runs: taskInstance.state.runs + 1, - stats, - }, - error, - }; - }; -} diff --git a/x-pack/plugins/oss_telemetry/server/plugin.ts b/x-pack/plugins/oss_telemetry/server/plugin.ts deleted file mode 100644 index 6a447da66952..000000000000 --- a/x-pack/plugins/oss_telemetry/server/plugin.ts +++ /dev/null @@ -1,53 +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 { Observable } from 'rxjs'; -import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server'; -import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; -import { registerCollectors } from './lib/collectors'; -import { registerTasks, scheduleTasks } from './lib/tasks'; -import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; - -export interface OssTelemetrySetupDependencies { - usageCollection: UsageCollectionSetup; - taskManager: TaskManagerSetupContract; -} -export interface OssTelemetryStartDependencies { - taskManager: TaskManagerStartContract; -} - -export class OssTelemetryPlugin implements Plugin { - private readonly logger: Logger; - private readonly config: Observable<{ kibana: { index: string } }>; - - constructor(initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get('oss_telemetry'); - this.config = initializerContext.config.legacy.globalConfig$; - } - - public setup( - core: CoreSetup, - deps: OssTelemetrySetupDependencies - ) { - registerTasks({ - taskManager: deps.taskManager, - logger: this.logger, - getStartServices: core.getStartServices, - config: this.config, - }); - registerCollectors( - deps.usageCollection, - core.getStartServices().then(([_, { taskManager }]) => taskManager) - ); - } - - public start(core: CoreStart, deps: OssTelemetryStartDependencies) { - scheduleTasks({ - taskManager: deps.taskManager, - logger: this.logger, - }); - } -} diff --git a/x-pack/plugins/oss_telemetry/server/test_utils/index.ts b/x-pack/plugins/oss_telemetry/server/test_utils/index.ts deleted file mode 100644 index 9201899d5a16..000000000000 --- a/x-pack/plugins/oss_telemetry/server/test_utils/index.ts +++ /dev/null @@ -1,90 +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 { LegacyAPICaller } from 'kibana/server'; - -import { of } from 'rxjs'; -import moment from 'moment'; -import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; -import { - ConcreteTaskInstance, - TaskStatus, - TaskManagerStartContract, -} from '../../../task_manager/server'; - -export const getMockTaskInstance = ( - overrides: Partial = {} -): ConcreteTaskInstance => ({ - state: { runs: 0, stats: {} }, - taskType: 'test', - params: {}, - id: '', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - ownerId: null, - ...overrides, -}); - -const defaultMockSavedObjects = [ - { - _id: 'visualization:coolviz-123', - _source: { - type: 'visualization', - visualization: { visState: '{"type": "shell_beads"}' }, - updated_at: moment().subtract(7, 'days').startOf('day').toString(), - }, - }, -]; - -const defaultMockTaskDocs = [getMockTaskInstance()]; - -export const getMockEs = async ( - mockCallWithInternal: LegacyAPICaller = getMockCallWithInternal() -) => { - const client = elasticsearchServiceMock.createLegacyClusterClient(); - (client.callAsInternalUser as any) = mockCallWithInternal; - return client; -}; - -export const getMockCallWithInternal = ( - hits: unknown[] = defaultMockSavedObjects -): LegacyAPICaller => { - return ((() => { - return Promise.resolve({ hits: { hits } }); - }) as unknown) as LegacyAPICaller; -}; - -export const getMockTaskFetch = ( - docs: ConcreteTaskInstance[] = defaultMockTaskDocs -): Partial> => { - return { - fetch: jest.fn((fetchOpts) => { - return Promise.resolve({ docs, searchAfter: [] }); - }), - } as Partial>; -}; - -export const getMockThrowingTaskFetch = ( - throws: Error -): Partial> => { - return { - fetch: jest.fn((fetchOpts) => { - throw throws; - }), - } as Partial>; -}; - -export const getMockConfig = () => { - return of({ kibana: { index: '' } }); -}; - -export const getCluster = () => ({ - callWithInternalUser: getMockCallWithInternal(), -}); diff --git a/x-pack/plugins/remote_clusters/kibana.json b/x-pack/plugins/remote_clusters/kibana.json index d90d6ea46057..0334af5a868f 100644 --- a/x-pack/plugins/remote_clusters/kibana.json +++ b/x-pack/plugins/remote_clusters/kibana.json @@ -8,7 +8,8 @@ "requiredPlugins": [ "licensing", "management", - "indexManagement" + "indexManagement", + "features" ], "optionalPlugins": [ "usageCollection", diff --git a/x-pack/plugins/remote_clusters/server/plugin.ts b/x-pack/plugins/remote_clusters/server/plugin.ts index 9b2d6a0a0538..0bef5d70fe70 100644 --- a/x-pack/plugins/remote_clusters/server/plugin.ts +++ b/x-pack/plugins/remote_clusters/server/plugin.ts @@ -35,7 +35,7 @@ export class RemoteClustersServerPlugin this.licenseStatus = { valid: false }; } - async setup({ http }: CoreSetup, { licensing, cloud }: Dependencies) { + async setup({ http }: CoreSetup, { features, licensing, cloud }: Dependencies) { const router = http.createRouter(); const config = await this.config$.pipe(first()).toPromise(); @@ -47,6 +47,19 @@ export class RemoteClustersServerPlugin }, }; + features.registerElasticsearchFeature({ + id: 'remote_clusters', + management: { + data: ['remote_clusters'], + }, + privileges: [ + { + requiredClusterPrivileges: ['manage'], + ui: [], + }, + ], + }); + // Register routes registerGetRoute(routeDependencies); registerAddRoute(routeDependencies); diff --git a/x-pack/plugins/remote_clusters/server/types.ts b/x-pack/plugins/remote_clusters/server/types.ts index 23f4ed158c2d..86862a90da9c 100644 --- a/x-pack/plugins/remote_clusters/server/types.ts +++ b/x-pack/plugins/remote_clusters/server/types.ts @@ -5,12 +5,14 @@ */ import { IRouter } from 'kibana/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { CloudSetup } from '../../cloud/server'; export interface Dependencies { licensing: LicensingPluginSetup; cloud: CloudSetup; + features: FeaturesPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/reporting/kibana.json b/x-pack/plugins/reporting/kibana.json index a5d7f3d20c44..33141eec4629 100644 --- a/x-pack/plugins/reporting/kibana.json +++ b/x-pack/plugins/reporting/kibana.json @@ -14,7 +14,8 @@ "licensing", "uiActions", "embeddable", - "share" + "share", + "features" ], "server": true, "ui": true, diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index be32b52f1981..3657d323b3ed 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -15,6 +15,7 @@ import { SavedObjectsServiceStart, UiSettingsServiceStart, } from 'src/core/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; import { ReportingConfig } from './'; @@ -25,6 +26,7 @@ import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/scr import { ReportingStore } from './lib/store'; export interface ReportingInternalSetup { + features: FeaturesPluginSetup; elasticsearch: ElasticsearchServiceSetup; licensing: LicensingPluginSetup; basePath: BasePath['get']; @@ -99,6 +101,26 @@ export class ReportingCore { this.pluginSetup$.next(true); } + /** + * Registers reporting as an Elasticsearch feature for the purpose of toggling visibility based on roles. + */ + public registerFeature() { + const config = this.getConfig(); + const allowedRoles = ['superuser', ...(config.get('roles')?.allow ?? [])]; + this.getPluginSetupDeps().features.registerElasticsearchFeature({ + id: 'reporting', + catalogue: ['reporting'], + management: { + insightsAndAlerting: ['reporting'], + }, + privileges: allowedRoles.map((role) => ({ + requiredClusterPrivileges: [], + requiredRoles: [role], + ui: [], + })), + }); + } + /* * Gives synchronous access to the config */ diff --git a/x-pack/plugins/reporting/server/plugin.test.ts b/x-pack/plugins/reporting/server/plugin.test.ts index e0d018869cef..d323a281c06f 100644 --- a/x-pack/plugins/reporting/server/plugin.test.ts +++ b/x-pack/plugins/reporting/server/plugin.test.ts @@ -17,6 +17,7 @@ jest.mock('./browsers/install', () => ({ import { coreMock } from 'src/core/server/mocks'; import { ReportingPlugin } from './plugin'; import { createMockConfigSchema } from './test_helpers'; +import { featuresPluginMock } from '../../features/server/mocks'; const sleep = (time: number) => new Promise((r) => setTimeout(r, time)); @@ -35,6 +36,7 @@ describe('Reporting Plugin', () => { coreStart = await coreMock.createStart(); pluginSetup = ({ licensing: {}, + features: featuresPluginMock.createSetup(), usageCollection: { makeUsageCollector: jest.fn(), registerCollector: jest.fn(), diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index af1ccfd592b9..adb89abe2028 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -70,13 +70,14 @@ export class ReportingPlugin }); const { elasticsearch, http } = core; - const { licensing, security } = plugins; + const { features, licensing, security } = plugins; const { initializerContext: initContext, reportingCore } = this; const router = http.createRouter(); const basePath = http.basePath.get; reportingCore.pluginSetup({ + features, elasticsearch, licensing, basePath, @@ -91,6 +92,8 @@ export class ReportingPlugin (async () => { const config = await buildConfig(initContext, core, this.logger); reportingCore.setConfig(config); + // Feature registration relies on config, so it cannot be setup before here. + reportingCore.registerFeature(); this.logger.debug('Setup complete'); })().catch((e) => { this.logger.error(`Error in Reporting setup, reporting may not function properly`); diff --git a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts index d1ebb4d59e63..559726e0b8a9 100644 --- a/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts +++ b/x-pack/plugins/reporting/server/test_helpers/create_mock_reportingplugin.ts @@ -10,6 +10,7 @@ jest.mock('../browsers'); jest.mock('../lib/create_queue'); import * as Rx from 'rxjs'; +import { featuresPluginMock } from '../../../features/server/mocks'; import { ReportingConfig, ReportingCore } from '../'; import { chromium, @@ -32,6 +33,7 @@ const createMockPluginSetup = ( setupMock?: any ): ReportingInternalSetup => { return { + features: featuresPluginMock.createSetup(), elasticsearch: setupMock.elasticsearch || { legacy: { client: {} } }, basePath: setupMock.basePath || '/all-about-that-basepath', router: setupMock.router, diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index bb2d5368cd18..c67a95c2de75 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -9,6 +9,7 @@ import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { DataPluginStart } from 'src/plugins/data/server/plugin'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { CancellationToken } from '../../../plugins/reporting/common'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { AuthenticatedUser, SecurityPluginSetup } from '../../security/server'; import { JobStatus } from '../common/types'; @@ -92,6 +93,7 @@ export interface ConditionalHeaders { export interface ReportingSetupDeps { licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; security?: SecurityPluginSetup; usageCollection?: UsageCollectionSetup; } diff --git a/x-pack/plugins/rollup/kibana.json b/x-pack/plugins/rollup/kibana.json index e6915f65599c..725b563c3674 100644 --- a/x-pack/plugins/rollup/kibana.json +++ b/x-pack/plugins/rollup/kibana.json @@ -7,7 +7,8 @@ "requiredPlugins": [ "indexPatternManagement", "management", - "licensing" + "licensing", + "features" ], "optionalPlugins": [ "home", diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts index 713852b4d739..8b3a6355f950 100644 --- a/x-pack/plugins/rollup/server/plugin.ts +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -64,7 +64,7 @@ export class RollupPlugin implements Plugin { public setup( { http, uiSettings, getStartServices }: CoreSetup, - { licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies + { features, licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies ) { this.license.setup( { @@ -80,6 +80,20 @@ export class RollupPlugin implements Plugin { } ); + features.registerElasticsearchFeature({ + id: 'rollup_jobs', + management: { + data: ['rollup_jobs'], + }, + catalogue: ['rollup_jobs'], + privileges: [ + { + requiredClusterPrivileges: ['manage_rollup'], + ui: [], + }, + ], + }); + http.registerRouteHandlerContext('rollup', async (context, request) => { this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); return { diff --git a/x-pack/plugins/rollup/server/types.ts b/x-pack/plugins/rollup/server/types.ts index 2a7644de764b..290d2df05009 100644 --- a/x-pack/plugins/rollup/server/types.ts +++ b/x-pack/plugins/rollup/server/types.ts @@ -9,6 +9,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { License } from './services'; import { IndexPatternsFetcher } from './shared_imports'; @@ -22,6 +23,7 @@ export interface Dependencies { visTypeTimeseries?: VisTypeTimeseriesSetup; usageCollection?: UsageCollectionSetup; licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/security/common/licensing/index.mock.ts b/x-pack/plugins/security/common/licensing/index.mock.ts index 06a7057abb87..87225f479cee 100644 --- a/x-pack/plugins/security/common/licensing/index.mock.ts +++ b/x-pack/plugins/security/common/licensing/index.mock.ts @@ -9,6 +9,7 @@ import { SecurityLicense } from '.'; export const licenseMock = { create: (): jest.Mocked => ({ + isLicenseAvailable: jest.fn(), isEnabled: jest.fn().mockReturnValue(true), getFeatures: jest.fn(), features$: of(), diff --git a/x-pack/plugins/security/common/licensing/license_service.test.ts b/x-pack/plugins/security/common/licensing/license_service.test.ts index 564b71a2e0fa..94aad8d3ac53 100644 --- a/x-pack/plugins/security/common/licensing/license_service.test.ts +++ b/x-pack/plugins/security/common/licensing/license_service.test.ts @@ -13,6 +13,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(undefined as any), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(false); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: false, @@ -34,6 +35,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(rawLicenseMock), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(false); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: false, @@ -60,6 +62,7 @@ describe('license features', function () { const subscriptionHandler = jest.fn(); const subscription = serviceSetup.license.features$.subscribe(subscriptionHandler); try { + expect(serviceSetup.license.isLicenseAvailable()).toEqual(false); expect(subscriptionHandler).toHaveBeenCalledTimes(1); expect(subscriptionHandler.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -80,6 +83,7 @@ describe('license features', function () { `); rawLicense$.next(licenseMock.createLicenseMock()); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(subscriptionHandler).toHaveBeenCalledTimes(2); expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(` Array [ @@ -112,6 +116,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(mockRawLicense), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: true, @@ -136,6 +141,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(mockRawLicense), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: false, allowLogin: false, @@ -159,6 +165,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(mockRawLicense), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: true, @@ -182,6 +189,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(mockRawLicense), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: true, @@ -205,6 +213,7 @@ describe('license features', function () { const serviceSetup = new SecurityLicenseService().setup({ license$: of(mockRawLicense), }); + expect(serviceSetup.license.isLicenseAvailable()).toEqual(true); expect(serviceSetup.license.getFeatures()).toEqual({ showLogin: true, allowLogin: true, diff --git a/x-pack/plugins/security/common/licensing/license_service.ts b/x-pack/plugins/security/common/licensing/license_service.ts index 75c7670f28a6..09b6ae95c282 100644 --- a/x-pack/plugins/security/common/licensing/license_service.ts +++ b/x-pack/plugins/security/common/licensing/license_service.ts @@ -10,6 +10,7 @@ import { ILicense } from '../../../licensing/common/types'; import { SecurityLicenseFeatures } from './license_features'; export interface SecurityLicense { + isLicenseAvailable(): boolean; isEnabled(): boolean; getFeatures(): SecurityLicenseFeatures; features$: Observable; @@ -31,6 +32,8 @@ export class SecurityLicenseService { return { license: Object.freeze({ + isLicenseAvailable: () => rawLicense?.isAvailable ?? false, + isEnabled: () => this.isSecurityEnabledFromRawLicense(rawLicense), getFeatures: () => this.calculateFeaturesFromRawLicense(rawLicense), diff --git a/x-pack/plugins/security/kibana.json b/x-pack/plugins/security/kibana.json index 6a09e9e55a01..40d7e293eaf6 100644 --- a/x-pack/plugins/security/kibana.json +++ b/x-pack/plugins/security/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "security"], "requiredPlugins": ["data", "features", "licensing", "taskManager"], - "optionalPlugins": ["home", "management"], + "optionalPlugins": ["home", "management", "usageCollection"], "server": true, "ui": true, "requiredBundles": [ diff --git a/x-pack/plugins/security/public/management/management_service.test.ts b/x-pack/plugins/security/public/management/management_service.test.ts index ce93fb7c98f4..cd06693a43bf 100644 --- a/x-pack/plugins/security/public/management/management_service.test.ts +++ b/x-pack/plugins/security/public/management/management_service.test.ts @@ -78,7 +78,10 @@ describe('ManagementService', () => { }); describe('start()', () => { - function startService(initialFeatures: Partial) { + function startService( + initialFeatures: Partial, + canManageSecurity: boolean = true + ) { const { fatalErrors, getStartServices } = coreMock.createSetup(); const licenseSubject = new BehaviorSubject( @@ -106,10 +109,11 @@ describe('ManagementService', () => { management: managementSetup, }); - const getMockedApp = () => { + const getMockedApp = (id: string) => { // All apps are enabled by default. let enabled = true; return ({ + id, get enabled() { return enabled; }, @@ -123,13 +127,26 @@ describe('ManagementService', () => { }; mockSection.getApp = jest.fn().mockImplementation((id) => mockApps.get(id)); const mockApps = new Map>([ - [usersManagementApp.id, getMockedApp()], - [rolesManagementApp.id, getMockedApp()], - [apiKeysManagementApp.id, getMockedApp()], - [roleMappingsManagementApp.id, getMockedApp()], + [usersManagementApp.id, getMockedApp(usersManagementApp.id)], + [rolesManagementApp.id, getMockedApp(rolesManagementApp.id)], + [apiKeysManagementApp.id, getMockedApp(apiKeysManagementApp.id)], + [roleMappingsManagementApp.id, getMockedApp(roleMappingsManagementApp.id)], ] as Array<[string, jest.Mocked]>); - service.start(); + service.start({ + capabilities: { + management: { + security: { + users: canManageSecurity, + roles: canManageSecurity, + role_mappings: canManageSecurity, + api_keys: canManageSecurity, + }, + }, + navLinks: {}, + catalogue: {}, + }, + }); return { mockApps, @@ -178,6 +195,19 @@ describe('ManagementService', () => { } }); + it('apps are disabled if capabilities are false', () => { + const { mockApps } = startService( + { + showLinks: true, + showRoleMappingsManagement: true, + }, + false + ); + for (const [, mockApp] of mockApps) { + expect(mockApp.enabled).toBe(false); + } + }); + it('role mappings app is disabled if `showRoleMappingsManagement` changes after `start`', () => { const { mockApps, updateFeatures } = startService({ showLinks: true, diff --git a/x-pack/plugins/security/public/management/management_service.ts b/x-pack/plugins/security/public/management/management_service.ts index 199fd917da07..1fc648c12f80 100644 --- a/x-pack/plugins/security/public/management/management_service.ts +++ b/x-pack/plugins/security/public/management/management_service.ts @@ -5,7 +5,7 @@ */ import { Subscription } from 'rxjs'; -import { StartServicesAccessor, FatalErrorsSetup } from 'src/core/public'; +import { StartServicesAccessor, FatalErrorsSetup, Capabilities } from 'src/core/public'; import { ManagementApp, ManagementSetup, @@ -27,6 +27,10 @@ interface SetupParams { getStartServices: StartServicesAccessor; } +interface StartParams { + capabilities: Capabilities; +} + export class ManagementService { private license!: SecurityLicense; private licenseFeaturesSubscription?: Subscription; @@ -44,7 +48,7 @@ export class ManagementService { this.securitySection.registerApp(roleMappingsManagementApp.create({ getStartServices })); } - start() { + start({ capabilities }: StartParams) { this.licenseFeaturesSubscription = this.license.features$.subscribe(async (features) => { const securitySection = this.securitySection!; @@ -61,6 +65,11 @@ export class ManagementService { // Iterate over all registered apps and update their enable status depending on the available // license features. for (const [app, enableStatus] of securityManagementAppsStatuses) { + if (capabilities.management.security[app.id] !== true) { + app.disable(); + continue; + } + if (app.enabled === enableStatus) { continue; } diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts index 08561234fd70..2b78355787ff 100644 --- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts +++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts @@ -4,17 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureConfig } from '../../../../../features/public'; +import { KibanaFeature, KibanaFeatureConfig } from '../../../../../features/public'; export const createFeature = ( - config: Pick & { + config: Pick< + KibanaFeatureConfig, + 'id' | 'name' | 'subFeatures' | 'reserved' | 'privilegesTooltip' + > & { excludeFromBaseAll?: boolean; excludeFromBaseRead?: boolean; - privileges?: FeatureConfig['privileges']; + privileges?: KibanaFeatureConfig['privileges']; } ) => { const { excludeFromBaseAll, excludeFromBaseRead, privileges, ...rest } = config; - return new Feature({ + return new KibanaFeature({ icon: 'discoverApp', navLinkId: 'discover', app: [], diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts index 6821c163d817..02a18039cee7 100644 --- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts +++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_privileges.ts @@ -7,7 +7,7 @@ import { Actions } from '../../../../server/authorization'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { privilegesFactory } from '../../../../server/authorization/privileges'; -import { Feature } from '../../../../../features/public'; +import { KibanaFeature } from '../../../../../features/public'; import { KibanaPrivileges } from '../model'; import { SecurityLicenseFeatures } from '../../..'; @@ -15,11 +15,11 @@ import { SecurityLicenseFeatures } from '../../..'; import { featuresPluginMock } from '../../../../../features/server/mocks'; export const createRawKibanaPrivileges = ( - features: Feature[], + features: KibanaFeature[], { allowSubFeaturePrivileges = true } = {} ) => { const featuresService = featuresPluginMock.createSetup(); - featuresService.getFeatures.mockReturnValue(features); + featuresService.getKibanaFeatures.mockReturnValue(features); const licensingService = { getFeatures: () => ({ allowSubFeaturePrivileges } as SecurityLicenseFeatures), @@ -33,7 +33,7 @@ export const createRawKibanaPrivileges = ( }; export const createKibanaPrivileges = ( - features: Feature[], + features: KibanaFeature[], { allowSubFeaturePrivileges = true } = {} ) => { return new KibanaPrivileges( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index f6fe2f394fd3..bf791b37087b 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { act } from '@testing-library/react'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { Capabilities } from 'src/core/public'; -import { Feature } from '../../../../../features/public'; +import { KibanaFeature } from '../../../../../features/public'; import { Role } from '../../../../common/model'; import { DocumentationLinksService } from '../documentation_links'; import { EditRolePage } from './edit_role_page'; @@ -27,7 +27,7 @@ import { createRawKibanaPrivileges } from '../__fixtures__/kibana_privileges'; const buildFeatures = () => { return [ - new Feature({ + new KibanaFeature({ id: 'feature1', name: 'Feature 1', icon: 'addDataApp', @@ -51,7 +51,7 @@ const buildFeatures = () => { }, }, }), - new Feature({ + new KibanaFeature({ id: 'feature2', name: 'Feature 2', icon: 'addDataApp', @@ -75,7 +75,7 @@ const buildFeatures = () => { }, }, }), - ] as Feature[]; + ] as KibanaFeature[]; }; const buildBuiltinESPrivileges = () => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 15888733ec42..01f8969e61f4 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -40,7 +40,7 @@ import { } from 'src/core/public'; import { ScopedHistory } from 'kibana/public'; import { FeaturesPluginStart } from '../../../../../features/public'; -import { Feature } from '../../../../../features/common'; +import { KibanaFeature } from '../../../../../features/common'; import { IndexPatternsContract } from '../../../../../../../src/plugins/data/public'; import { Space } from '../../../../../spaces/public'; import { @@ -247,7 +247,7 @@ function useFeatures( getFeatures: FeaturesPluginStart['getFeatures'], fatalErrors: FatalErrorsSetup ) { - const [features, setFeatures] = useState(null); + const [features, setFeatures] = useState(null); useEffect(() => { getFeatures() .catch((err: IHttpFetchError) => { @@ -260,7 +260,7 @@ function useFeatures( // 404 here, and respond in a way that still allows the UI to render itself. const unauthorizedForFeatures = err.response?.status === 404; if (unauthorizedForFeatures) { - return [] as Feature[]; + return [] as KibanaFeature[]; } fatalErrors.add(err); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap index 1c020685c246..a2e46af19bf3 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap @@ -184,6 +184,7 @@ exports[`it renders without crashing 1`] = ` }, "getFeatures": [MockFunction], "isEnabled": [MockFunction], + "isLicenseAvailable": [MockFunction], } } onChange={[MockFunction]} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx index 2a0922d614f1..02d692bf9f50 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { FeatureTable } from './feature_table'; import { Role } from '../../../../../../../common/model'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { Feature, SubFeatureConfig } from '../../../../../../../../features/public'; +import { KibanaFeature, SubFeatureConfig } from '../../../../../../../../features/public'; import { kibanaFeatures, createFeature } from '../../../../__fixtures__/kibana_features'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; @@ -24,7 +24,7 @@ const createRole = (kibana: Role['kibana'] = []): Role => { }; interface TestConfig { - features: Feature[]; + features: KibanaFeature[]; role: Role; privilegeIndex: number; calculateDisplayedPrivileges: boolean; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx index 5530d9964f8c..bc6061334591 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.test.tsx @@ -13,7 +13,7 @@ import { PrivilegeDisplay } from './privilege_display'; import { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileges'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; -import { Feature } from '../../../../../../../../features/public'; +import { KibanaFeature } from '../../../../../../../../features/public'; import { findTestSubject } from 'test_utils/find_test_subject'; interface TableRow { @@ -24,7 +24,7 @@ interface TableRow { } const features = [ - new Feature({ + new KibanaFeature({ id: 'normal', name: 'normal feature', app: [], @@ -39,7 +39,7 @@ const features = [ }, }, }), - new Feature({ + new KibanaFeature({ id: 'normal_with_sub', name: 'normal feature with sub features', app: [], @@ -92,7 +92,7 @@ const features = [ }, ], }), - new Feature({ + new KibanaFeature({ id: 'bothPrivilegesExcludedFromBase', name: 'bothPrivilegesExcludedFromBase', app: [], @@ -109,7 +109,7 @@ const features = [ }, }, }), - new Feature({ + new KibanaFeature({ id: 'allPrivilegeExcludedFromBase', name: 'allPrivilegeExcludedFromBase', app: [], diff --git a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts index fd93aaa23194..4739346b2cb7 100644 --- a/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts +++ b/x-pack/plugins/security/public/management/roles/model/kibana_privileges.ts @@ -8,7 +8,7 @@ import { RawKibanaPrivileges, RoleKibanaPrivilege } from '../../../../common/mod import { KibanaPrivilege } from './kibana_privilege'; import { PrivilegeCollection } from './privilege_collection'; import { SecuredFeature } from './secured_feature'; -import { Feature } from '../../../../../features/common'; +import { KibanaFeature } from '../../../../../features/common'; import { isGlobalPrivilegeDefinition } from '../edit_role/privilege_utils'; function toBasePrivilege(entry: [string, string[]]): [string, KibanaPrivilege] { @@ -29,7 +29,7 @@ export class KibanaPrivileges { private feature: ReadonlyMap; - constructor(rawKibanaPrivileges: RawKibanaPrivileges, features: Feature[]) { + constructor(rawKibanaPrivileges: RawKibanaPrivileges, features: KibanaFeature[]) { this.global = recordsToBasePrivilegeMap(rawKibanaPrivileges.global); this.spaces = recordsToBasePrivilegeMap(rawKibanaPrivileges.space); this.feature = new Map( diff --git a/x-pack/plugins/security/public/management/roles/model/secured_feature.ts b/x-pack/plugins/security/public/management/roles/model/secured_feature.ts index 284a85583c33..894e06b6e585 100644 --- a/x-pack/plugins/security/public/management/roles/model/secured_feature.ts +++ b/x-pack/plugins/security/public/management/roles/model/secured_feature.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureConfig } from '../../../../../features/common'; +import { KibanaFeature, KibanaFeatureConfig } from '../../../../../features/common'; import { PrimaryFeaturePrivilege } from './primary_feature_privilege'; import { SecuredSubFeature } from './secured_sub_feature'; import { SubFeaturePrivilege } from './sub_feature_privilege'; -export class SecuredFeature extends Feature { +export class SecuredFeature extends KibanaFeature { private readonly primaryFeaturePrivileges: PrimaryFeaturePrivilege[]; private readonly minimalPrimaryFeaturePrivileges: PrimaryFeaturePrivilege[]; @@ -18,7 +18,10 @@ export class SecuredFeature extends Feature { private readonly securedSubFeatures: SecuredSubFeature[]; - constructor(config: FeatureConfig, actionMapping: { [privilegeId: string]: string[] } = {}) { + constructor( + config: KibanaFeatureConfig, + actionMapping: { [privilegeId: string]: string[] } = {} + ) { super(config); this.primaryFeaturePrivileges = Object.entries(this.config.privileges || {}).map( ([id, privilege]) => new PrimaryFeaturePrivilege(id, privilege, actionMapping[id]) diff --git a/x-pack/plugins/security/public/plugin.test.tsx b/x-pack/plugins/security/public/plugin.test.tsx index 8cec4fbc2f5a..fb8034da1173 100644 --- a/x-pack/plugins/security/public/plugin.test.tsx +++ b/x-pack/plugins/security/public/plugin.test.tsx @@ -41,6 +41,7 @@ describe('Security Plugin', () => { __legacyCompat: { logoutUrl: '/some-base-path/logout', tenant: '/some-base-path' }, authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) }, license: { + isLicenseAvailable: expect.any(Function), isEnabled: expect.any(Function), getFeatures: expect.any(Function), features$: expect.any(Observable), @@ -67,6 +68,7 @@ describe('Security Plugin', () => { expect(setupManagementServiceMock).toHaveBeenCalledWith({ authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) }, license: { + isLicenseAvailable: expect.any(Function), isEnabled: expect.any(Function), getFeatures: expect.any(Function), features$: expect.any(Observable), @@ -112,7 +114,8 @@ describe('Security Plugin', () => { } ); - plugin.start(coreMock.createStart({ basePath: '/some-base-path' }), { + const coreStart = coreMock.createStart({ basePath: '/some-base-path' }); + plugin.start(coreStart, { data: {} as DataPublicPluginStart, features: {} as FeaturesPluginStart, management: managementStartMock, diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index e3905dc2acf4..f5770ae2bc35 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -141,7 +141,7 @@ export class SecurityPlugin this.sessionTimeout.start(); this.navControlService.start({ core }); if (management) { - this.managementService.start(); + this.managementService.start({ capabilities: core.application.capabilities }); } } diff --git a/x-pack/plugins/security/server/authorization/api_authorization.test.ts b/x-pack/plugins/security/server/authorization/api_authorization.test.ts index 75aa27c3c88c..d4ec9a0e0db5 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.test.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.test.ts @@ -94,7 +94,9 @@ describe('initAPIAuthorization', () => { expect(mockResponse.notFound).not.toHaveBeenCalled(); expect(mockPostAuthToolkit.next).toHaveBeenCalledTimes(1); - expect(mockCheckPrivileges).toHaveBeenCalledWith([mockAuthz.actions.api.get('foo')]); + expect(mockCheckPrivileges).toHaveBeenCalledWith({ + kibana: [mockAuthz.actions.api.get('foo')], + }); expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); @@ -129,7 +131,9 @@ describe('initAPIAuthorization', () => { expect(mockResponse.notFound).toHaveBeenCalledTimes(1); expect(mockPostAuthToolkit.next).not.toHaveBeenCalled(); - expect(mockCheckPrivileges).toHaveBeenCalledWith([mockAuthz.actions.api.get('foo')]); + expect(mockCheckPrivileges).toHaveBeenCalledWith({ + kibana: [mockAuthz.actions.api.get('foo')], + }); expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); }); diff --git a/x-pack/plugins/security/server/authorization/api_authorization.ts b/x-pack/plugins/security/server/authorization/api_authorization.ts index 0ffd3ba7ba82..9129330ec947 100644 --- a/x-pack/plugins/security/server/authorization/api_authorization.ts +++ b/x-pack/plugins/security/server/authorization/api_authorization.ts @@ -29,7 +29,7 @@ export function initAPIAuthorization( const apiActions = actionTags.map((tag) => actions.api.get(tag.substring(tagPrefix.length))); const checkPrivileges = checkPrivilegesDynamicallyWithRequest(request); - const checkPrivilegesResponse = await checkPrivileges(apiActions); + const checkPrivilegesResponse = await checkPrivileges({ kibana: apiActions }); // we've actually authorized the request if (checkPrivilegesResponse.hasAllRequested) { diff --git a/x-pack/plugins/security/server/authorization/app_authorization.test.ts b/x-pack/plugins/security/server/authorization/app_authorization.test.ts index 1dc072ab2e6e..f40d502a9cd7 100644 --- a/x-pack/plugins/security/server/authorization/app_authorization.test.ts +++ b/x-pack/plugins/security/server/authorization/app_authorization.test.ts @@ -18,7 +18,7 @@ import { authorizationMock } from './index.mock'; const createFeaturesSetupContractMock = (): FeaturesSetupContract => { const mock = featuresPluginMock.createSetup(); - mock.getFeatures.mockReturnValue([ + mock.getKibanaFeatures.mockReturnValue([ { id: 'foo', name: 'Foo', app: ['foo'], privileges: {} } as any, ]); return mock; @@ -132,7 +132,7 @@ describe('initAppAuthorization', () => { expect(mockResponse.notFound).not.toHaveBeenCalled(); expect(mockPostAuthToolkit.next).toHaveBeenCalledTimes(1); - expect(mockCheckPrivileges).toHaveBeenCalledWith(mockAuthz.actions.app.get('foo')); + expect(mockCheckPrivileges).toHaveBeenCalledWith({ kibana: mockAuthz.actions.app.get('foo') }); expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); @@ -172,7 +172,7 @@ describe('initAppAuthorization', () => { expect(mockResponse.notFound).toHaveBeenCalledTimes(1); expect(mockPostAuthToolkit.next).not.toHaveBeenCalled(); - expect(mockCheckPrivileges).toHaveBeenCalledWith(mockAuthz.actions.app.get('foo')); + expect(mockCheckPrivileges).toHaveBeenCalledWith({ kibana: mockAuthz.actions.app.get('foo') }); expect(mockAuthz.mode.useRbacForRequest).toHaveBeenCalledWith(mockRequest); }); }); diff --git a/x-pack/plugins/security/server/authorization/app_authorization.ts b/x-pack/plugins/security/server/authorization/app_authorization.ts index 1036997ca821..4170fd2cdb38 100644 --- a/x-pack/plugins/security/server/authorization/app_authorization.ts +++ b/x-pack/plugins/security/server/authorization/app_authorization.ts @@ -19,7 +19,7 @@ class ProtectedApplications { if (this.applications == null) { this.applications = new Set( this.featuresService - .getFeatures() + .getKibanaFeatures() .map((feature) => feature.app) .flat() ); @@ -63,7 +63,7 @@ export function initAppAuthorization( const checkPrivileges = checkPrivilegesDynamicallyWithRequest(request); const appAction = actions.app.get(appId); - const checkPrivilegesResponse = await checkPrivileges(appAction); + const checkPrivilegesResponse = await checkPrivileges({ kibana: appAction }); logger.debug(`authorizing access to "${appId}"`); // we've actually authorized the request diff --git a/x-pack/plugins/security/server/authorization/authorization_service.test.ts b/x-pack/plugins/security/server/authorization/authorization_service.test.ts index 2fdc2d169e97..c00127f7d122 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.test.ts +++ b/x-pack/plugins/security/server/authorization/authorization_service.test.ts @@ -74,6 +74,7 @@ it(`#setup returns exposed services`, () => { packageVersion: 'some-version', features: mockFeaturesSetup, getSpacesService: mockGetSpacesService, + getCurrentUser: jest.fn(), }); expect(authz.actions.version).toBe('version:some-version'); @@ -133,10 +134,11 @@ describe('#start', () => { getSpacesService: jest .fn() .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), + getCurrentUser: jest.fn(), }); const featuresStart = featuresPluginMock.createStart(); - featuresStart.getFeatures.mockReturnValue([]); + featuresStart.getKibanaFeatures.mockReturnValue([]); authorizationService.start({ clusterClient: mockClusterClient, @@ -203,10 +205,12 @@ it('#stop unsubscribes from license and ES updates.', async () => { getSpacesService: jest .fn() .mockReturnValue({ getSpaceId: jest.fn(), namespaceToSpaceId: jest.fn() }), + getCurrentUser: jest.fn(), }); const featuresStart = featuresPluginMock.createStart(); - featuresStart.getFeatures.mockReturnValue([]); + featuresStart.getKibanaFeatures.mockReturnValue([]); + authorizationService.start({ clusterClient: mockClusterClient, features: featuresStart, diff --git a/x-pack/plugins/security/server/authorization/authorization_service.ts b/x-pack/plugins/security/server/authorization/authorization_service.ts index 2dead301b298..fd3a60fb4d90 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.ts +++ b/x-pack/plugins/security/server/authorization/authorization_service.ts @@ -22,7 +22,7 @@ import { import { SpacesService } from '../plugin'; import { Actions } from './actions'; -import { CheckPrivilegesWithRequest, checkPrivilegesWithRequestFactory } from './check_privileges'; +import { checkPrivilegesWithRequestFactory } from './check_privileges'; import { CheckPrivilegesDynamicallyWithRequest, checkPrivilegesDynamicallyWithRequestFactory, @@ -41,7 +41,9 @@ import { validateReservedPrivileges } from './validate_reserved_privileges'; import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; import { APPLICATION_PREFIX } from '../../common/constants'; import { SecurityLicense } from '../../common/licensing'; +import { CheckPrivilegesWithRequest } from './types'; import { OnlineStatusRetryScheduler } from '../elasticsearch'; +import { AuthenticatedUser } from '..'; export { Actions } from './actions'; export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; @@ -57,6 +59,7 @@ interface AuthorizationServiceSetupParams { features: FeaturesPluginSetup; kibanaIndexName: string; getSpacesService(): SpacesService | undefined; + getCurrentUser(request: KibanaRequest): AuthenticatedUser | null; } interface AuthorizationServiceStartParams { @@ -92,6 +95,7 @@ export class AuthorizationService { features, kibanaIndexName, getSpacesService, + getCurrentUser, }: AuthorizationServiceSetupParams): AuthorizationServiceSetup { this.logger = loggers.get('authorization'); this.applicationName = `${APPLICATION_PREFIX}${kibanaIndexName}`; @@ -132,9 +136,11 @@ export class AuthorizationService { const disableUICapabilities = disableUICapabilitiesFactory( request, - features.getFeatures(), + features.getKibanaFeatures(), + features.getElasticsearchFeatures(), this.logger, - authz + authz, + getCurrentUser(request) ); if (!request.auth.isAuthenticated) { @@ -152,7 +158,7 @@ export class AuthorizationService { } start({ clusterClient, features, online$ }: AuthorizationServiceStartParams) { - const allFeatures = features.getFeatures(); + const allFeatures = features.getKibanaFeatures(); validateFeaturePrivileges(allFeatures); validateReservedPrivileges(allFeatures); diff --git a/x-pack/plugins/security/server/authorization/check_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_privileges.test.ts index b380f45a12d8..4151ff645005 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.test.ts @@ -33,7 +33,11 @@ const createMockClusterClient = (response: any) => { describe('#atSpace', () => { const checkPrivilegesAtSpaceTest = async (options: { spaceId: string; - privilegeOrPrivileges: string | string[]; + kibanaPrivileges?: string | string[]; + elasticsearchPrivileges?: { + cluster: string[]; + index: Record; + }; esHasPrivilegesResponse: HasPrivilegesResponse; }) => { const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient( @@ -50,25 +54,39 @@ describe('#atSpace', () => { let actualResult; let errorThrown = null; try { - actualResult = await checkPrivileges.atSpace(options.spaceId, options.privilegeOrPrivileges); + actualResult = await checkPrivileges.atSpace(options.spaceId, { + kibana: options.kibanaPrivileges, + elasticsearch: options.elasticsearchPrivileges, + }); } catch (err) { errorThrown = err; } + const expectedIndexPrivilegePayload = Object.entries( + options.elasticsearchPrivileges?.index ?? {} + ).map(([names, indexPrivileges]) => ({ + names, + privileges: indexPrivileges, + })); + expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request); expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.hasPrivileges', { body: { + cluster: options.elasticsearchPrivileges?.cluster, + index: expectedIndexPrivilegePayload, applications: [ { application, resources: [`space:${options.spaceId}`], - privileges: uniq([ - mockActions.version, - mockActions.login, - ...(Array.isArray(options.privilegeOrPrivileges) - ? options.privilegeOrPrivileges - : [options.privilegeOrPrivileges]), - ]), + privileges: options.kibanaPrivileges + ? uniq([ + mockActions.version, + mockActions.login, + ...(Array.isArray(options.kibanaPrivileges) + ? options.kibanaPrivileges + : [options.kibanaPrivileges]), + ]) + : [mockActions.version, mockActions.login], }, ], }, @@ -83,7 +101,7 @@ describe('#atSpace', () => { test('successful when checking for login and user has login', async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: true, username: 'foo-username', @@ -100,13 +118,19 @@ describe('#atSpace', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "mock-action:login", - "resource": "space_1", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "mock-action:login", + "resource": "space_1", + }, + ], + }, "username": "foo-username", } `); @@ -115,7 +139,7 @@ describe('#atSpace', () => { test(`failure when checking for login and user doesn't have login`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -132,13 +156,19 @@ describe('#atSpace', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": false, - "privilege": "mock-action:login", - "resource": "space_1", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": false, + "privilege": "mock-action:login", + "resource": "space_1", + }, + ], + }, "username": "foo-username", } `); @@ -147,7 +177,7 @@ describe('#atSpace', () => { test(`throws error when checking for login and user has login but doesn't have version`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -169,7 +199,7 @@ describe('#atSpace', () => { test(`successful when checking for two actions and the user has both`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -191,18 +221,24 @@ describe('#atSpace', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", - }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, "username": "foo-username", } `); @@ -211,7 +247,7 @@ describe('#atSpace', () => { test(`failure when checking for two actions and the user has only one`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -233,18 +269,24 @@ describe('#atSpace', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": false, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, "username": "foo-username", } `); @@ -254,7 +296,7 @@ describe('#atSpace', () => { test(`throws a validation error when an extra privilege is present in the response`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -278,7 +320,7 @@ describe('#atSpace', () => { test(`throws a validation error when privileges are missing in the response`, async () => { const result = await checkPrivilegesAtSpaceTest({ spaceId: 'space_1', - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -297,12 +339,551 @@ describe('#atSpace', () => { ); }); }); + + describe('with both Kibana and Elasticsearch privileges', () => { + it('successful when checking for privileges, and user has all', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only es privileges', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only kibana privileges', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has none', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + ], + }, + "username": "foo-username", + } + `); + }); + }); + + describe('with Elasticsearch privileges', () => { + it('successful when checking for cluster privileges, and user has both', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for index privileges, and user has both', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: [], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for a combination of index and cluster privileges', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for a combination of index and cluster privileges, and some are missing', async () => { + const result = await checkPrivilegesAtSpaceTest({ + spaceId: 'space_1', + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: false, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": false, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + }); }); describe('#atSpaces', () => { const checkPrivilegesAtSpacesTest = async (options: { spaceIds: string[]; - privilegeOrPrivileges: string | string[]; + kibanaPrivileges?: string | string[]; + elasticsearchPrivileges?: { + cluster: string[]; + index: Record; + }; esHasPrivilegesResponse: HasPrivilegesResponse; }) => { const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient( @@ -319,28 +900,39 @@ describe('#atSpaces', () => { let actualResult; let errorThrown = null; try { - actualResult = await checkPrivileges.atSpaces( - options.spaceIds, - options.privilegeOrPrivileges - ); + actualResult = await checkPrivileges.atSpaces(options.spaceIds, { + kibana: options.kibanaPrivileges, + elasticsearch: options.elasticsearchPrivileges, + }); } catch (err) { errorThrown = err; } + const expectedIndexPrivilegePayload = Object.entries( + options.elasticsearchPrivileges?.index ?? {} + ).map(([names, indexPrivileges]) => ({ + names, + privileges: indexPrivileges, + })); + expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request); expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.hasPrivileges', { body: { + cluster: options.elasticsearchPrivileges?.cluster, + index: expectedIndexPrivilegePayload, applications: [ { application, resources: options.spaceIds.map((spaceId) => `space:${spaceId}`), - privileges: uniq([ - mockActions.version, - mockActions.login, - ...(Array.isArray(options.privilegeOrPrivileges) - ? options.privilegeOrPrivileges - : [options.privilegeOrPrivileges]), - ]), + privileges: options.kibanaPrivileges + ? uniq([ + mockActions.version, + mockActions.login, + ...(Array.isArray(options.kibanaPrivileges) + ? options.kibanaPrivileges + : [options.kibanaPrivileges]), + ]) + : [mockActions.version, mockActions.login], }, ], }, @@ -355,7 +947,7 @@ describe('#atSpaces', () => { test('successful when checking for login and user has login at both spaces', async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: true, username: 'foo-username', @@ -376,18 +968,24 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "mock-action:login", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "mock-action:login", - "resource": "space_2", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "mock-action:login", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "mock-action:login", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -396,7 +994,7 @@ describe('#atSpaces', () => { test('failure when checking for login and user has login at only one space', async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -417,18 +1015,24 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "mock-action:login", - "resource": "space_1", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - Object { - "authorized": false, - "privilege": "mock-action:login", - "resource": "space_2", - }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "mock-action:login", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "mock-action:login", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -437,7 +1041,7 @@ describe('#atSpaces', () => { test(`throws error when checking for login and user has login but doesn't have version`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -463,7 +1067,7 @@ describe('#atSpaces', () => { test(`throws error when Elasticsearch returns malformed response`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -492,7 +1096,7 @@ describe('#atSpaces', () => { test(`successful when checking for two actions at two spaces and user has it all`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -520,28 +1124,34 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_2", - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_2", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -550,7 +1160,7 @@ describe('#atSpaces', () => { test(`failure when checking for two actions at two spaces and user has one action at one space`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -578,28 +1188,34 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", - }, - Object { - "authorized": false, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", - }, - Object { - "authorized": false, - "privilege": "saved_object:foo-type/get", - "resource": "space_2", - }, - Object { - "authorized": false, - "privilege": "saved_object:bar-type/get", - "resource": "space_2", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -608,7 +1224,7 @@ describe('#atSpaces', () => { test(`failure when checking for two actions at two spaces and user has two actions at one space`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -636,28 +1252,34 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", - }, - Object { - "authorized": false, - "privilege": "saved_object:foo-type/get", - "resource": "space_2", - }, - Object { - "authorized": false, - "privilege": "saved_object:bar-type/get", - "resource": "space_2", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -666,7 +1288,7 @@ describe('#atSpaces', () => { test(`failure when checking for two actions at two spaces and user has two actions at one space & one action at the other`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -694,28 +1316,34 @@ describe('#atSpaces', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": "space_1", - }, - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": "space_2", - }, - Object { - "authorized": false, - "privilege": "saved_object:bar-type/get", - "resource": "space_2", + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, "username": "foo-username", } `); @@ -725,7 +1353,7 @@ describe('#atSpaces', () => { test(`throws a validation error when an extra privilege is present in the response`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -755,7 +1383,7 @@ describe('#atSpaces', () => { test(`throws a validation error when privileges are missing in the response`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -783,7 +1411,7 @@ describe('#atSpaces', () => { test(`throws a validation error when an extra space is present in the response`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -816,7 +1444,7 @@ describe('#atSpaces', () => { test(`throws a validation error when an a space is missing in the response`, async () => { const result = await checkPrivilegesAtSpacesTest({ spaceIds: ['space_1', 'space_2'], - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -836,13 +1464,632 @@ describe('#atSpaces', () => { ); }); }); -}); -describe('#globally', () => { - const checkPrivilegesGloballyTest = async (options: { - privilegeOrPrivileges: string | string[]; - esHasPrivilegesResponse: HasPrivilegesResponse; - }) => { + describe('with both Kibana and Elasticsearch privileges', () => { + it('successful when checking for privileges, and user has all', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only es privileges', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only kibana privileges', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has none', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_1", + }, + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": "space_2", + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": "space_2", + }, + ], + }, + "username": "foo-username", + } + `); + }); + }); + + describe('with Elasticsearch privileges', () => { + it('successful when checking for cluster privileges, and user has both', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for index privileges, and user has both', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: [], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for a combination of index and cluster privileges', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for a combination of index and cluster privileges, and some are missing', async () => { + const result = await checkPrivilegesAtSpacesTest({ + spaceIds: ['space_1', 'space_2'], + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + 'space:space_1': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + 'space:space_2': { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: false, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": false, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + }); +}); + +describe('#globally', () => { + const checkPrivilegesGloballyTest = async (options: { + kibanaPrivileges?: string | string[]; + elasticsearchPrivileges?: { + cluster: string[]; + index: Record; + }; + esHasPrivilegesResponse: HasPrivilegesResponse; + }) => { const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient( options.esHasPrivilegesResponse ); @@ -857,25 +2104,39 @@ describe('#globally', () => { let actualResult; let errorThrown = null; try { - actualResult = await checkPrivileges.globally(options.privilegeOrPrivileges); + actualResult = await checkPrivileges.globally({ + kibana: options.kibanaPrivileges, + elasticsearch: options.elasticsearchPrivileges, + }); } catch (err) { errorThrown = err; } + const expectedIndexPrivilegePayload = Object.entries( + options.elasticsearchPrivileges?.index ?? {} + ).map(([names, indexPrivileges]) => ({ + names, + privileges: indexPrivileges, + })); + expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request); expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.hasPrivileges', { body: { + cluster: options.elasticsearchPrivileges?.cluster, + index: expectedIndexPrivilegePayload, applications: [ { application, resources: [GLOBAL_RESOURCE], - privileges: uniq([ - mockActions.version, - mockActions.login, - ...(Array.isArray(options.privilegeOrPrivileges) - ? options.privilegeOrPrivileges - : [options.privilegeOrPrivileges]), - ]), + privileges: options.kibanaPrivileges + ? uniq([ + mockActions.version, + mockActions.login, + ...(Array.isArray(options.kibanaPrivileges) + ? options.kibanaPrivileges + : [options.kibanaPrivileges]), + ]) + : [mockActions.version, mockActions.login], }, ], }, @@ -889,7 +2150,7 @@ describe('#globally', () => { test('successful when checking for login and user has login', async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: true, username: 'foo-username', @@ -906,13 +2167,19 @@ describe('#globally', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "mock-action:login", - "resource": undefined, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "mock-action:login", + "resource": undefined, + }, + ], + }, "username": "foo-username", } `); @@ -920,7 +2187,7 @@ describe('#globally', () => { test(`failure when checking for login and user doesn't have login`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -937,13 +2204,19 @@ describe('#globally', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": false, - "privilege": "mock-action:login", - "resource": undefined, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": false, + "privilege": "mock-action:login", + "resource": undefined, + }, + ], + }, "username": "foo-username", } `); @@ -951,7 +2224,7 @@ describe('#globally', () => { test(`throws error when checking for login and user has login but doesn't have version`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: mockActions.login, + kibanaPrivileges: mockActions.login, esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -972,7 +2245,7 @@ describe('#globally', () => { test(`throws error when Elasticsearch returns malformed response`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -996,7 +2269,7 @@ describe('#globally', () => { test(`successful when checking for two actions and the user has both`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -1018,18 +2291,24 @@ describe('#globally', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": true, - "privileges": Array [ - Object { - "authorized": true, - "privilege": "saved_object:foo-type/get", - "resource": undefined, - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": undefined, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, "username": "foo-username", } `); @@ -1037,7 +2316,7 @@ describe('#globally', () => { test(`failure when checking for two actions and the user has only one`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: [ + kibanaPrivileges: [ `saved_object:${savedObjectTypes[0]}/get`, `saved_object:${savedObjectTypes[1]}/get`, ], @@ -1059,18 +2338,24 @@ describe('#globally', () => { expect(result).toMatchInlineSnapshot(` Object { "hasAllRequested": false, - "privileges": Array [ - Object { - "authorized": false, - "privilege": "saved_object:foo-type/get", - "resource": undefined, - }, - Object { - "authorized": true, - "privilege": "saved_object:bar-type/get", - "resource": undefined, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object {}, }, - ], + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, "username": "foo-username", } `); @@ -1079,7 +2364,7 @@ describe('#globally', () => { describe('with a malformed Elasticsearch response', () => { test(`throws a validation error when an extra privilege is present in the response`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -1102,7 +2387,7 @@ describe('#globally', () => { test(`throws a validation error when privileges are missing in the response`, async () => { const result = await checkPrivilegesGloballyTest({ - privilegeOrPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], + kibanaPrivileges: [`saved_object:${savedObjectTypes[0]}/get`], esHasPrivilegesResponse: { has_all_requested: false, username: 'foo-username', @@ -1121,4 +2406,531 @@ describe('#globally', () => { ); }); }); + + describe('with both Kibana and Elasticsearch privileges', () => { + it('successful when checking for privileges, and user has all', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only es privileges', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has only kibana privileges', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: true, + [`saved_object:${savedObjectTypes[1]}/get`]: true, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": true, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": true, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for privileges, and user has none', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + kibanaPrivileges: [ + `saved_object:${savedObjectTypes[0]}/get`, + `saved_object:${savedObjectTypes[1]}/get`, + ], + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + [`saved_object:${savedObjectTypes[0]}/get`]: false, + [`saved_object:${savedObjectTypes[1]}/get`]: false, + }, + }, + }, + cluster: { + foo: false, + bar: false, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": false, + "privilege": "foo", + }, + Object { + "authorized": false, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [ + Object { + "authorized": false, + "privilege": "saved_object:foo-type/get", + "resource": undefined, + }, + Object { + "authorized": false, + "privilege": "saved_object:bar-type/get", + "resource": undefined, + }, + ], + }, + "username": "foo-username", + } + `); + }); + }); + + describe('with Elasticsearch privileges', () => { + it('successful when checking for cluster privileges, and user has both', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['foo', 'bar'], + index: {}, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + foo: true, + bar: true, + }, + index: {}, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "foo", + }, + Object { + "authorized": true, + "privilege": "bar", + }, + ], + "index": Object {}, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for index privileges, and user has both', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: [], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('successful when checking for a combination of index and cluster privileges', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: true, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: true, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": true, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": true, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + + it('failure when checking for a combination of index and cluster privileges, and some are missing', async () => { + const result = await checkPrivilegesGloballyTest({ + elasticsearchPrivileges: { + cluster: ['manage', 'monitor'], + index: { + foo: ['all'], + bar: ['read', 'view_index_metadata'], + }, + }, + esHasPrivilegesResponse: { + has_all_requested: false, + username: 'foo-username', + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.login]: true, + [mockActions.version]: true, + }, + }, + }, + cluster: { + manage: true, + monitor: true, + }, + index: { + foo: { + all: true, + }, + bar: { + read: true, + view_index_metadata: false, + }, + }, + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "hasAllRequested": false, + "privileges": Object { + "elasticsearch": Object { + "cluster": Array [ + Object { + "authorized": true, + "privilege": "manage", + }, + Object { + "authorized": true, + "privilege": "monitor", + }, + ], + "index": Object { + "bar": Array [ + Object { + "authorized": true, + "privilege": "read", + }, + Object { + "authorized": false, + "privilege": "view_index_metadata", + }, + ], + "foo": Array [ + Object { + "authorized": true, + "privilege": "all", + }, + ], + }, + }, + "kibana": Array [], + }, + "username": "foo-username", + } + `); + }); + }); }); diff --git a/x-pack/plugins/security/server/authorization/check_privileges.ts b/x-pack/plugins/security/server/authorization/check_privileges.ts index 3129777a7881..27e1802b4e5c 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.ts @@ -8,7 +8,13 @@ import { pick, transform, uniq } from 'lodash'; import { ILegacyClusterClient, KibanaRequest } from '../../../../../src/core/server'; import { GLOBAL_RESOURCE } from '../../common/constants'; import { ResourceSerializer } from './resource_serializer'; -import { HasPrivilegesResponse, HasPrivilegesResponseApplication } from './types'; +import { + HasPrivilegesResponse, + HasPrivilegesResponseApplication, + CheckPrivilegesPayload, + CheckPrivileges, + CheckPrivilegesResponse, +} from './types'; import { validateEsPrivilegeResponse } from './validate_es_response'; interface CheckPrivilegesActions { @@ -16,33 +22,6 @@ interface CheckPrivilegesActions { version: string; } -export interface CheckPrivilegesResponse { - hasAllRequested: boolean; - username: string; - privileges: Array<{ - /** - * If this attribute is undefined, this element is a privilege for the global resource. - */ - resource?: string; - privilege: string; - authorized: boolean; - }>; -} - -export type CheckPrivilegesWithRequest = (request: KibanaRequest) => CheckPrivileges; - -export interface CheckPrivileges { - atSpace( - spaceId: string, - privilegeOrPrivileges: string | string[] - ): Promise; - atSpaces( - spaceIds: string[], - privilegeOrPrivileges: string | string[] - ): Promise; - globally(privilegeOrPrivileges: string | string[]): Promise; -} - export function checkPrivilegesWithRequestFactory( actions: CheckPrivilegesActions, clusterClient: ILegacyClusterClient, @@ -59,17 +38,26 @@ export function checkPrivilegesWithRequestFactory( return function checkPrivilegesWithRequest(request: KibanaRequest): CheckPrivileges { const checkPrivilegesAtResources = async ( resources: string[], - privilegeOrPrivileges: string | string[] + privileges: CheckPrivilegesPayload ): Promise => { - const privileges = Array.isArray(privilegeOrPrivileges) - ? privilegeOrPrivileges - : [privilegeOrPrivileges]; - const allApplicationPrivileges = uniq([actions.version, actions.login, ...privileges]); + const kibanaPrivileges = Array.isArray(privileges.kibana) + ? privileges.kibana + : privileges.kibana + ? [privileges.kibana] + : []; + const allApplicationPrivileges = uniq([actions.version, actions.login, ...kibanaPrivileges]); const hasPrivilegesResponse = (await clusterClient .asScoped(request) .callAsCurrentUser('shield.hasPrivileges', { body: { + cluster: privileges.elasticsearch?.cluster, + index: Object.entries(privileges.elasticsearch?.index ?? {}).map( + ([names, indexPrivileges]) => ({ + names, + privileges: indexPrivileges, + }) + ), applications: [ { application: applicationName, resources, privileges: allApplicationPrivileges }, ], @@ -85,6 +73,27 @@ export function checkPrivilegesWithRequestFactory( const applicationPrivilegesResponse = hasPrivilegesResponse.application[applicationName]; + const clusterPrivilegesResponse = hasPrivilegesResponse.cluster ?? {}; + + const clusterPrivileges = Object.entries(clusterPrivilegesResponse).map( + ([privilege, authorized]) => ({ + privilege, + authorized, + }) + ); + + const indexPrivileges = Object.entries(hasPrivilegesResponse.index ?? {}).reduce< + CheckPrivilegesResponse['privileges']['elasticsearch']['index'] + >((acc, [index, indexResponse]) => { + return { + ...acc, + [index]: Object.entries(indexResponse).map(([privilege, authorized]) => ({ + privilege, + authorized, + })), + }; + }, {}); + if (hasIncompatibleVersion(applicationPrivilegesResponse)) { throw new Error( 'Multiple versions of Kibana are running against the same Elasticsearch cluster, unable to authorize user.' @@ -93,7 +102,7 @@ export function checkPrivilegesWithRequestFactory( // we need to filter out the non requested privileges from the response const resourcePrivileges = transform(applicationPrivilegesResponse, (result, value, key) => { - result[key!] = pick(value, privileges); + result[key!] = pick(value, privileges.kibana ?? []); }) as HasPrivilegesResponseApplication; const privilegeArray = Object.entries(resourcePrivileges) .map(([key, val]) => { @@ -111,23 +120,29 @@ export function checkPrivilegesWithRequestFactory( return { hasAllRequested: hasPrivilegesResponse.has_all_requested, username: hasPrivilegesResponse.username, - privileges: privilegeArray, + privileges: { + kibana: privilegeArray, + elasticsearch: { + cluster: clusterPrivileges, + index: indexPrivileges, + }, + }, }; }; return { - async atSpace(spaceId: string, privilegeOrPrivileges: string | string[]) { + async atSpace(spaceId: string, privileges: CheckPrivilegesPayload) { const spaceResource = ResourceSerializer.serializeSpaceResource(spaceId); - return await checkPrivilegesAtResources([spaceResource], privilegeOrPrivileges); + return await checkPrivilegesAtResources([spaceResource], privileges); }, - async atSpaces(spaceIds: string[], privilegeOrPrivileges: string | string[]) { + async atSpaces(spaceIds: string[], privileges: CheckPrivilegesPayload) { const spaceResources = spaceIds.map((spaceId) => ResourceSerializer.serializeSpaceResource(spaceId) ); - return await checkPrivilegesAtResources(spaceResources, privilegeOrPrivileges); + return await checkPrivilegesAtResources(spaceResources, privileges); }, - async globally(privilegeOrPrivileges: string | string[]) { - return await checkPrivilegesAtResources([GLOBAL_RESOURCE], privilegeOrPrivileges); + async globally(privileges: CheckPrivilegesPayload) { + return await checkPrivilegesAtResources([GLOBAL_RESOURCE], privileges); }, }; }; diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts index 220674859763..093b308f5939 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts @@ -24,11 +24,13 @@ test(`checkPrivileges.atSpace when spaces is enabled`, async () => { namespaceToSpaceId: jest.fn(), }) )(request); - const result = await checkPrivilegesDynamically(privilegeOrPrivileges); + const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }); expect(result).toBe(expectedResult); expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, privilegeOrPrivileges); + expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, { + kibana: privilegeOrPrivileges, + }); }); test(`checkPrivileges.globally when spaces is disabled`, async () => { @@ -43,9 +45,9 @@ test(`checkPrivileges.globally when spaces is disabled`, async () => { mockCheckPrivilegesWithRequest, () => undefined )(request); - const result = await checkPrivilegesDynamically(privilegeOrPrivileges); + const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }); expect(result).toBe(expectedResult); expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivileges.globally).toHaveBeenCalledWith(privilegeOrPrivileges); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: privilegeOrPrivileges }); }); diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts index 6014bad739e7..cd5961e5940e 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts @@ -6,10 +6,11 @@ import { KibanaRequest } from '../../../../../src/core/server'; import { SpacesService } from '../plugin'; -import { CheckPrivilegesResponse, CheckPrivilegesWithRequest } from './check_privileges'; +import { CheckPrivilegesResponse, CheckPrivilegesWithRequest } from './types'; +import { CheckPrivilegesPayload } from './types'; export type CheckPrivilegesDynamically = ( - privilegeOrPrivileges: string | string[] + privileges: CheckPrivilegesPayload ) => Promise; export type CheckPrivilegesDynamicallyWithRequest = ( @@ -22,11 +23,11 @@ export function checkPrivilegesDynamicallyWithRequestFactory( ): CheckPrivilegesDynamicallyWithRequest { return function checkPrivilegesDynamicallyWithRequest(request: KibanaRequest) { const checkPrivileges = checkPrivilegesWithRequest(request); - return async function checkPrivilegesDynamically(privilegeOrPrivileges: string | string[]) { + return async function checkPrivilegesDynamically(privileges: CheckPrivilegesPayload) { const spacesService = getSpacesService(); return spacesService - ? await checkPrivileges.atSpace(spacesService.getSpaceId(request), privilegeOrPrivileges) - : await checkPrivileges.globally(privilegeOrPrivileges); + ? await checkPrivileges.atSpace(spacesService.getSpaceId(request), privileges) + : await checkPrivileges.globally(privileges); }; }; } diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts index 5e38045b88c7..f287cc04280a 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts @@ -7,7 +7,7 @@ import { checkSavedObjectsPrivilegesWithRequestFactory } from './check_saved_objects_privileges'; import { httpServerMock } from '../../../../../src/core/server/mocks'; -import { CheckPrivileges, CheckPrivilegesWithRequest } from './check_privileges'; +import { CheckPrivileges, CheckPrivilegesWithRequest } from './types'; import { SpacesService } from '../plugin'; let mockCheckPrivileges: jest.Mocked; @@ -31,7 +31,9 @@ beforeEach(() => { mockSpacesService = { getSpaceId: jest.fn(), - namespaceToSpaceId: jest.fn().mockImplementation((namespace: string) => `${namespace}-id`), + namespaceToSpaceId: jest + .fn() + .mockImplementation((namespace: string = 'default') => `${namespace}-id`), }; }); @@ -41,8 +43,6 @@ describe('#checkSavedObjectsPrivileges', () => { const namespace2 = 'qux'; describe('when checking multiple namespaces', () => { - const namespaces = [namespace1, namespace2]; - test(`throws an error when using an empty namespaces array`, async () => { const checkSavedObjectsPrivileges = createFactory(); @@ -58,6 +58,7 @@ describe('#checkSavedObjectsPrivileges', () => { mockCheckPrivileges.atSpaces.mockReturnValue(expectedResult as any); const checkSavedObjectsPrivileges = createFactory(); + const namespaces = [namespace1, namespace2]; const result = await checkSavedObjectsPrivileges(actions, namespaces); expect(result).toBe(expectedResult); @@ -68,7 +69,31 @@ describe('#checkSavedObjectsPrivileges', () => { expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledTimes(1); const spaceIds = mockSpacesService!.namespaceToSpaceId.mock.results.map((x) => x.value); - expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledWith(spaceIds, actions); + expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledWith(spaceIds, { kibana: actions }); + }); + + test(`de-duplicates namespaces`, async () => { + const expectedResult = Symbol(); + mockCheckPrivileges.atSpaces.mockReturnValue(expectedResult as any); + const checkSavedObjectsPrivileges = createFactory(); + + const namespaces = [undefined, 'default', namespace1, namespace1]; + const result = await checkSavedObjectsPrivileges(actions, namespaces); + + expect(result).toBe(expectedResult); + expect(mockSpacesService!.namespaceToSpaceId).toHaveBeenCalledTimes(4); + expect(mockSpacesService!.namespaceToSpaceId).toHaveBeenNthCalledWith(1, undefined); + expect(mockSpacesService!.namespaceToSpaceId).toHaveBeenNthCalledWith(2, 'default'); + expect(mockSpacesService!.namespaceToSpaceId).toHaveBeenNthCalledWith(3, namespace1); + expect(mockSpacesService!.namespaceToSpaceId).toHaveBeenNthCalledWith(4, namespace1); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); + expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledTimes(1); + const spaceIds = [ + mockSpacesService!.namespaceToSpaceId(undefined), // deduplicated with 'default' + mockSpacesService!.namespaceToSpaceId(namespace1), // deduplicated with namespace1 + ]; + expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledWith(spaceIds, { kibana: actions }); }); }); @@ -87,7 +112,7 @@ describe('#checkSavedObjectsPrivileges', () => { expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); expect(mockCheckPrivileges.atSpace).toHaveBeenCalledTimes(1); const spaceId = mockSpacesService!.namespaceToSpaceId.mock.results[0].value; - expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, actions); + expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, { kibana: actions }); }); test(`uses checkPrivileges.globally when Spaces is disabled`, async () => { @@ -102,7 +127,7 @@ describe('#checkSavedObjectsPrivileges', () => { expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledTimes(1); expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); expect(mockCheckPrivileges.globally).toHaveBeenCalledTimes(1); - expect(mockCheckPrivileges.globally).toHaveBeenCalledWith(actions); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: actions }); }); }); }); diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts index 0c2260542bf7..7c0ca7dcaa39 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts @@ -6,7 +6,7 @@ import { KibanaRequest } from '../../../../../src/core/server'; import { SpacesService } from '../plugin'; -import { CheckPrivilegesWithRequest, CheckPrivilegesResponse } from './check_privileges'; +import { CheckPrivilegesWithRequest, CheckPrivilegesResponse } from './types'; export type CheckSavedObjectsPrivilegesWithRequest = ( request: KibanaRequest @@ -14,9 +14,13 @@ export type CheckSavedObjectsPrivilegesWithRequest = ( export type CheckSavedObjectsPrivileges = ( actions: string | string[], - namespaceOrNamespaces?: string | string[] + namespaceOrNamespaces?: string | Array ) => Promise; +function uniq(arr: T[]): T[] { + return Array.from(new Set(arr)); +} + export const checkSavedObjectsPrivilegesWithRequestFactory = ( checkPrivilegesWithRequest: CheckPrivilegesWithRequest, getSpacesService: () => SpacesService | undefined @@ -26,23 +30,26 @@ export const checkSavedObjectsPrivilegesWithRequestFactory = ( ): CheckSavedObjectsPrivileges { return async function checkSavedObjectsPrivileges( actions: string | string[], - namespaceOrNamespaces?: string | string[] + namespaceOrNamespaces?: string | Array ) { const spacesService = getSpacesService(); if (!spacesService) { // Spaces disabled, authorizing globally - return await checkPrivilegesWithRequest(request).globally(actions); + return await checkPrivilegesWithRequest(request).globally({ kibana: actions }); } else if (Array.isArray(namespaceOrNamespaces)) { // Spaces enabled, authorizing against multiple spaces if (!namespaceOrNamespaces.length) { throw new Error(`Can't check saved object privileges for 0 namespaces`); } - const spaceIds = namespaceOrNamespaces.map((x) => spacesService.namespaceToSpaceId(x)); - return await checkPrivilegesWithRequest(request).atSpaces(spaceIds, actions); + const spaceIds = uniq( + namespaceOrNamespaces.map((x) => spacesService.namespaceToSpaceId(x)) + ); + + return await checkPrivilegesWithRequest(request).atSpaces(spaceIds, { kibana: actions }); } else { // Spaces enabled, authorizing against a single space const spaceId = spacesService.namespaceToSpaceId(namespaceOrNamespaces); - return await checkPrivilegesWithRequest(request).atSpace(spaceId, actions); + return await checkPrivilegesWithRequest(request).atSpace(spaceId, { kibana: actions }); } }; }; diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts index f9405214aac5..98faae6edab2 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.test.ts @@ -9,11 +9,17 @@ import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; import { httpServerMock, loggingSystemMock } from '../../../../../src/core/server/mocks'; import { authorizationMock } from './index.mock'; -import { Feature } from '../../../features/server'; +import { KibanaFeature, ElasticsearchFeature } from '../../../features/server'; +import { AuthenticatedUser } from '..'; +import { CheckPrivilegesResponse } from './types'; type MockAuthzOptions = | { rejectCheckPrivileges: any } - | { resolveCheckPrivileges: { privileges: Array<{ privilege: string; authorized: boolean }> } }; + | { + resolveCheckPrivileges: { + privileges: CheckPrivilegesResponse['privileges']; + }; + }; const actions = new Actions('1.0.0-zeta1'); const mockRequest = httpServerMock.createKibanaRequest(); @@ -31,14 +37,34 @@ const createMockAuthz = (options: MockAuthzOptions) => { throw options.rejectCheckPrivileges; } - const expected = options.resolveCheckPrivileges.privileges.map((x) => x.privilege); - expect(checkActions).toEqual(expected); + const expectedKibana = options.resolveCheckPrivileges.privileges.kibana.map( + (x) => x.privilege + ); + const expectedCluster = ( + options.resolveCheckPrivileges.privileges.elasticsearch.cluster ?? [] + ).map((x) => x.privilege); + + expect(checkActions).toEqual({ + kibana: expectedKibana, + elasticsearch: { cluster: expectedCluster, index: {} }, + }); return options.resolveCheckPrivileges; }); }); + mock.checkElasticsearchPrivilegesWithRequest.mockImplementation((request) => { + expect(request).toBe(mockRequest); + return jest.fn().mockImplementation((privileges) => {}); + }); return mock; }; +const createMockUser = (user: Partial = {}) => + ({ + username: 'mock_user', + roles: [], + ...user, + } as AuthenticatedUser); + describe('usingPrivileges', () => { describe('checkPrivileges errors', () => { test(`disables uiCapabilities when a 401 is thrown`, async () => { @@ -50,16 +76,28 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, [ - new Feature({ + new KibanaFeature({ id: 'fooFeature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', app: ['fooApp', 'foo'], navLinkId: 'foo', privileges: null, }), ], + [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: [], + }, + ], + }), + ], mockLoggers.get(), - mockAuthz + mockAuthz, + createMockUser() ); const result = await usingPrivileges( @@ -126,16 +164,28 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, [ - new Feature({ + new KibanaFeature({ id: 'fooFeature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', app: ['foo'], navLinkId: 'foo', privileges: null, }), ], + [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: [], + }, + ], + }), + ], mockLoggers.get(), - mockAuthz + mockAuthz, + createMockUser() ); const result = await usingPrivileges( @@ -199,8 +249,10 @@ describe('usingPrivileges', () => { const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, [], + [], mockLoggers.get(), - mockAuthz + mockAuthz, + createMockUser() ); await expect( @@ -234,40 +286,91 @@ describe('usingPrivileges', () => { test(`disables ui capabilities when they don't have privileges`, async () => { const mockAuthz = createMockAuthz({ resolveCheckPrivileges: { - privileges: [ - { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, - { privilege: actions.ui.get('navLinks', 'bar'), authorized: false }, - { privilege: actions.ui.get('navLinks', 'quz'), authorized: false }, - { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, - { privilege: actions.ui.get('management', 'kibana', 'settings'), authorized: false }, - { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'bar'), authorized: false }, - { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'bar'), authorized: false }, - ], + privileges: { + kibana: [ + { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, + { privilege: actions.ui.get('navLinks', 'bar'), authorized: false }, + { privilege: actions.ui.get('navLinks', 'quz'), authorized: false }, + { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, + { privilege: actions.ui.get('management', 'kibana', 'settings'), authorized: false }, + { + privilege: actions.ui.get('management', 'kibana', 'esManagement'), + authorized: false, + }, + { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, + { privilege: actions.ui.get('fooFeature', 'bar'), authorized: false }, + { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, + { privilege: actions.ui.get('barFeature', 'bar'), authorized: false }, + ], + elasticsearch: { + cluster: [ + { privilege: 'manage', authorized: false }, + { privilege: 'monitor', authorized: true }, + { privilege: 'manage_security', authorized: true }, + ], + index: {}, + }, + }, }, }); const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, [ - new Feature({ + new KibanaFeature({ id: 'fooFeature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', navLinkId: 'foo', app: [], privileges: null, }), - new Feature({ + new KibanaFeature({ id: 'barFeature', - name: 'Bar Feature', + name: 'Bar KibanaFeature', navLinkId: 'bar', app: ['bar'], privileges: null, }), ], + [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: ['manage'], + ui: ['es_manage'], + }, + { + requiredClusterPrivileges: ['monitor'], + ui: ['es_monitor'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'esSecurityFeature', + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: ['es_manage_sec'], + }, + ], + }), + new ElasticsearchFeature({ + id: 'esManagementFeature', + management: { + kibana: ['esManagement'], + }, + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], + }), + ], loggingSystemMock.create().get(), - mockAuthz + mockAuthz, + createMockUser() ); const result = await usingPrivileges( @@ -281,6 +384,7 @@ describe('usingPrivileges', () => { kibana: { indices: true, settings: false, + esManagement: true, }, }, catalogue: {}, @@ -292,6 +396,14 @@ describe('usingPrivileges', () => { foo: true, bar: true, }, + esFeature: { + es_manage: true, + es_monitor: true, + }, + esSecurityFeature: { + es_manage_sec: true, + }, + esManagementFeature: {}, }) ); @@ -305,6 +417,7 @@ describe('usingPrivileges', () => { kibana: { indices: true, settings: false, + esManagement: true, }, }, catalogue: {}, @@ -316,44 +429,70 @@ describe('usingPrivileges', () => { foo: true, bar: false, }, + esFeature: { + es_manage: false, + es_monitor: true, + }, + esSecurityFeature: { + es_manage_sec: true, + }, + esManagementFeature: {}, }); }); test(`doesn't re-enable disabled uiCapabilities`, async () => { const mockAuthz = createMockAuthz({ resolveCheckPrivileges: { - privileges: [ - { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, - { privilege: actions.ui.get('navLinks', 'bar'), authorized: true }, - { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('fooFeature', 'bar'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, - { privilege: actions.ui.get('barFeature', 'bar'), authorized: true }, - ], + privileges: { + kibana: [ + { privilege: actions.ui.get('navLinks', 'foo'), authorized: true }, + { privilege: actions.ui.get('navLinks', 'bar'), authorized: true }, + { privilege: actions.ui.get('management', 'kibana', 'indices'), authorized: true }, + { privilege: actions.ui.get('fooFeature', 'foo'), authorized: true }, + { privilege: actions.ui.get('fooFeature', 'bar'), authorized: true }, + { privilege: actions.ui.get('barFeature', 'foo'), authorized: true }, + { privilege: actions.ui.get('barFeature', 'bar'), authorized: true }, + ], + elasticsearch: { + cluster: [], + index: {}, + }, + }, }, }); const { usingPrivileges } = disableUICapabilitiesFactory( mockRequest, [ - new Feature({ + new KibanaFeature({ id: 'fooFeature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', navLinkId: 'foo', app: [], privileges: null, }), - new Feature({ + new KibanaFeature({ id: 'barFeature', - name: 'Bar Feature', + name: 'Bar KibanaFeature', navLinkId: 'bar', app: [], privileges: null, }), ], + [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: [], + }, + ], + }), + ], loggingSystemMock.create().get(), - mockAuthz + mockAuthz, + createMockUser() ); const result = await usingPrivileges( @@ -409,16 +548,28 @@ describe('all', () => { const { all } = disableUICapabilitiesFactory( mockRequest, [ - new Feature({ + new KibanaFeature({ id: 'fooFeature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', app: ['foo'], navLinkId: 'foo', privileges: null, }), ], + [ + new ElasticsearchFeature({ + id: 'esFeature', + privileges: [ + { + requiredClusterPrivileges: [], + ui: ['bar'], + }, + ], + }), + ], loggingSystemMock.create().get(), - mockAuthz + mockAuthz, + createMockUser() ); const result = all( @@ -441,6 +592,9 @@ describe('all', () => { foo: true, bar: true, }, + esFeature: { + bar: true, + }, }) ); expect(result).toEqual({ @@ -462,6 +616,9 @@ describe('all', () => { foo: false, bar: false, }, + esFeature: { + bar: false, + }, }); }); }); diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts index 41d596d570fb..89cc9065655c 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts @@ -5,18 +5,26 @@ */ import { flatten, isObject, mapValues } from 'lodash'; +import { RecursiveReadonly, RecursiveReadonlyArray } from '@kbn/utility-types'; import type { Capabilities as UICapabilities } from '../../../../../src/core/types'; import { KibanaRequest, Logger } from '../../../../../src/core/server'; -import { Feature } from '../../../features/server'; +import { + KibanaFeature, + ElasticsearchFeature, + FeatureElasticsearchPrivileges, +} from '../../../features/server'; -import { CheckPrivilegesResponse } from './check_privileges'; +import { CheckPrivilegesResponse } from './types'; import { AuthorizationServiceSetup } from '.'; +import { AuthenticatedUser } from '..'; export function disableUICapabilitiesFactory( request: KibanaRequest, - features: Feature[], + features: KibanaFeature[], + elasticsearchFeatures: ElasticsearchFeature[], logger: Logger, - authz: AuthorizationServiceSetup + authz: AuthorizationServiceSetup, + user: AuthenticatedUser | null ) { // nav links are sourced from the apps property. // The Kibana Platform associates nav links to the app which registers it, in a 1:1 relationship. @@ -25,6 +33,39 @@ export function disableUICapabilitiesFactory( .flatMap((feature) => feature.app) .filter((navLinkId) => navLinkId != null); + const elasticsearchFeatureMap = elasticsearchFeatures.reduce< + Record> + >((acc, esFeature) => { + return { + ...acc, + [esFeature.id]: esFeature.privileges, + }; + }, {}); + + const allRequiredClusterPrivileges = Array.from( + new Set( + Object.values(elasticsearchFeatureMap) + .flat() + .map((p) => p.requiredClusterPrivileges) + .flat() + ) + ); + + const allRequiredIndexPrivileges = Object.values(elasticsearchFeatureMap) + .flat() + .filter((p) => !!p.requiredIndexPrivileges) + .reduce>((acc, p) => { + return { + ...acc, + ...Object.entries(p.requiredIndexPrivileges!).reduce((acc2, [indexName, privileges]) => { + return { + ...acc2, + [indexName]: [...(acc[indexName] ?? []), ...privileges], + }; + }, {}), + }; + }, {}); + const shouldDisableFeatureUICapability = ( featureId: keyof UICapabilities, uiCapability: string @@ -59,6 +100,12 @@ export function disableUICapabilitiesFactory( uiCapability: string, value: boolean | Record ): string[] { + // Capabilities derived from Elasticsearch features should not be + // included here, as the result is used to check authorization against + // Kibana Privileges, rather than Elasticsearch Privileges. + if (elasticsearchFeatureMap.hasOwnProperty(featureId)) { + return []; + } if (typeof value === 'boolean') { return [authz.actions.ui.get(featureId, uiCapability)]; } @@ -85,7 +132,13 @@ export function disableUICapabilitiesFactory( let checkPrivilegesResponse: CheckPrivilegesResponse; try { const checkPrivilegesDynamically = authz.checkPrivilegesDynamicallyWithRequest(request); - checkPrivilegesResponse = await checkPrivilegesDynamically(uiActions); + checkPrivilegesResponse = await checkPrivilegesDynamically({ + kibana: uiActions, + elasticsearch: { + cluster: allRequiredClusterPrivileges, + index: allRequiredIndexPrivileges, + }, + }); } catch (err) { // if we get a 401/403, then we want to disable all uiCapabilities, as this // is generally when the user hasn't authenticated yet and we're displaying the @@ -110,9 +163,65 @@ export function disableUICapabilitiesFactory( } const action = authz.actions.ui.get(featureId, ...uiCapabilityParts); - return checkPrivilegesResponse.privileges.some( - (x) => x.privilege === action && x.authorized === true - ); + + const isElasticsearchFeature = elasticsearchFeatureMap.hasOwnProperty(featureId); + const isCatalogueFeature = featureId === 'catalogue'; + const isManagementFeature = featureId === 'management'; + + if (!isElasticsearchFeature) { + const hasRequiredKibanaPrivileges = checkPrivilegesResponse.privileges.kibana.some( + (x) => x.privilege === action && x.authorized === true + ); + + // Catalogue and management capbility buckets can also be influenced by ES privileges, + // so the early return is not possible for these. + if ((!isCatalogueFeature && !isManagementFeature) || hasRequiredKibanaPrivileges) { + return hasRequiredKibanaPrivileges; + } + } + + return elasticsearchFeatures.some((esFeature) => { + if (isCatalogueFeature) { + const [catalogueEntry] = uiCapabilityParts; + const featureGrantsCatalogueEntry = (esFeature.catalogue ?? []).includes(catalogueEntry); + return ( + featureGrantsCatalogueEntry && + hasAnyRequiredElasticsearchPrivilegesForFeature( + esFeature, + checkPrivilegesResponse, + user + ) + ); + } else if (isManagementFeature) { + const [managementSectionId, managementEntryId] = uiCapabilityParts; + const featureGrantsManagementEntry = + (esFeature.management ?? {}).hasOwnProperty(managementSectionId) && + esFeature.management![managementSectionId].includes(managementEntryId); + + return ( + featureGrantsManagementEntry && + hasAnyRequiredElasticsearchPrivilegesForFeature( + esFeature, + checkPrivilegesResponse, + user + ) + ); + } else if (esFeature.id === featureId) { + if (uiCapabilityParts.length !== 1) { + // The current privilege system does not allow for this to happen. + // This is a safeguard against future changes. + throw new Error( + `Elasticsearch feature ${esFeature.id} expected a single capability, but found ${uiCapabilityParts.length}` + ); + } + return hasRequiredElasticsearchPrivilegesForCapability( + esFeature, + uiCapabilityParts[0], + checkPrivilegesResponse, + user + ); + } + }); }; return mapValues(uiCapabilities, (featureUICapabilities, featureId) => { @@ -151,3 +260,56 @@ export function disableUICapabilitiesFactory( usingPrivileges, }; } + +function hasRequiredElasticsearchPrivilegesForCapability( + esFeature: ElasticsearchFeature, + uiCapability: string, + checkPrivilegesResponse: CheckPrivilegesResponse, + user: AuthenticatedUser | null +) { + return esFeature.privileges.some((privilege) => { + const privilegeGrantsCapability = privilege.ui.includes(uiCapability); + if (!privilegeGrantsCapability) { + return false; + } + + return isGrantedElasticsearchPrivilege(privilege, checkPrivilegesResponse, user); + }); +} + +function hasAnyRequiredElasticsearchPrivilegesForFeature( + esFeature: ElasticsearchFeature, + checkPrivilegesResponse: CheckPrivilegesResponse, + user: AuthenticatedUser | null +) { + return esFeature.privileges.some((privilege) => { + return isGrantedElasticsearchPrivilege(privilege, checkPrivilegesResponse, user); + }); +} + +function isGrantedElasticsearchPrivilege( + privilege: RecursiveReadonly, + checkPrivilegesResponse: CheckPrivilegesResponse, + user: AuthenticatedUser | null +) { + const hasRequiredClusterPrivileges = privilege.requiredClusterPrivileges.every( + (expectedClusterPriv) => + checkPrivilegesResponse.privileges.elasticsearch.cluster.some( + (x) => x.privilege === expectedClusterPriv && x.authorized === true + ) + ); + + const hasRequiredIndexPrivileges = Object.entries(privilege.requiredIndexPrivileges ?? {}).every( + ([indexName, requiredIndexPrivileges]) => { + return checkPrivilegesResponse.privileges.elasticsearch.index[indexName] + .filter((indexResponse) => requiredIndexPrivileges.includes(indexResponse.privilege)) + .every((indexResponse) => indexResponse.authorized); + } + ); + + const hasRequiredRoles = (privilege.requiredRoles ?? []).every( + (requiredRole) => user?.roles.includes(requiredRole) ?? false + ); + + return hasRequiredClusterPrivileges && hasRequiredIndexPrivileges && hasRequiredRoles; +} diff --git a/x-pack/plugins/security/server/authorization/index.mock.ts b/x-pack/plugins/security/server/authorization/index.mock.ts index 62b254d132d9..6cb78a3001a9 100644 --- a/x-pack/plugins/security/server/authorization/index.mock.ts +++ b/x-pack/plugins/security/server/authorization/index.mock.ts @@ -13,6 +13,7 @@ export const authorizationMock = { }: { version?: string; applicationName?: string } = {}) => ({ actions: actionsMock.create(version), checkPrivilegesWithRequest: jest.fn(), + checkElasticsearchPrivilegesWithRequest: jest.fn(), checkPrivilegesDynamicallyWithRequest: jest.fn(), checkSavedObjectsPrivilegesWithRequest: jest.fn(), applicationName, diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts index 5e9c1818cad2..dc261e2eec98 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.test.ts @@ -6,7 +6,7 @@ import { Actions } from '../../actions'; import { FeaturePrivilegeAlertingBuilder } from './alerting'; -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; const version = '1.0.0-zeta1'; @@ -29,7 +29,7 @@ describe(`feature_privilege_builder`, () => { ui: [], }; - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'my-feature', name: 'my-feature', app: [], @@ -60,7 +60,7 @@ describe(`feature_privilege_builder`, () => { ui: [], }; - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'my-feature', name: 'my-feature', app: [], @@ -97,7 +97,7 @@ describe(`feature_privilege_builder`, () => { ui: [], }; - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'my-feature', name: 'my-feature', app: [], @@ -144,7 +144,7 @@ describe(`feature_privilege_builder`, () => { ui: [], }; - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'my-feature', name: 'my-feature', app: [], diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts index eb278a575520..fa9cadf2aea6 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/alerting.ts @@ -5,7 +5,7 @@ */ import { uniq } from 'lodash'; -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; const readOperations: string[] = ['get', 'getAlertState', 'getAlertInstanceSummary', 'find']; @@ -24,7 +24,10 @@ const writeOperations: string[] = [ const allOperations: string[] = [...readOperations, ...writeOperations]; export class FeaturePrivilegeAlertingBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions( + privilegeDefinition: FeatureKibanaPrivileges, + feature: KibanaFeature + ): string[] { const getAlertingPrivilege = ( operations: string[], privilegedTypes: readonly string[], diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/api.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/api.ts index 6b7d94bb0127..0e63cdceffc5 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/api.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/api.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeApiBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { if (privilegeDefinition.api) { return privilegeDefinition.api.map((operation) => this.actions.api.get(operation)); } diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/app.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/app.ts index 213aa83f2d26..bf6b0e60f104 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/app.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/app.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeAppBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { const appIds = privilegeDefinition.app; if (!appIds) { diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/catalogue.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/catalogue.ts index f1ea7091b948..97a3c9c1e336 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/catalogue.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/catalogue.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeCatalogueBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { const catalogueEntries = privilegeDefinition.catalogue; if (!catalogueEntries) { diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/feature_privilege_builder.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/feature_privilege_builder.ts index 172ab24eb7e5..0eded66d65b0 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/feature_privilege_builder.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/feature_privilege_builder.ts @@ -4,17 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; import { Actions } from '../../actions'; export interface FeaturePrivilegeBuilder { - getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[]; + getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: KibanaFeature): string[]; } export abstract class BaseFeaturePrivilegeBuilder implements FeaturePrivilegeBuilder { constructor(protected readonly actions: Actions) {} public abstract getActions( privilegeDefinition: FeatureKibanaPrivileges, - feature: Feature + feature: KibanaFeature ): string[]; } diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/index.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/index.ts index 76b664cbbe2a..998fbc5cc5e2 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/index.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/index.ts @@ -5,7 +5,7 @@ */ import { flatten } from 'lodash'; -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; import { Actions } from '../../actions'; import { FeaturePrivilegeApiBuilder } from './api'; import { FeaturePrivilegeAppBuilder } from './app'; @@ -31,7 +31,7 @@ export const featurePrivilegeBuilderFactory = (actions: Actions): FeaturePrivile ]; return { - getActions(privilege: FeatureKibanaPrivileges, feature: Feature) { + getActions(privilege: FeatureKibanaPrivileges, feature: KibanaFeature) { return flatten(builders.map((builder) => builder.getActions(privilege, feature))); }, }; diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/management.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/management.ts index be784949dc2f..67b8cdb7616d 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/management.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/management.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeManagementBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { const managementSections = privilegeDefinition.management; if (!managementSections) { diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/navlink.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/navlink.ts index a6e5a01c7dba..7400675ed17f 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/navlink.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/navlink.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeNavlinkBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { return (privilegeDefinition.app ?? []).map((app) => this.actions.ui.get('navLinks', app)); } } diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts index 2c325fc8c6cb..0dd89f2c5f3c 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts @@ -5,7 +5,7 @@ */ import { flatten, uniq } from 'lodash'; -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; const readOperations: string[] = ['bulk_get', 'get', 'find']; @@ -13,7 +13,7 @@ const writeOperations: string[] = ['create', 'bulk_create', 'update', 'bulk_upda const allOperations: string[] = [...readOperations, ...writeOperations]; export class FeaturePrivilegeSavedObjectBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions(privilegeDefinition: FeatureKibanaPrivileges): string[] { return uniq([ ...flatten( privilegeDefinition.savedObject.all.map((type) => [ diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/ui.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/ui.ts index 31bc351206e5..dd167a291f11 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/ui.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/ui.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; export class FeaturePrivilegeUIBuilder extends BaseFeaturePrivilegeBuilder { - public getActions(privilegeDefinition: FeatureKibanaPrivileges, feature: Feature): string[] { + public getActions( + privilegeDefinition: FeatureKibanaPrivileges, + feature: KibanaFeature + ): string[] { return privilegeDefinition.ui.map((ui) => this.actions.ui.get(feature.id, ui)); } } diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.test.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.test.ts index bb1f0c33fdee..033040fd2f14 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.test.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../../features/server'; +import { KibanaFeature } from '../../../../../features/server'; import { featurePrivilegeIterator } from './feature_privilege_iterator'; describe('featurePrivilegeIterator', () => { it('handles features with no privileges', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', privileges: null, @@ -26,7 +26,7 @@ describe('featurePrivilegeIterator', () => { }); it('handles features with no sub-features', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', privileges: { @@ -117,7 +117,7 @@ describe('featurePrivilegeIterator', () => { }); it('filters privileges using the provided predicate', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', privileges: { @@ -190,7 +190,7 @@ describe('featurePrivilegeIterator', () => { }); it('ignores sub features when `augmentWithSubFeaturePrivileges` is false', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -313,7 +313,7 @@ describe('featurePrivilegeIterator', () => { }); it('ignores sub features when `includeIn` is none, even if `augmentWithSubFeaturePrivileges` is true', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -436,7 +436,7 @@ describe('featurePrivilegeIterator', () => { }); it('includes sub feature privileges into both all and read when`augmentWithSubFeaturePrivileges` is true and `includeIn: read`', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -563,7 +563,7 @@ describe('featurePrivilegeIterator', () => { }); it('does not duplicate privileges when merging', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -686,7 +686,7 @@ describe('featurePrivilegeIterator', () => { }); it('includes sub feature privileges into both all and read when`augmentWithSubFeaturePrivileges` is true and `includeIn: all`', () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -811,7 +811,7 @@ describe('featurePrivilegeIterator', () => { }); it(`can augment primary feature privileges even if they don't specify their own`, () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -919,7 +919,7 @@ describe('featurePrivilegeIterator', () => { }); it(`can augment primary feature privileges even if the sub-feature privileges don't specify their own`, () => { - const feature = new Feature({ + const feature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.ts index 17c9464b1475..dba33f7a4f36 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/feature_privilege_iterator.ts @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import { Feature, FeatureKibanaPrivileges } from '../../../../../features/server'; +import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server'; import { subFeaturePrivilegeIterator } from './sub_feature_privilege_iterator'; interface IteratorOptions { @@ -14,7 +14,7 @@ interface IteratorOptions { } export function* featurePrivilegeIterator( - feature: Feature, + feature: KibanaFeature, options: IteratorOptions ): IterableIterator<{ privilegeId: string; privilege: FeatureKibanaPrivileges }> { for (const entry of Object.entries(feature.privileges ?? {})) { @@ -35,7 +35,7 @@ export function* featurePrivilegeIterator( function mergeWithSubFeatures( privilegeId: string, privilege: FeatureKibanaPrivileges, - feature: Feature + feature: KibanaFeature ) { const mergedConfig = _.cloneDeep(privilege); for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature)) { diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/sub_feature_privilege_iterator.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/sub_feature_privilege_iterator.ts index b288262be25c..d54b6d458d91 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/sub_feature_privilege_iterator.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_iterator/sub_feature_privilege_iterator.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SubFeaturePrivilegeConfig } from '../../../../../features/common'; -import { Feature } from '../../../../../features/server'; +import { KibanaFeature, SubFeaturePrivilegeConfig } from '../../../../../features/common'; export function* subFeaturePrivilegeIterator( - feature: Feature + feature: KibanaFeature ): IterableIterator { for (const subFeature of feature.subFeatures) { for (const group of subFeature.privilegeGroups) { diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts index 89ac73c22075..dd8ac44386db 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../features/server'; +import { KibanaFeature } from '../../../../features/server'; import { Actions } from '../actions'; import { privilegesFactory } from './privileges'; @@ -14,10 +14,10 @@ const actions = new Actions('1.0.0-zeta1'); describe('features', () => { test('actions defined at the feature do not cascade to the privileges', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo-feature', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: ['app-1', 'app-2'], @@ -45,7 +45,7 @@ describe('features', () => { ]; const mockFeaturesService = featuresPluginMock.createSetup(); - mockFeaturesService.getFeatures.mockReturnValue(features); + mockFeaturesService.getKibanaFeatures.mockReturnValue(features); const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), @@ -60,10 +60,10 @@ describe('features', () => { }); test(`actions only specified at the privilege are alright too`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -85,13 +85,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const expectedAllPrivileges = [ actions.login, @@ -159,23 +159,23 @@ describe('features', () => { }); test(`features with no privileges aren't listed`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: null, }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).not.toHaveProperty('features.foo'); @@ -200,10 +200,10 @@ describe('features', () => { ].forEach(({ group, expectManageSpaces, expectGetFeatures, expectEnterpriseSearch }) => { describe(`${group}`, () => { test('actions defined in any feature privilege are included in `all`', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: [], @@ -238,13 +238,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty(`${group}.all`, [ @@ -256,6 +256,7 @@ describe('features', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), @@ -319,10 +320,10 @@ describe('features', () => { }); test('actions defined in a feature privilege with name `read` are included in `read`', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: [], @@ -357,13 +358,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty(`${group}.read`, [ @@ -401,10 +402,10 @@ describe('features', () => { }); test('actions defined in a reserved privilege are not included in `all` or `read`', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: [], @@ -431,13 +432,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty(`${group}.all`, [ @@ -449,6 +450,7 @@ describe('features', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), @@ -457,10 +459,10 @@ describe('features', () => { }); test('actions defined in a feature with excludeFromBasePrivileges are not included in `all` or `read', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', excludeFromBasePrivileges: true, icon: 'arrowDown', navLinkId: 'kibana:foo', @@ -496,13 +498,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty(`${group}.all`, [ @@ -514,6 +516,7 @@ describe('features', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), @@ -522,10 +525,10 @@ describe('features', () => { }); test('actions defined in an individual feature privilege with excludeFromBasePrivileges are not included in `all` or `read`', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: [], @@ -562,13 +565,13 @@ describe('features', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty(`${group}.all`, [ @@ -580,6 +583,7 @@ describe('features', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), ] : []), ...(expectEnterpriseSearch ? [actions.ui.get('enterpriseSearch', 'all')] : []), @@ -591,10 +595,10 @@ describe('features', () => { describe('reserved', () => { test('actions defined at the feature do not cascade to the privileges', () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', navLinkId: 'kibana:foo', app: ['app-1', 'app-2'], @@ -621,23 +625,23 @@ describe('reserved', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty('reserved.foo', [actions.version]); }); test(`actions only specified at the privilege are alright too`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: null, @@ -659,13 +663,13 @@ describe('reserved', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).toHaveProperty('reserved.foo', [ @@ -698,10 +702,10 @@ describe('reserved', () => { }); test(`features with no reservedPrivileges aren't listed`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -723,13 +727,13 @@ describe('reserved', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual).not.toHaveProperty('reserved.foo'); @@ -739,10 +743,10 @@ describe('reserved', () => { describe('subFeatures', () => { describe(`with includeIn: 'none'`, () => { test(`should not augment the primary feature privileges, base privileges, or minimal feature privileges`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -786,13 +790,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`, [ @@ -841,6 +845,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), actions.ui.get('foo', 'foo'), ]); @@ -865,10 +870,10 @@ describe('subFeatures', () => { describe(`with includeIn: 'read'`, () => { test(`should augment the primary feature privileges and base privileges, but never the minimal versions`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -912,13 +917,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`, [ @@ -993,6 +998,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), @@ -1063,10 +1069,10 @@ describe('subFeatures', () => { }); test(`should augment the primary feature privileges, but not base privileges if feature is excluded from them.`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], excludeFromBasePrivileges: true, @@ -1111,13 +1117,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`, [ @@ -1192,6 +1198,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), ]); expect(actual).toHaveProperty('global.read', [actions.login, actions.version]); @@ -1203,10 +1210,10 @@ describe('subFeatures', () => { describe(`with includeIn: 'all'`, () => { test(`should augment the primary 'all' feature privileges and base 'all' privileges, but never the minimal versions`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -1250,13 +1257,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`, [ @@ -1319,6 +1326,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), @@ -1365,10 +1373,10 @@ describe('subFeatures', () => { }); test(`should augment the primary 'all' feature privileges, but not the base privileges if the feature is excluded from them`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], excludeFromBasePrivileges: true, @@ -1413,13 +1421,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`, [ @@ -1482,6 +1490,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), ]); expect(actual).toHaveProperty('global.read', [actions.login, actions.version]); @@ -1493,10 +1502,10 @@ describe('subFeatures', () => { describe(`when license does not allow sub features`, () => { test(`should augment the primary feature privileges, and should not create minimal or sub-feature privileges`, () => { - const features: Feature[] = [ - new Feature({ + const features: KibanaFeature[] = [ + new KibanaFeature({ id: 'foo', - name: 'Foo Feature', + name: 'Foo KibanaFeature', icon: 'arrowDown', app: [], privileges: { @@ -1540,13 +1549,13 @@ describe('subFeatures', () => { }), ]; - const mockXPackMainPlugin = { - getFeatures: jest.fn().mockReturnValue(features), + const mockFeaturesPlugin = { + getKibanaFeatures: jest.fn().mockReturnValue(features), }; const mockLicenseService = { getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: false }), }; - const privileges = privilegesFactory(actions, mockXPackMainPlugin as any, mockLicenseService); + const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService); const actual = privileges.get(); expect(actual.features).not.toHaveProperty(`foo.subFeaturePriv1`); @@ -1598,6 +1607,7 @@ describe('subFeatures', () => { actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), actions.savedObject.get('all-sub-feature-type', 'bulk_get'), actions.savedObject.get('all-sub-feature-type', 'get'), diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.ts index 5d8ef3f376ca..24b46222e7f3 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.ts @@ -6,7 +6,10 @@ import { uniq } from 'lodash'; import { SecurityLicense } from '../../../common/licensing'; -import { Feature, PluginSetupContract as FeaturesPluginSetup } from '../../../../features/server'; +import { + KibanaFeature, + PluginSetupContract as FeaturesPluginSetup, +} from '../../../../features/server'; import { RawKibanaPrivileges } from '../../../common/model'; import { Actions } from '../actions'; import { featurePrivilegeBuilderFactory } from './feature_privilege_builder'; @@ -28,7 +31,7 @@ export function privilegesFactory( return { get() { - const features = featuresService.getFeatures(); + const features = featuresService.getKibanaFeatures(); const { allowSubFeaturePrivileges } = licenseService.getFeatures(); const basePrivilegeFeatures = features.filter( (feature) => !feature.excludeFromBasePrivileges @@ -100,6 +103,7 @@ export function privilegesFactory( actions.space.manage, actions.ui.get('spaces', 'manage'), actions.ui.get('management', 'kibana', 'spaces'), + actions.ui.get('catalogue', 'spaces'), actions.ui.get('enterpriseSearch', 'all'), ...allActions, ], @@ -109,7 +113,7 @@ export function privilegesFactory( all: [actions.login, actions.version, ...allActions], read: [actions.login, actions.version, ...readActions], }, - reserved: features.reduce((acc: Record, feature: Feature) => { + reserved: features.reduce((acc: Record, feature: KibanaFeature) => { if (feature.reserved) { feature.reserved.privileges.forEach((reservedPrivilege) => { acc[reservedPrivilege.id] = [ diff --git a/x-pack/plugins/security/server/authorization/types.ts b/x-pack/plugins/security/server/authorization/types.ts index 75188d1191b1..bedf46862e4f 100644 --- a/x-pack/plugins/security/server/authorization/types.ts +++ b/x-pack/plugins/security/server/authorization/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { KibanaRequest } from 'src/core/server'; + export interface HasPrivilegesResponseApplication { [resource: string]: { [privilegeName: string]: boolean; @@ -16,4 +18,58 @@ export interface HasPrivilegesResponse { application: { [applicationName: string]: HasPrivilegesResponseApplication; }; + cluster?: { + [privilegeName: string]: boolean; + }; + index?: { + [indexName: string]: { + [privilegeName: string]: boolean; + }; + }; +} + +export interface CheckPrivilegesResponse { + hasAllRequested: boolean; + username: string; + privileges: { + kibana: Array<{ + /** + * If this attribute is undefined, this element is a privilege for the global resource. + */ + resource?: string; + privilege: string; + authorized: boolean; + }>; + elasticsearch: { + cluster: Array<{ + privilege: string; + authorized: boolean; + }>; + index: { + [indexName: string]: Array<{ + privilege: string; + authorized: boolean; + }>; + }; + }; + }; +} + +export type CheckPrivilegesWithRequest = (request: KibanaRequest) => CheckPrivileges; + +export interface CheckPrivileges { + atSpace(spaceId: string, privileges: CheckPrivilegesPayload): Promise; + atSpaces( + spaceIds: string[], + privileges: CheckPrivilegesPayload + ): Promise; + globally(privileges: CheckPrivilegesPayload): Promise; +} + +export interface CheckPrivilegesPayload { + kibana?: string | string[]; + elasticsearch?: { + cluster: string[]; + index: Record; + }; } diff --git a/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts b/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts index cd2c7faa263c..8e6d72670c8d 100644 --- a/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; import { validateFeaturePrivileges } from './validate_feature_privileges'; it('allows features to be defined without privileges', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -19,7 +19,7 @@ it('allows features to be defined without privileges', () => { }); it('allows features with reserved privileges to be defined', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -45,7 +45,7 @@ it('allows features with reserved privileges to be defined', () => { }); it('allows features with sub-features to be defined', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -108,7 +108,7 @@ it('allows features with sub-features to be defined', () => { }); it('does not allow features with sub-features which have id conflicts with the minimal privileges', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -153,12 +153,12 @@ it('does not allow features with sub-features which have id conflicts with the m }); expect(() => validateFeaturePrivileges([feature])).toThrowErrorMatchingInlineSnapshot( - `"Feature 'foo' already has a privilege with ID 'minimal_all'. Sub feature 'sub-feature-1' cannot also specify this."` + `"KibanaFeature 'foo' already has a privilege with ID 'minimal_all'. Sub feature 'sub-feature-1' cannot also specify this."` ); }); it('does not allow features with sub-features which have id conflicts with the primary feature privileges', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -203,12 +203,12 @@ it('does not allow features with sub-features which have id conflicts with the p }); expect(() => validateFeaturePrivileges([feature])).toThrowErrorMatchingInlineSnapshot( - `"Feature 'foo' already has a privilege with ID 'read'. Sub feature 'sub-feature-1' cannot also specify this."` + `"KibanaFeature 'foo' already has a privilege with ID 'read'. Sub feature 'sub-feature-1' cannot also specify this."` ); }); it('does not allow features with sub-features which have id conflicts each other', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -273,6 +273,6 @@ it('does not allow features with sub-features which have id conflicts each other }); expect(() => validateFeaturePrivileges([feature])).toThrowErrorMatchingInlineSnapshot( - `"Feature 'foo' already has a privilege with ID 'some-sub-feature'. Sub feature 'sub-feature-2' cannot also specify this."` + `"KibanaFeature 'foo' already has a privilege with ID 'some-sub-feature'. Sub feature 'sub-feature-2' cannot also specify this."` ); }); diff --git a/x-pack/plugins/security/server/authorization/validate_feature_privileges.ts b/x-pack/plugins/security/server/authorization/validate_feature_privileges.ts index 79e5348b4ac6..eeb9c4cb7431 100644 --- a/x-pack/plugins/security/server/authorization/validate_feature_privileges.ts +++ b/x-pack/plugins/security/server/authorization/validate_feature_privileges.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; -export function validateFeaturePrivileges(features: Feature[]) { +export function validateFeaturePrivileges(features: KibanaFeature[]) { for (const feature of features) { const seenPrivilegeIds = new Set(); Object.keys(feature.privileges ?? {}).forEach((privilegeId) => { @@ -20,7 +20,7 @@ export function validateFeaturePrivileges(features: Feature[]) { subFeaturePrivilegeGroup.privileges.forEach((subFeaturePrivilege) => { if (seenPrivilegeIds.has(subFeaturePrivilege.id)) { throw new Error( - `Feature '${feature.id}' already has a privilege with ID '${subFeaturePrivilege.id}'. Sub feature '${subFeature.name}' cannot also specify this.` + `KibanaFeature '${feature.id}' already has a privilege with ID '${subFeaturePrivilege.id}'. Sub feature '${subFeature.name}' cannot also specify this.` ); } seenPrivilegeIds.add(subFeaturePrivilege.id); diff --git a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts index 26af0dadfb28..d91a4d415131 100644 --- a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; import { validateReservedPrivileges } from './validate_reserved_privileges'; it('allows features to be defined without privileges', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -19,7 +19,7 @@ it('allows features to be defined without privileges', () => { }); it('allows features with a single reserved privilege to be defined', () => { - const feature: Feature = new Feature({ + const feature: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -45,7 +45,7 @@ it('allows features with a single reserved privilege to be defined', () => { }); it('allows multiple features with reserved privileges to be defined', () => { - const feature1: Feature = new Feature({ + const feature1: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -67,7 +67,7 @@ it('allows multiple features with reserved privileges to be defined', () => { }, }); - const feature2: Feature = new Feature({ + const feature2: KibanaFeature = new KibanaFeature({ id: 'foo2', name: 'foo', app: [], @@ -93,7 +93,7 @@ it('allows multiple features with reserved privileges to be defined', () => { }); it('prevents a feature from specifying the same reserved privilege id', () => { - const feature1: Feature = new Feature({ + const feature1: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -131,7 +131,7 @@ it('prevents a feature from specifying the same reserved privilege id', () => { }); it('prevents features from sharing a reserved privilege id', () => { - const feature1: Feature = new Feature({ + const feature1: KibanaFeature = new KibanaFeature({ id: 'foo', name: 'foo', app: [], @@ -153,7 +153,7 @@ it('prevents features from sharing a reserved privilege id', () => { }, }); - const feature2: Feature = new Feature({ + const feature2: KibanaFeature = new KibanaFeature({ id: 'foo2', name: 'foo', app: [], diff --git a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts index 0915308fc0f8..23e5c28a4af1 100644 --- a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts +++ b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; -export function validateReservedPrivileges(features: Feature[]) { +export function validateReservedPrivileges(features: KibanaFeature[]) { const seenPrivilegeIds = new Set(); for (const feature of features) { diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 520081ae30d8..093a7643fbf6 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -904,11 +904,13 @@ describe('createConfig()', () => { }, "sortedProviders": Array [ Object { + "hasAccessAgreement": false, "name": "saml", "order": 0, "type": "saml", }, Object { + "hasAccessAgreement": false, "name": "basic", "order": 1, "type": "basic", @@ -982,6 +984,63 @@ describe('createConfig()', () => { ).toBe(true); }); + it('indicates which providers have the access agreement enabled', () => { + expect( + createConfig( + ConfigSchema.validate({ + authc: { + providers: { + basic: { basic1: { order: 3 } }, + saml: { + saml1: { order: 2, realm: 'saml1', accessAgreement: { message: 'foo' } }, + saml2: { order: 1, realm: 'saml2' }, + }, + oidc: { + oidc1: { order: 0, realm: 'oidc1', accessAgreement: { message: 'foo' } }, + oidc2: { order: 4, realm: 'oidc2' }, + }, + }, + }, + }), + loggingSystemMock.create().get(), + { isTLSEnabled: true } + ).authc.sortedProviders + ).toMatchInlineSnapshot(` + Array [ + Object { + "hasAccessAgreement": true, + "name": "oidc1", + "order": 0, + "type": "oidc", + }, + Object { + "hasAccessAgreement": false, + "name": "saml2", + "order": 1, + "type": "saml", + }, + Object { + "hasAccessAgreement": true, + "name": "saml1", + "order": 2, + "type": "saml", + }, + Object { + "hasAccessAgreement": false, + "name": "basic1", + "order": 3, + "type": "basic", + }, + Object { + "hasAccessAgreement": false, + "name": "oidc2", + "order": 4, + "type": "oidc", + }, + ] + `); + }); + it('correctly sorts providers based on the `order`', () => { expect( createConfig( @@ -1000,26 +1059,31 @@ describe('createConfig()', () => { ).toMatchInlineSnapshot(` Array [ Object { + "hasAccessAgreement": false, "name": "oidc1", "order": 0, "type": "oidc", }, Object { + "hasAccessAgreement": false, "name": "saml2", "order": 1, "type": "saml", }, Object { + "hasAccessAgreement": false, "name": "saml1", "order": 2, "type": "saml", }, Object { + "hasAccessAgreement": false, "name": "basic1", "order": 3, "type": "basic", }, Object { + "hasAccessAgreement": false, "name": "oidc2", "order": 4, "type": "oidc", diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index dcfe4825fb03..9ccbdac5e09f 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -255,13 +255,19 @@ export function createConfig( type: keyof ProvidersConfigType; name: string; order: number; + hasAccessAgreement: boolean; }> = []; for (const [type, providerGroup] of Object.entries(providers)) { - for (const [name, { enabled, order }] of Object.entries(providerGroup ?? {})) { + for (const [name, { enabled, order, accessAgreement }] of Object.entries(providerGroup ?? {})) { if (!enabled) { delete providerGroup![name]; } else { - sortedProviders.push({ type: type as any, name, order }); + sortedProviders.push({ + type: type as any, + name, + order, + hasAccessAgreement: !!accessAgreement?.message, + }); } } } diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/index.ts b/x-pack/plugins/security/server/features/index.ts similarity index 78% rename from x-pack/plugins/security_solution/public/common/lib/connectors/index.ts rename to x-pack/plugins/security/server/features/index.ts index 33afa82c84f3..3fe097c2bec1 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/index.ts +++ b/x-pack/plugins/security/server/features/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getActionType as resilientActionType } from './resilient'; +export { securityFeatures } from './security_features'; diff --git a/x-pack/plugins/security/server/features/security_features.ts b/x-pack/plugins/security/server/features/security_features.ts new file mode 100644 index 000000000000..d80314c077aa --- /dev/null +++ b/x-pack/plugins/security/server/features/security_features.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ElasticsearchFeatureConfig } from '../../../features/server'; + +const userManagementFeature: ElasticsearchFeatureConfig = { + id: 'users', + management: { + security: ['users'], + }, + catalogue: ['security'], + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], +}; + +const rolesManagementFeature: ElasticsearchFeatureConfig = { + id: 'roles', + management: { + security: ['roles'], + }, + catalogue: ['security'], + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], +}; + +const apiKeysManagementFeature: ElasticsearchFeatureConfig = { + id: 'api_keys', + management: { + security: ['api_keys'], + }, + catalogue: ['security'], + privileges: [ + { + requiredClusterPrivileges: ['manage_api_key'], + ui: [], + }, + { + requiredClusterPrivileges: ['manage_own_api_key'], + ui: [], + }, + ], +}; + +const roleMappingsManagementFeature: ElasticsearchFeatureConfig = { + id: 'role_mappings', + management: { + security: ['role_mappings'], + }, + catalogue: ['security'], + privileges: [ + { + requiredClusterPrivileges: ['manage_security'], + ui: [], + }, + ], +}; + +export const securityFeatures = [ + userManagementFeature, + rolesManagementFeature, + apiKeysManagementFeature, + roleMappingsManagementFeature, +]; diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index 8d13f8107571..9088d4f08d0e 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -11,6 +11,7 @@ import { ConfigSchema } from './config'; import { Plugin, PluginSetupDependencies } from './plugin'; import { coreMock, elasticsearchServiceMock } from '../../../../src/core/server/mocks'; +import { featuresPluginMock } from '../../features/server/mocks'; import { taskManagerMock } from '../../task_manager/server/mocks'; describe('Security Plugin', () => { @@ -44,6 +45,7 @@ describe('Security Plugin', () => { mockDependencies = ({ licensing: { license$: of({}), featureUsage: { register: jest.fn() } }, + features: featuresPluginMock.createSetup(), taskManager: taskManagerMock.createSetup(), } as unknown) as PluginSetupDependencies; }); @@ -108,6 +110,7 @@ describe('Security Plugin', () => { }, "getFeatures": [Function], "isEnabled": [Function], + "isLicenseAvailable": [Function], }, "registerSpacesService": [Function], } diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 7d94e03916fa..dc9139473004 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -7,6 +7,7 @@ import { combineLatest } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { TypeOf } from '@kbn/config-schema'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { deepFreeze, CoreSetup, @@ -15,6 +16,7 @@ import { PluginInitializerContext, } from '../../../../src/core/server'; import { SpacesPluginSetup } from '../../spaces/server'; +import { PluginSetupContract as FeaturesSetupContract } from '../../features/server'; import { PluginSetupContract as FeaturesPluginSetup, PluginStartContract as FeaturesPluginStart, @@ -30,14 +32,21 @@ import { SecurityLicenseService, SecurityLicense } from '../common/licensing'; import { setupSavedObjects } from './saved_objects'; import { AuditService, SecurityAuditLogger, AuditServiceSetup } from './audit'; import { SecurityFeatureUsageService, SecurityFeatureUsageServiceStart } from './feature_usage'; +import { securityFeatures } from './features'; import { ElasticsearchService } from './elasticsearch'; import { SessionManagementService } from './session_management'; +import { registerSecurityUsageCollector } from './usage_collector'; export type SpacesService = Pick< SpacesPluginSetup['spacesService'], 'getSpaceId' | 'namespaceToSpaceId' >; +export type FeaturesService = Pick< + FeaturesSetupContract, + 'getKibanaFeatures' | 'getElasticsearchFeatures' +>; + /** * Describes public Security plugin contract returned at the `setup` stage. */ @@ -74,6 +83,7 @@ export interface PluginSetupDependencies { features: FeaturesPluginSetup; licensing: LicensingPluginSetup; taskManager: TaskManagerSetupContract; + usageCollection?: UsageCollectionSetup; } export interface PluginStartDependencies { @@ -123,7 +133,7 @@ export class Plugin { public async setup( core: CoreSetup, - { features, licensing, taskManager }: PluginSetupDependencies + { features, licensing, taskManager, usageCollection }: PluginSetupDependencies ) { const [config, legacyConfig] = await combineLatest([ this.initializerContext.config.create>().pipe( @@ -143,6 +153,10 @@ export class Plugin { license$: licensing.license$, }); + securityFeatures.forEach((securityFeature) => + features.registerElasticsearchFeature(securityFeature) + ); + const { clusterClient } = this.elasticsearchService.setup({ elasticsearch: core.elasticsearch, license, @@ -151,6 +165,8 @@ export class Plugin { this.featureUsageService.setup({ featureUsage: licensing.featureUsage }); + registerSecurityUsageCollector({ usageCollection, config, license }); + const audit = this.auditService.setup({ license, config: config.audit }); const auditLogger = new SecurityAuditLogger(audit.getLogger()); @@ -183,6 +199,7 @@ export class Plugin { packageVersion: this.initializerContext.env.packageInfo.version, getSpacesService: this.getSpacesService, features, + getCurrentUser: authc.getCurrentUser, }); setupSavedObjects({ @@ -206,7 +223,7 @@ export class Plugin { getFeatures: () => core .getStartServices() - .then(([, { features: featuresStart }]) => featuresStart.getFeatures()), + .then(([, { features: featuresStart }]) => featuresStart.getKibanaFeatures()), getFeatureUsageService: this.getFeatureUsageService, }); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts index 8f115f11329d..6e9b88f30479 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.test.ts @@ -15,7 +15,7 @@ import { httpServerMock, } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; -import { Feature } from '../../../../../features/server'; +import { KibanaFeature } from '../../../../../features/server'; import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock'; const application = 'kibana-.kibana'; @@ -83,7 +83,7 @@ const putRoleTest = ( ); mockRouteDefinitionParams.getFeatures.mockResolvedValue([ - new Feature({ + new KibanaFeature({ id: 'feature_1', name: 'feature 1', app: [], diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.ts index d83cf92bcaa0..cdedc9ac8a5e 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.ts @@ -5,7 +5,7 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { Feature } from '../../../../../features/common'; +import { KibanaFeature } from '../../../../../features/common'; import { RouteDefinitionParams } from '../../index'; import { createLicensedRouteHandler } from '../../licensed_route_handler'; import { wrapIntoCustomErrorResponse } from '../../../errors'; @@ -16,7 +16,7 @@ import { } from './model'; const roleGrantsSubFeaturePrivileges = ( - features: Feature[], + features: KibanaFeature[], role: TypeOf> ) => { if (!role.kibana) { @@ -77,7 +77,7 @@ export function definePutRolesRoutes({ rawRoles[name] ? rawRoles[name].applications : [] ); - const [features] = await Promise.all([ + const [features] = await Promise.all([ getFeatures(), clusterClient .asScoped(request) diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index a3f046ae4f9e..7880e95240ff 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; import { HttpResources, IBasePath, @@ -42,7 +42,7 @@ export interface RouteDefinitionParams { authz: AuthorizationServiceSetup; session: PublicMethodsOf; license: SecurityLicense; - getFeatures: () => Promise; + getFeatures: () => Promise; getFeatureUsageService: () => SecurityFeatureUsageServiceStart; } diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index 7f7f969e8b48..7ada34ff5cca 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -117,7 +117,11 @@ const expectSuccess = async (fn: Function, args: Record, action?: s return result; }; -const expectPrivilegeCheck = async (fn: Function, args: Record) => { +const expectPrivilegeCheck = async ( + fn: Function, + args: Record, + namespacesOverride?: Array +) => { clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( getMockCheckPrivilegesFailure ); @@ -131,7 +135,7 @@ const expectPrivilegeCheck = async (fn: Function, args: Record) => expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(1); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledWith( actions, - args.options?.namespace ?? args.options?.namespaces + namespacesOverride ?? args.options?.namespace ?? args.options?.namespaces ); }; @@ -218,15 +222,17 @@ function getMockCheckPrivilegesSuccess(actions: string | string[], namespaces?: return { hasAllRequested: true, username: USERNAME, - privileges: _namespaces - .map((resource) => - _actions.map((action) => ({ - resource, - privilege: action, - authorized: true, - })) - ) - .flat(), + privileges: { + kibana: _namespaces + .map((resource) => + _actions.map((action) => ({ + resource, + privilege: action, + authorized: true, + })) + ) + .flat(), + }, }; } @@ -242,15 +248,17 @@ function getMockCheckPrivilegesFailure(actions: string | string[], namespaces?: return { hasAllRequested: false, username: USERNAME, - privileges: _namespaces - .map((resource, idxa) => - _actions.map((action, idxb) => ({ - resource, - privilege: action, - authorized: idxa > 0 || idxb > 0, - })) - ) - .flat(), + privileges: { + kibana: _namespaces + .map((resource, idxa) => + _actions.map((action, idxb) => ({ + resource, + privilege: action, + authorized: idxa > 0 || idxb > 0, + })) + ) + .flat(), + }, }; } @@ -483,7 +491,18 @@ describe('#bulkUpdate', () => { test(`checks privileges for user, actions, and namespace`, async () => { const objects = [obj1, obj2]; - await expectPrivilegeCheck(client.bulkUpdate, { objects, options }); + const namespacesOverride = [options.namespace]; // the bulkCreate function checks privileges as an array + await expectPrivilegeCheck(client.bulkUpdate, { objects, options }, namespacesOverride); + }); + + test(`checks privileges for object namespaces if present`, async () => { + const objects = [ + { ...obj1, namespace: 'foo-ns' }, + { ...obj2, namespace: 'bar-ns' }, + ]; + const namespacesOverride = [undefined, 'foo-ns', 'bar-ns']; + // use the default namespace for the options + await expectPrivilegeCheck(client.bulkUpdate, { objects, options: {} }, namespacesOverride); }); test(`filters namespaces that the user doesn't have access to`, async () => { diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts index 68fe65d204d6..16e52c69f274 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts @@ -19,7 +19,7 @@ import { } from '../../../../../src/core/server'; import { SecurityAuditLogger } from '../audit'; import { Actions, CheckSavedObjectsPrivileges } from '../authorization'; -import { CheckPrivilegesResponse } from '../authorization/check_privileges'; +import { CheckPrivilegesResponse } from '../authorization/types'; import { SpacesService } from '../plugin'; interface SecureSavedObjectsClientWrapperOptions { @@ -199,12 +199,16 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra objects: Array> = [], options: SavedObjectsBaseOptions = {} ) { - await this.ensureAuthorized( - this.getUniqueObjectTypes(objects), - 'bulk_update', - options && options.namespace, - { objects, options } - ); + const objectNamespaces = objects + // The repository treats an `undefined` object namespace is treated as the absence of a namespace, falling back to options.namespace; + // in this case, filter it out here so we don't accidentally check for privileges in the Default space when we shouldn't be doing so. + .filter(({ namespace }) => namespace !== undefined) + .map(({ namespace }) => namespace!); + const namespaces = [options?.namespace, ...objectNamespaces]; + await this.ensureAuthorized(this.getUniqueObjectTypes(objects), 'bulk_update', namespaces, { + objects, + options, + }); const response = await this.baseClient.bulkUpdate(objects, options); return await this.redactSavedObjectsNamespaces(response); @@ -212,7 +216,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra private async checkPrivileges( actions: string | string[], - namespaceOrNamespaces?: string | string[] + namespaceOrNamespaces?: string | Array ) { try { return await this.checkSavedObjectsPrivilegesAsCurrentUser(actions, namespaceOrNamespaces); @@ -224,7 +228,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra private async ensureAuthorized( typeOrTypes: string | string[], action: string, - namespaceOrNamespaces?: string | string[], + namespaceOrNamespaces?: string | Array, args?: Record, auditAction: string = action, requiresAll = true @@ -238,12 +242,12 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra const { hasAllRequested, username, privileges } = result; const spaceIds = uniq( - privileges.map(({ resource }) => resource).filter((x) => x !== undefined) + privileges.kibana.map(({ resource }) => resource).filter((x) => x !== undefined) ).sort() as string[]; const isAuthorized = (requiresAll && hasAllRequested) || - (!requiresAll && privileges.some(({ authorized }) => authorized)); + (!requiresAll && privileges.kibana.some(({ authorized }) => authorized)); if (isAuthorized) { this.auditLogger.savedObjectsAuthorizationSuccess( username, @@ -271,7 +275,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra } private getMissingPrivileges(privileges: CheckPrivilegesResponse['privileges']) { - return privileges + return privileges.kibana .filter(({ authorized }) => !authorized) .map(({ resource, privilege }) => ({ spaceId: resource, privilege })); } @@ -284,7 +288,7 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra const action = this.actions.login; const checkPrivilegesResult = await this.checkPrivileges(action, namespaces); // check if the user can log into each namespace - const map = checkPrivilegesResult.privileges.reduce( + const map = checkPrivilegesResult.privileges.kibana.reduce( (acc: Record, { resource, authorized }) => { // there should never be a case where more than one privilege is returned for a given space // if there is, fail-safe (authorized + unauthorized = unauthorized) diff --git a/x-pack/plugins/security/server/usage_collector/index.ts b/x-pack/plugins/security/server/usage_collector/index.ts new file mode 100644 index 000000000000..dd405ebac424 --- /dev/null +++ b/x-pack/plugins/security/server/usage_collector/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerSecurityUsageCollector } from './security_usage_collector'; diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts new file mode 100644 index 000000000000..6c3dcddcdb41 --- /dev/null +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts @@ -0,0 +1,465 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createConfig, ConfigSchema } from '../config'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { TypeOf } from '@kbn/config-schema'; +import { usageCollectionPluginMock } from 'src/plugins/usage_collection/server/mocks'; +import { registerSecurityUsageCollector } from './security_usage_collector'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; +import { licenseMock } from '../../common/licensing/index.mock'; +import { SecurityLicenseFeatures } from '../../common/licensing'; + +describe('Security UsageCollector', () => { + const createSecurityConfig = (config: TypeOf) => { + return createConfig(config, loggingSystemMock.createLogger(), { isTLSEnabled: true }); + }; + + const createSecurityLicense = ({ + allowAccessAgreement = true, + allowAuditLogging = true, + allowRbac = true, + isLicenseAvailable, + }: Partial & { isLicenseAvailable: boolean }) => { + const license = licenseMock.create(); + license.isLicenseAvailable.mockReturnValue(isLicenseAvailable); + license.getFeatures.mockReturnValue({ + allowAccessAgreement, + allowAuditLogging, + allowRbac, + } as SecurityLicenseFeatures); + return license; + }; + + const clusterClient = elasticsearchServiceMock.createLegacyClusterClient(); + + describe('initialization', () => { + it('handles an undefined usage collector', () => { + const config = createSecurityConfig(ConfigSchema.validate({})); + const usageCollection = undefined; + const license = createSecurityLicense({ allowRbac: false, isLicenseAvailable: false }); + registerSecurityUsageCollector({ usageCollection, config, license }); + }); + + it('registers itself and waits for the license to become available before reporting itself as ready', async () => { + const config = createSecurityConfig(ConfigSchema.validate({})); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ allowRbac: false, isLicenseAvailable: false }); + + registerSecurityUsageCollector({ usageCollection, config, license }); + + expect(usageCollection.getCollectorByType('security')?.isReady()).toBe(false); + + license.isLicenseAvailable.mockReturnValue(true); + license.getFeatures.mockReturnValue({ allowRbac: true } as SecurityLicenseFeatures); + + expect(usageCollection.getCollectorByType('security')?.isReady()).toBe(true); + }); + }); + + it('reports correctly for a default configuration', async () => { + const config = createSecurityConfig(ConfigSchema.validate({})); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + + it('reports correctly when security is disabled in Elasticsearch', async () => { + const config = createSecurityConfig(ConfigSchema.validate({})); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ allowRbac: false, isLicenseAvailable: true }); + + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 0, + enabledAuthProviders: [], + loginSelectorEnabled: false, + httpAuthSchemes: [], + }); + }); + + describe('auth providers', () => { + it('does not report disabled auth providers', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + providers: { + basic: { + basic: { + order: 0, + }, + disabledBasic: { + enabled: false, + order: 1, + }, + }, + saml: { + disabledSaml: { + enabled: false, + realm: 'foo', + order: 2, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + + it('reports the types and count of enabled auth providers', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + providers: { + basic: { + basic: { + order: 0, + enabled: false, + }, + }, + saml: { + saml1: { + realm: 'foo', + order: 1, + }, + saml2: { + realm: 'bar', + order: 2, + }, + }, + pki: { + pki1: { + enabled: true, + order: 3, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 3, + enabledAuthProviders: ['saml', 'pki'], + loginSelectorEnabled: true, + httpAuthSchemes: ['apikey'], + }); + }); + }); + + describe('access agreement', () => { + it('reports if the access agreement message is configured for any provider', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + providers: { + saml: { + saml1: { + realm: 'foo', + order: 1, + accessAgreement: { + message: 'foo message', + }, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: true, + authProviderCount: 1, + enabledAuthProviders: ['saml'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + it('does not report the access agreement if the license does not permit it', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + providers: { + saml: { + saml1: { + realm: 'foo', + order: 1, + accessAgreement: { + message: 'foo message', + }, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ + isLicenseAvailable: true, + allowAccessAgreement: false, + }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['saml'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + + it('does not report the access agreement for disabled providers', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + providers: { + saml: { + saml1: { + enabled: false, + realm: 'foo', + order: 1, + accessAgreement: { + message: 'foo message', + }, + }, + saml2: { + realm: 'foo', + order: 2, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['saml'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + }); + + describe('login selector', () => { + it('reports when the login selector is enabled', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + selector: { + enabled: true, + }, + providers: { + saml: { + saml1: { + realm: 'foo', + order: 1, + showInSelector: true, + }, + }, + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['saml'], + loginSelectorEnabled: true, + httpAuthSchemes: ['apikey'], + }); + }); + }); + + describe('audit logging', () => { + it('reports when audit logging is enabled', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + audit: { + enabled: true, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true, allowAuditLogging: true }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: true, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + + it('does not report audit logging when the license does not permit it', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + audit: { + enabled: true, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true, allowAuditLogging: false }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['apikey'], + }); + }); + }); + + describe('http auth schemes', () => { + it('reports customized http auth schemes', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + http: { + schemes: ['basic', 'Negotiate'], + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true, allowAuditLogging: false }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['basic', 'Negotiate'], + }); + }); + + it('does not report auth schemes that are not "well known"', async () => { + const config = createSecurityConfig( + ConfigSchema.validate({ + authc: { + http: { + schemes: ['basic', 'Negotiate', 'customScheme'], + }, + }, + }) + ); + const usageCollection = usageCollectionPluginMock.createSetupContract(); + const license = createSecurityLicense({ isLicenseAvailable: true, allowAuditLogging: false }); + registerSecurityUsageCollector({ usageCollection, config, license }); + + const usage = await usageCollection + .getCollectorByType('security') + ?.fetch(clusterClient.asScoped().callAsCurrentUser); + + expect(usage).toEqual({ + auditLoggingEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 1, + enabledAuthProviders: ['basic'], + loginSelectorEnabled: false, + httpAuthSchemes: ['basic', 'Negotiate'], + }); + }); + }); +}); diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts new file mode 100644 index 000000000000..11e58f7f95fc --- /dev/null +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { ConfigType } from '../config'; +import { SecurityLicense } from '../../common/licensing'; + +interface Usage { + auditLoggingEnabled: boolean; + loginSelectorEnabled: boolean; + accessAgreementEnabled: boolean; + authProviderCount: number; + enabledAuthProviders: string[]; + httpAuthSchemes: string[]; +} + +interface Deps { + usageCollection?: UsageCollectionSetup; + config: ConfigType; + license: SecurityLicense; +} + +// List of auth schemes collected from https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml +const WELL_KNOWN_AUTH_SCHEMES = [ + 'basic', + 'bearer', + 'digest', + 'hoba', + 'mutual', + 'negotiate', + 'oauth', + 'scram-sha-1', + 'scram-sha-256', + 'vapid', + 'apikey', // not part of the spec, but used by the Elastic Stack for API Key authentication +]; + +export function registerSecurityUsageCollector({ usageCollection, config, license }: Deps): void { + // usageCollection is an optional dependency, so make sure to return if it is not registered. + if (!usageCollection) { + return; + } + + // create usage collector + const securityCollector = usageCollection.makeUsageCollector({ + type: 'security', + isReady: () => license.isLicenseAvailable(), + schema: { + auditLoggingEnabled: { + type: 'boolean', + }, + loginSelectorEnabled: { + type: 'boolean', + }, + accessAgreementEnabled: { + type: 'boolean', + }, + authProviderCount: { + type: 'number', + }, + enabledAuthProviders: { + type: 'keyword', + }, + httpAuthSchemes: { + type: 'keyword', + }, + }, + fetch: () => { + const { allowRbac, allowAccessAgreement, allowAuditLogging } = license.getFeatures(); + if (!allowRbac) { + return { + auditLoggingEnabled: false, + loginSelectorEnabled: false, + accessAgreementEnabled: false, + authProviderCount: 0, + enabledAuthProviders: [], + httpAuthSchemes: [], + }; + } + + const auditLoggingEnabled = allowAuditLogging && config.audit.enabled; + const loginSelectorEnabled = config.authc.selector.enabled; + const authProviderCount = config.authc.sortedProviders.length; + const enabledAuthProviders = [ + ...new Set( + config.authc.sortedProviders.reduce( + (acc, provider) => [...acc, provider.type], + [] as string[] + ) + ), + ]; + const accessAgreementEnabled = + allowAccessAgreement && + config.authc.sortedProviders.some((provider) => provider.hasAccessAgreement); + + const httpAuthSchemes = config.authc.http.schemes.filter((scheme) => + WELL_KNOWN_AUTH_SCHEMES.includes(scheme.toLowerCase()) + ); + + return { + auditLoggingEnabled, + loginSelectorEnabled, + accessAgreementEnabled, + authProviderCount, + enabledAuthProviders, + httpAuthSchemes, + }; + }, + }); + + // register usage collector + usageCollection.registerCollector(securityCollector); +} diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts index be3a1e82356c..7e3b3d125fb5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts @@ -13,6 +13,12 @@ import { ECSCategory, ANCESTRY_LIMIT, } from './generate_data'; +import { firstNonNullValue, values } from './models/ecs_safety_helpers'; +import { + entityIDSafeVersion, + parentEntityIDSafeVersion, + timestampSafeVersion, +} from './models/event'; interface Node { events: Event[]; @@ -30,7 +36,7 @@ describe('data generator', () => { const event1 = generator.generateEvent(); const event2 = generator.generateEvent(); - expect(event2.event.sequence).toBe(event1.event.sequence + 1); + expect(event2.event?.sequence).toBe((firstNonNullValue(event1.event?.sequence) ?? 0) + 1); }); it('creates the same documents with same random seed', () => { @@ -76,37 +82,37 @@ describe('data generator', () => { const timestamp = new Date().getTime(); const alert = generator.generateAlert(timestamp); expect(alert['@timestamp']).toEqual(timestamp); - expect(alert.event.action).not.toBeNull(); + expect(alert.event?.action).not.toBeNull(); expect(alert.Endpoint).not.toBeNull(); expect(alert.agent).not.toBeNull(); expect(alert.host).not.toBeNull(); - expect(alert.process.entity_id).not.toBeNull(); + expect(alert.process?.entity_id).not.toBeNull(); }); it('creates process event documents', () => { const timestamp = new Date().getTime(); const processEvent = generator.generateEvent({ timestamp }); expect(processEvent['@timestamp']).toEqual(timestamp); - expect(processEvent.event.category).toEqual(['process']); - expect(processEvent.event.kind).toEqual('event'); - expect(processEvent.event.type).toEqual(['start']); + expect(processEvent.event?.category).toEqual(['process']); + expect(processEvent.event?.kind).toEqual('event'); + expect(processEvent.event?.type).toEqual(['start']); expect(processEvent.agent).not.toBeNull(); expect(processEvent.host).not.toBeNull(); - expect(processEvent.process.entity_id).not.toBeNull(); - expect(processEvent.process.name).not.toBeNull(); + expect(processEvent.process?.entity_id).not.toBeNull(); + expect(processEvent.process?.name).not.toBeNull(); }); it('creates other event documents', () => { const timestamp = new Date().getTime(); const processEvent = generator.generateEvent({ timestamp, eventCategory: 'dns' }); expect(processEvent['@timestamp']).toEqual(timestamp); - expect(processEvent.event.category).toEqual('dns'); - expect(processEvent.event.kind).toEqual('event'); - expect(processEvent.event.type).toEqual(['start']); + expect(processEvent.event?.category).toEqual('dns'); + expect(processEvent.event?.kind).toEqual('event'); + expect(processEvent.event?.type).toEqual(['start']); expect(processEvent.agent).not.toBeNull(); expect(processEvent.host).not.toBeNull(); - expect(processEvent.process.entity_id).not.toBeNull(); - expect(processEvent.process.name).not.toBeNull(); + expect(processEvent.process?.entity_id).not.toBeNull(); + expect(processEvent.process?.name).not.toBeNull(); }); describe('creates events with an empty ancestry array', () => { @@ -128,7 +134,7 @@ describe('data generator', () => { it('creates all events with an empty ancestry array', () => { for (const event of tree.allEvents) { - expect(event.process.Ext!.ancestry!.length).toEqual(0); + expect(event.process?.Ext?.ancestry?.length).toEqual(0); } }); }); @@ -194,22 +200,23 @@ describe('data generator', () => { const inRelated = node.relatedEvents.includes(event); const inRelatedAlerts = node.relatedAlerts.includes(event); - return (inRelated || inRelatedAlerts || inLifecycle) && event.process.entity_id === node.id; + return (inRelated || inRelatedAlerts || inLifecycle) && event.process?.entity_id === node.id; }; const verifyAncestry = (event: Event, genTree: Tree) => { - if (event.process.Ext!.ancestry!.length > 0) { - expect(event.process.parent?.entity_id).toBe(event.process.Ext!.ancestry![0]); + const ancestry = values(event.process?.Ext?.ancestry); + if (ancestry.length > 0) { + expect(event.process?.parent?.entity_id).toBe(ancestry[0]); } - for (let i = 0; i < event.process.Ext!.ancestry!.length; i++) { - const ancestor = event.process.Ext!.ancestry![i]; + for (let i = 0; i < ancestry.length; i++) { + const ancestor = ancestry[i]; const parent = genTree.children.get(ancestor) || genTree.ancestry.get(ancestor); - expect(ancestor).toBe(parent?.lifecycle[0].process.entity_id); + expect(ancestor).toBe(parent?.lifecycle[0].process?.entity_id); // the next ancestor should be the grandparent - if (i + 1 < event.process.Ext!.ancestry!.length) { - const grandparent = event.process.Ext!.ancestry![i + 1]; - expect(grandparent).toBe(parent?.lifecycle[0].process.parent?.entity_id); + if (i + 1 < ancestry.length) { + const grandparent = ancestry[i + 1]; + expect(grandparent).toBe(parent?.lifecycle[0].process?.parent?.entity_id); } } }; @@ -217,13 +224,14 @@ describe('data generator', () => { it('creates related events in ascending order', () => { // the order should not change since it should already be in ascending order const relatedEventsAsc = _.cloneDeep(tree.origin.relatedEvents).sort( - (event1, event2) => event1['@timestamp'] - event2['@timestamp'] + (event1, event2) => + (timestampSafeVersion(event1) ?? 0) - (timestampSafeVersion(event2) ?? 0) ); expect(tree.origin.relatedEvents).toStrictEqual(relatedEventsAsc); }); it('has ancestry array defined', () => { - expect(tree.origin.lifecycle[0].process.Ext!.ancestry!.length).toBe(ANCESTRY_LIMIT); + expect(values(tree.origin.lifecycle[0].process?.Ext?.ancestry).length).toBe(ANCESTRY_LIMIT); for (const event of tree.allEvents) { verifyAncestry(event, tree); } @@ -252,12 +260,9 @@ describe('data generator', () => { const counts: Record = {}; for (const event of node.relatedEvents) { - if (Array.isArray(event.event.category)) { - for (const cat of event.event.category) { - counts[cat] = counts[cat] + 1 || 1; - } - } else { - counts[event.event.category] = counts[event.event.category] + 1 || 1; + const categories = values(event.event?.category); + for (const cat of categories) { + counts[cat] = counts[cat] + 1 || 1; } } expect(counts[ECSCategory.Driver]).toEqual(1); @@ -316,15 +321,18 @@ describe('data generator', () => { expect(tree.allEvents.length).toBeGreaterThan(0); tree.allEvents.forEach((event) => { - const ancestor = tree.ancestry.get(event.process.entity_id); - if (ancestor) { - expect(eventInNode(event, ancestor)).toBeTruthy(); - return; - } + const entityID = entityIDSafeVersion(event); + if (entityID) { + const ancestor = tree.ancestry.get(entityID); + if (ancestor) { + expect(eventInNode(event, ancestor)).toBeTruthy(); + return; + } - const children = tree.children.get(event.process.entity_id); - if (children) { - expect(eventInNode(event, children)).toBeTruthy(); + const children = tree.children.get(entityID); + if (children) { + expect(eventInNode(event, children)).toBeTruthy(); + } } }); }); @@ -351,9 +359,8 @@ describe('data generator', () => { let events: Event[]; const isCategoryProcess = (event: Event) => { - return ( - _.isEqual(event.event.category, ['process']) || _.isEqual(event.event.category, 'process') - ); + const category = values(event.event?.category); + return _.isEqual(category, ['process']); }; beforeEach(() => { @@ -366,12 +373,16 @@ describe('data generator', () => { it('with n-1 process events', () => { for (let i = events.length - 2; i > 0; ) { - const parentEntityIdOfChild = events[i].process.parent?.entity_id; - for (; --i >= -1 && (events[i].event.kind !== 'event' || !isCategoryProcess(events[i])); ) { + const parentEntityIdOfChild = parentEntityIDSafeVersion(events[i]); + for ( + ; + --i >= -1 && (events[i].event?.kind !== 'event' || !isCategoryProcess(events[i])); + + ) { // related event - skip it } expect(i).toBeGreaterThanOrEqual(0); - expect(parentEntityIdOfChild).toEqual(events[i].process.entity_id); + expect(parentEntityIdOfChild).toEqual(entityIDSafeVersion(events[i])); } }); @@ -380,7 +391,7 @@ describe('data generator', () => { for ( ; previousProcessEventIndex >= -1 && - (events[previousProcessEventIndex].event.kind !== 'event' || + (events[previousProcessEventIndex].event?.kind !== 'event' || !isCategoryProcess(events[previousProcessEventIndex])); previousProcessEventIndex-- ) { @@ -388,14 +399,14 @@ describe('data generator', () => { } expect(previousProcessEventIndex).toBeGreaterThanOrEqual(0); // The alert should be last and have the same entity_id as the previous process event - expect(events[events.length - 1].process.entity_id).toEqual( - events[previousProcessEventIndex].process.entity_id + expect(events[events.length - 1].process?.entity_id).toEqual( + events[previousProcessEventIndex].process?.entity_id ); - expect(events[events.length - 1].process.parent?.entity_id).toEqual( - events[previousProcessEventIndex].process.parent?.entity_id + expect(events[events.length - 1].process?.parent?.entity_id).toEqual( + events[previousProcessEventIndex].process?.parent?.entity_id ); - expect(events[events.length - 1].event.kind).toEqual('alert'); - expect(events[events.length - 1].event.category).toEqual('malware'); + expect(events[events.length - 1].event?.kind).toEqual('alert'); + expect(events[events.length - 1].event?.category).toEqual('malware'); }); }); @@ -403,14 +414,17 @@ describe('data generator', () => { // First pass we gather up all the events by entity_id const tree: Record = {}; events.forEach((event) => { - if (event.process.entity_id in tree) { - tree[event.process.entity_id].events.push(event); - } else { - tree[event.process.entity_id] = { - events: [event], - children: [], - parent_entity_id: event.process.parent?.entity_id, - }; + const entityID = entityIDSafeVersion(event); + if (entityID) { + if (entityID in tree) { + tree[entityID].events.push(event); + } else { + tree[entityID] = { + events: [event], + children: [], + parent_entity_id: parentEntityIDSafeVersion(event), + }; + } } }); // Second pass add child references to each node @@ -419,8 +433,14 @@ describe('data generator', () => { tree[value.parent_entity_id].children.push(value); } } + + const entityID = entityIDSafeVersion(events[0]); + if (!entityID) { + throw new Error('entity id was invalid'); + } + // The root node must be first in the array or this fails - return tree[events[0].process.entity_id]; + return tree[entityID]; } function countResolverEvents(rootNode: Node, generations: number): number { diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index e1ff34463d21..7f31c71fe712 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -7,7 +7,6 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; import { AlertEvent, - EndpointEvent, EndpointStatus, Host, HostMetadata, @@ -15,9 +14,15 @@ import { HostPolicyResponseActionStatus, OSFields, PolicyData, + SafeEndpointEvent, } from './types'; import { factory as policyFactory } from './models/policy_config'; -import { parentEntityId } from './models/event'; +import { + ancestryArray, + entityIDSafeVersion, + parentEntityIDSafeVersion, + timestampSafeVersion, +} from './models/event'; import { GetAgentPoliciesResponseItem, GetPackagesResponse, @@ -28,8 +33,9 @@ import { InstallationStatus, KibanaAssetReference, } from '../../../ingest_manager/common/types/models'; +import { firstNonNullValue } from './models/ecs_safety_helpers'; -export type Event = AlertEvent | EndpointEvent; +export type Event = AlertEvent | SafeEndpointEvent; /** * This value indicates the limit for the size of the ancestry array. The endpoint currently saves up to 20 values * in its messages. To simulate a limit on the array size I'm using 2 here so that we can't rely on there being a large @@ -426,13 +432,13 @@ export class EndpointDocGenerator { * @param ts - Timestamp to put in the event * @param entityID - entityID of the originating process * @param parentEntityID - optional entityID of the parent process, if it exists - * @param ancestryArray - an array of ancestors for the generated alert + * @param ancestry - an array of ancestors for the generated alert */ public generateAlert( ts = new Date().getTime(), entityID = this.randomString(10), parentEntityID?: string, - ancestryArray: string[] = [] + ancestry: string[] = [] ): AlertEvent { return { ...this.commonInfo, @@ -493,7 +499,7 @@ export class EndpointDocGenerator { sha256: 'fake sha256', }, Ext: { - ancestry: ancestryArray, + ancestry, code_signature: [ { trusted: false, @@ -555,7 +561,7 @@ export class EndpointDocGenerator { * Creates an event, customized by the options parameter * @param options - Allows event field values to be specified */ - public generateEvent(options: EventOptions = {}): EndpointEvent { + public generateEvent(options: EventOptions = {}): Event { // this will default to an empty array for the ancestry field if options.ancestry isn't included const ancestry: string[] = options.ancestry?.slice(0, options?.ancestryArrayLimit ?? ANCESTRY_LIMIT) ?? []; @@ -643,7 +649,11 @@ export class EndpointDocGenerator { public generateTree(options: TreeOptions = {}): Tree { const optionsWithDef = getTreeOptionsWithDef(options); const addEventToMap = (nodeMap: Map, event: Event) => { - const nodeId = event.process.entity_id; + const nodeId = entityIDSafeVersion(event); + if (!nodeId) { + return nodeMap; + } + // if a node already exists for the entity_id we'll use that one, otherwise let's create a new empty node // and add the event to the right array. let node = nodeMap.get(nodeId); @@ -652,18 +662,13 @@ export class EndpointDocGenerator { } // place the event in the right array depending on its category - if (event.event.kind === 'event') { - if ( - (Array.isArray(event.event.category) && - event.event.category.length === 1 && - event.event.category[0] === 'process') || - event.event.category === 'process' - ) { + if (firstNonNullValue(event.event?.kind) === 'event') { + if (firstNonNullValue(event.event?.category) === 'process') { node.lifecycle.push(event); } else { node.relatedEvents.push(event); } - } else if (event.event.kind === 'alert') { + } else if (firstNonNullValue(event.event?.kind) === 'alert') { node.relatedAlerts.push(event); } @@ -673,7 +678,7 @@ export class EndpointDocGenerator { const groupNodesByParent = (children: Map) => { const nodesByParent: Map> = new Map(); for (const node of children.values()) { - const parentID = parentEntityId(node.lifecycle[0]); + const parentID = parentEntityIDSafeVersion(node.lifecycle[0]); if (parentID) { let groupedNodes = nodesByParent.get(parentID); @@ -715,9 +720,13 @@ export class EndpointDocGenerator { const ancestryNodes: Map = ancestry.reduce(addEventToMap, new Map()); const alert = ancestry[ancestry.length - 1]; - const origin = ancestryNodes.get(alert.process.entity_id); + const alertEntityID = entityIDSafeVersion(alert); + if (!alertEntityID) { + throw Error("could not find the originating alert's entity id"); + } + const origin = ancestryNodes.get(alertEntityID); if (!origin) { - throw Error(`could not find origin while building tree: ${alert.process.entity_id}`); + throw Error(`could not find origin while building tree: ${alertEntityID}`); } const children = Array.from(this.descendantsTreeGenerator(alert, optionsWithDef)); @@ -799,7 +808,7 @@ export class EndpointDocGenerator { }); events.push(root); let ancestor = root; - let timestamp = root['@timestamp'] + 1000; + let timestamp = (timestampSafeVersion(root) ?? 0) + 1000; const addRelatedAlerts = ( node: Event, @@ -836,8 +845,8 @@ export class EndpointDocGenerator { events.push( this.generateEvent({ timestamp: timestamp + termProcessDuration * 1000, - entityID: root.process.entity_id, - parentEntityID: root.process.parent?.entity_id, + entityID: entityIDSafeVersion(root), + parentEntityID: parentEntityIDSafeVersion(root), eventCategory: ['process'], eventType: ['end'], }) @@ -845,13 +854,20 @@ export class EndpointDocGenerator { } for (let i = 0; i < opts.ancestors; i++) { + const ancestorEntityID = entityIDSafeVersion(ancestor); + const ancestry: string[] = []; + if (ancestorEntityID) { + ancestry.push(ancestorEntityID); + } + + ancestry.push(...(ancestryArray(ancestor) ?? [])); ancestor = this.generateEvent({ timestamp, - parentEntityID: ancestor.process.entity_id, + parentEntityID: entityIDSafeVersion(ancestor), // add the parent to the ancestry array - ancestry: [ancestor.process.entity_id, ...(ancestor.process.Ext?.ancestry ?? [])], + ancestry, ancestryArrayLimit: opts.ancestryArraySize, - parentPid: ancestor.process.pid, + parentPid: firstNonNullValue(ancestor.process?.pid), pid: this.randomN(5000), }); events.push(ancestor); @@ -862,11 +878,11 @@ export class EndpointDocGenerator { events.push( this.generateEvent({ timestamp: timestamp + termProcessDuration * 1000, - entityID: ancestor.process.entity_id, - parentEntityID: ancestor.process.parent?.entity_id, + entityID: entityIDSafeVersion(ancestor), + parentEntityID: parentEntityIDSafeVersion(ancestor), eventCategory: ['process'], eventType: ['end'], - ancestry: ancestor.process.Ext?.ancestry, + ancestry: ancestryArray(ancestor), ancestryArrayLimit: opts.ancestryArraySize, }) ); @@ -890,9 +906,9 @@ export class EndpointDocGenerator { events.push( this.generateAlert( timestamp, - ancestor.process.entity_id, - ancestor.process.parent?.entity_id, - ancestor.process.Ext?.ancestry + entityIDSafeVersion(ancestor), + parentEntityIDSafeVersion(ancestor), + ancestryArray(ancestor) ) ); return events; @@ -922,7 +938,7 @@ export class EndpointDocGenerator { maxChildren, }; const lineage: NodeState[] = [rootState]; - let timestamp = root['@timestamp']; + let timestamp = timestampSafeVersion(root) ?? 0; while (lineage.length > 0) { const currentState = lineage[lineage.length - 1]; // If we get to a state node and it has made all the children, move back up a level @@ -937,13 +953,17 @@ export class EndpointDocGenerator { // Otherwise, add a child and any nodes associated with it currentState.childrenCreated++; timestamp = timestamp + 1000; + const currentStateEntityID = entityIDSafeVersion(currentState.event); + const ancestry: string[] = []; + if (currentStateEntityID) { + ancestry.push(currentStateEntityID); + } + ancestry.push(...(ancestryArray(currentState.event) ?? [])); + const child = this.generateEvent({ timestamp, - parentEntityID: currentState.event.process.entity_id, - ancestry: [ - currentState.event.process.entity_id, - ...(currentState.event.process.Ext?.ancestry ?? []), - ], + parentEntityID: currentStateEntityID, + ancestry, ancestryArrayLimit: opts.ancestryArraySize, }); @@ -962,11 +982,11 @@ export class EndpointDocGenerator { processDuration = this.randomN(1000000); // This lets termination events be up to 1 million seconds after the creation event (~11 days) yield this.generateEvent({ timestamp: timestamp + processDuration * 1000, - entityID: child.process.entity_id, - parentEntityID: child.process.parent?.entity_id, + entityID: entityIDSafeVersion(child), + parentEntityID: parentEntityIDSafeVersion(child), eventCategory: ['process'], eventType: ['end'], - ancestry: child.process.Ext?.ancestry, + ancestry, ancestryArrayLimit: opts.ancestryArraySize, }); } @@ -998,7 +1018,8 @@ export class EndpointDocGenerator { ordered: boolean = false ) { let relatedEventsInfo: RelatedEventInfo[]; - let ts = node['@timestamp'] + 1; + const nodeTimestamp = timestampSafeVersion(node) ?? 0; + let ts = nodeTimestamp + 1; if (typeof relatedEvents === 'number') { relatedEventsInfo = [{ category: RelatedEventCategory.Random, count: relatedEvents }]; } else { @@ -1017,16 +1038,16 @@ export class EndpointDocGenerator { if (ordered) { ts += this.randomN(processDuration) * 1000; } else { - ts = node['@timestamp'] + this.randomN(processDuration) * 1000; + ts = nodeTimestamp + this.randomN(processDuration) * 1000; } yield this.generateEvent({ timestamp: ts, - entityID: node.process.entity_id, - parentEntityID: node.process.parent?.entity_id, + entityID: entityIDSafeVersion(node), + parentEntityID: parentEntityIDSafeVersion(node), eventCategory: eventInfo.category, eventType: eventInfo.creationType, - ancestry: node.process.Ext?.ancestry, + ancestry: ancestryArray(node), }); } } @@ -1044,12 +1065,12 @@ export class EndpointDocGenerator { alertCreationTime: number = 6 * 3600 ) { for (let i = 0; i < relatedAlerts; i++) { - const ts = node['@timestamp'] + this.randomN(alertCreationTime) * 1000; + const ts = (timestampSafeVersion(node) ?? 0) + this.randomN(alertCreationTime) * 1000; yield this.generateAlert( ts, - node.process.entity_id, - node.process.parent?.entity_id, - node.process.Ext?.ancestry + entityIDSafeVersion(node), + parentEntityIDSafeVersion(node), + ancestryArray(node) ); } } diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 9a61738cd84b..b8c2fdbe65f1 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -7,6 +7,7 @@ import { Client } from '@elastic/elasticsearch'; import seedrandom from 'seedrandom'; import { EndpointDocGenerator, TreeOptions, Event } from './generate_data'; +import { firstNonNullValue } from './models/ecs_safety_helpers'; export async function indexHostsAndAlerts( client: Client, @@ -86,7 +87,7 @@ async function indexAlerts( // eslint-disable-next-line @typescript-eslint/no-explicit-any (array: Array>, doc) => { let index = eventIndex; - if (doc.event.kind === 'alert') { + if (firstNonNullValue(doc.event?.kind) === 'alert') { index = alertIndex; } array.push({ create: { _index: index } }, doc); diff --git a/x-pack/plugins/security_solution/common/endpoint/models/ecs_safety_helpers.ts b/x-pack/plugins/security_solution/common/endpoint/models/ecs_safety_helpers.ts index 8b419e90a6ee..5dc75bb707d0 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/ecs_safety_helpers.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/ecs_safety_helpers.ts @@ -46,12 +46,12 @@ export function values(valueOrCollection: ECSField): T[] { if (Array.isArray(valueOrCollection)) { const nonNullValues: T[] = []; for (const value of valueOrCollection) { - if (value !== null) { + if (value !== null && value !== undefined) { nonNullValues.push(value); } } return nonNullValues; - } else if (valueOrCollection !== null) { + } else if (valueOrCollection !== null && valueOrCollection !== undefined) { // if there is a single non-null value, wrap it in an array and return it. return [valueOrCollection]; } else { diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts index 6e6e0f443015..2b0aa1601ab3 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.test.ts @@ -5,7 +5,7 @@ */ import { EndpointDocGenerator } from '../generate_data'; import { descriptiveName, isProcessRunning } from './event'; -import { ResolverEvent } from '../types'; +import { ResolverEvent, SafeResolverEvent } from '../types'; describe('Generated documents', () => { let generator: EndpointDocGenerator; @@ -17,20 +17,31 @@ describe('Generated documents', () => { it('returns the right name for a registry event', () => { const extensions = { registry: { key: `HKLM/Windows/Software/abc` } }; const event = generator.generateEvent({ eventCategory: 'registry', extensions }); - expect(descriptiveName(event)).toEqual({ subject: `HKLM/Windows/Software/abc` }); + // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies + // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. + expect(descriptiveName(event as ResolverEvent)).toEqual({ + subject: `HKLM/Windows/Software/abc`, + }); }); it('returns the right name for a network event', () => { const randomIP = `${generator.randomIP()}`; const extensions = { network: { direction: 'outbound', forwarded_ip: randomIP } }; const event = generator.generateEvent({ eventCategory: 'network', extensions }); - expect(descriptiveName(event)).toEqual({ subject: `${randomIP}`, descriptor: 'outbound' }); + // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies + // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. + expect(descriptiveName(event as ResolverEvent)).toEqual({ + subject: `${randomIP}`, + descriptor: 'outbound', + }); }); it('returns the right name for a file event', () => { const extensions = { file: { path: 'C:\\My Documents\\business\\January\\processName' } }; const event = generator.generateEvent({ eventCategory: 'file', extensions }); - expect(descriptiveName(event)).toEqual({ + // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies + // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. + expect(descriptiveName(event as ResolverEvent)).toEqual({ subject: 'C:\\My Documents\\business\\January\\processName', }); }); @@ -38,27 +49,31 @@ describe('Generated documents', () => { it('returns the right name for a dns event', () => { const extensions = { dns: { question: { name: `${generator.randomIP()}` } } }; const event = generator.generateEvent({ eventCategory: 'dns', extensions }); - expect(descriptiveName(event)).toEqual({ subject: extensions.dns.question.name }); + // casting to ResolverEvent here because the `descriptiveName` function is used by the frontend is still relies + // on the unsafe ResolverEvent type. Once it's switched over to the safe version we can remove this cast. + expect(descriptiveName(event as ResolverEvent)).toEqual({ + subject: extensions.dns.question.name, + }); }); }); describe('Process running events', () => { it('is a running event when event.type is a string', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: 'start', }); expect(isProcessRunning(event)).toBeTruthy(); }); it('is a running event when event.type is an array of strings', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: ['start'], }); expect(isProcessRunning(event)).toBeTruthy(); }); it('is a running event when event.type is an array of strings and contains start', () => { - let event: ResolverEvent = generator.generateEvent({ + let event: SafeResolverEvent = generator.generateEvent({ eventType: ['bogus', 'start', 'creation'], }); expect(isProcessRunning(event)).toBeTruthy(); @@ -70,35 +85,35 @@ describe('Generated documents', () => { }); it('is not a running event when event.type is only and end type', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: ['end'], }); expect(isProcessRunning(event)).toBeFalsy(); }); it('is not a running event when event.type is empty', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: [], }); expect(isProcessRunning(event)).toBeFalsy(); }); it('is not a running event when event.type is bogus', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: ['bogus'], }); expect(isProcessRunning(event)).toBeFalsy(); }); it('is a running event when event.type contains info', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: ['info'], }); expect(isProcessRunning(event)).toBeTruthy(); }); it('is a running event when event.type contains change', () => { - const event: ResolverEvent = generator.generateEvent({ + const event: SafeResolverEvent = generator.generateEvent({ eventType: ['bogus', 'change'], }); expect(isProcessRunning(event)).toBeTruthy(); diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index a0e9be58911c..07208214a641 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -9,7 +9,7 @@ import { SafeResolverEvent, SafeLegacyEndpointEvent, } from '../types'; -import { firstNonNullValue } from './ecs_safety_helpers'; +import { firstNonNullValue, hasValue, values } from './ecs_safety_helpers'; /* * Determine if a `ResolverEvent` is the legacy variety. Can be used to narrow `ResolverEvent` to `LegacyEndpointEvent`. @@ -27,32 +27,24 @@ export function isLegacyEvent(event: ResolverEvent): event is LegacyEndpointEven return (event as LegacyEndpointEvent).endgame !== undefined; } -export function isProcessRunning(event: ResolverEvent): boolean { - if (isLegacyEvent(event)) { - return ( - event.event?.type === 'process_start' || - event.event?.action === 'fork_event' || - event.event?.type === 'already_running' - ); - } - - if (Array.isArray(event.event.type)) { +export function isProcessRunning(event: SafeResolverEvent): boolean { + if (isLegacyEventSafeVersion(event)) { return ( - event.event.type.includes('start') || - event.event.type.includes('change') || - event.event.type.includes('info') + hasValue(event.event?.type, 'process_start') || + hasValue(event.event?.action, 'fork_event') || + hasValue(event.event?.type, 'already_running') ); } return ( - event.event.type === 'start' || event.event.type === 'change' || event.event.type === 'info' + hasValue(event.event?.type, 'start') || + hasValue(event.event?.type, 'change') || + hasValue(event.event?.type, 'info') ); } -export function timestampSafeVersion(event: SafeResolverEvent): string | undefined | number { - return isLegacyEventSafeVersion(event) - ? firstNonNullValue(event.endgame?.timestamp_utc) - : firstNonNullValue(event?.['@timestamp']); +export function timestampSafeVersion(event: SafeResolverEvent): undefined | number { + return firstNonNullValue(event?.['@timestamp']); } /** @@ -75,11 +67,7 @@ export function timestampAsDateSafeVersion(event: SafeResolverEvent): Date | und } export function eventTimestamp(event: ResolverEvent): string | undefined | number { - if (isLegacyEvent(event)) { - return event.endgame.timestamp_utc; - } else { - return event['@timestamp']; - } + return event['@timestamp']; } export function eventName(event: ResolverEvent): string { @@ -105,14 +93,7 @@ export function eventId(event: ResolverEvent): number | undefined | string { return event.event.id; } -export function eventSequence(event: ResolverEvent): number | undefined { - if (isLegacyEvent(event)) { - return firstNonNullValue(event.endgame.serial_event_id); - } - return firstNonNullValue(event.event?.sequence); -} - -export function eventSequenceSafeVersion(event: SafeResolverEvent): number | undefined { +export function eventSequence(event: SafeResolverEvent): number | undefined { if (isLegacyEventSafeVersion(event)) { return firstNonNullValue(event.endgame.serial_event_id); } @@ -156,16 +137,16 @@ export function parentEntityIDSafeVersion(event: SafeResolverEvent): string | un return firstNonNullValue(event.process?.parent?.entity_id); } -export function ancestryArray(event: ResolverEvent): string[] | undefined { - if (isLegacyEvent(event)) { +export function ancestryArray(event: SafeResolverEvent): string[] | undefined { + if (isLegacyEventSafeVersion(event)) { return undefined; } // this is to guard against the endpoint accidentally not sending the ancestry array // otherwise the request will fail when really we should just try using the parent entity id - return event.process.Ext?.ancestry; + return values(event.process?.Ext?.ancestry); } -export function getAncestryAsArray(event: ResolverEvent | undefined): string[] { +export function getAncestryAsArray(event: SafeResolverEvent | undefined): string[] { if (!event) { return []; } @@ -175,7 +156,7 @@ export function getAncestryAsArray(event: ResolverEvent | undefined): string[] { return ancestors; } - const parentID = parentEntityId(event); + const parentID = parentEntityIDSafeVersion(event); if (parentID) { return [parentID]; } diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index e0bd916103a2..cc40225ec1a1 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -112,6 +112,27 @@ export interface ResolverChildNode extends ResolverLifecycleNode { nextChild?: string | null; } +/** + * Safe version of `ResolverChildNode`. + */ +export interface SafeResolverChildNode extends SafeResolverLifecycleNode { + /** + * nextChild can have 3 different states: + * + * undefined: This indicates that you should not use this node for additional queries. It does not mean that node does + * not have any more direct children. The node could have more direct children but to determine that, use the + * ResolverChildren node's nextChild. + * + * null: Indicates that we have received all the children of the node. There may be more descendants though. + * + * string: Indicates this is a leaf node and it can be used to continue querying for additional descendants + * using this node's entity_id + * + * For more information see the resolver docs on pagination [here](../../server/endpoint/routes/resolver/docs/README.md#L129) + */ + nextChild?: string | null; +} + /** * The response structure for the children route. The structure is an array of nodes where each node * has an array of lifecycle events. @@ -131,6 +152,24 @@ export interface ResolverChildren { nextChild: string | null; } +/** + * Safe version of `ResolverChildren`. + */ +export interface SafeResolverChildren { + childNodes: SafeResolverChildNode[]; + /** + * nextChild can have 2 different states: + * + * null: Indicates that we have received all the descendants that can be retrieved using this node. To retrieve more + * nodes in the tree use a cursor provided in one of the returned children. If no other cursor exists then the tree + * is complete. + * + * string: Indicates this node has more descendants that can be retrieved, pass this cursor in while using this node's + * entity_id for the request. + */ + nextChild: string | null; +} + /** * A flattened tree representing the nodes in a resolver graph. */ @@ -148,6 +187,23 @@ export interface ResolverTree { stats: ResolverNodeStats; } +/** + * Safe version of `ResolverTree`. + */ +export interface SafeResolverTree { + /** + * Origin of the tree. This is in the middle of the tree. Typically this would be the same + * process node that generated an alert. + */ + entityID: string; + children: SafeResolverChildren; + relatedEvents: Omit; + relatedAlerts: Omit; + ancestry: SafeResolverAncestry; + lifecycle: SafeResolverEvent[]; + stats: ResolverNodeStats; +} + /** * The lifecycle events (start, end etc) for a node. */ @@ -160,6 +216,18 @@ export interface ResolverLifecycleNode { stats?: ResolverNodeStats; } +/** + * Safe version of `ResolverLifecycleNode`. + */ +export interface SafeResolverLifecycleNode { + entityID: string; + lifecycle: SafeResolverEvent[]; + /** + * stats are only set when the entire tree is being fetched + */ + stats?: ResolverNodeStats; +} + /** * The response structure when searching for ancestors of a node. */ @@ -175,6 +243,21 @@ export interface ResolverAncestry { nextAncestor: string | null; } +/** + * Safe version of `ResolverAncestry`. + */ +export interface SafeResolverAncestry { + /** + * An array of ancestors with the lifecycle events grouped together + */ + ancestors: SafeResolverLifecycleNode[]; + /** + * A cursor for retrieving additional ancestors for a particular node. `null` indicates that there were no additional + * ancestors when the request returned. More could have been ingested by ES after the fact though. + */ + nextAncestor: string | null; +} + /** * Response structure for the related events route. */ @@ -198,7 +281,7 @@ export interface SafeResolverRelatedEvents { */ export interface ResolverRelatedAlerts { entityID: string; - alerts: ResolverEvent[]; + alerts: SafeResolverEvent[]; nextAlert: string | null; } @@ -251,152 +334,133 @@ export interface Host { /** * A record of hashes for something. Provides hashes in multiple formats. A favorite structure of the Elastic Endpoint. */ -interface Hashes { +type Hashes = Partial<{ /** * A hash in MD5 format. */ - md5: string; + md5: ECSField; /** * A hash in SHA-1 format. */ - sha1: string; + sha1: ECSField; /** * A hash in SHA-256 format. */ - sha256: string; -} + sha256: ECSField; +}>; -interface MalwareClassification { - identifier: string; - score: number; - threshold: number; - version: string; -} +type MalwareClassification = Partial<{ + identifier: ECSField; + score: ECSField; + threshold: ECSField; + version: ECSField; +}>; -interface ThreadFields { - id: number; - Ext: { - service_name: string; - start: number; - start_address: number; - start_address_module: string; - }; -} +type ThreadFields = Partial<{ + id: ECSField; + Ext: Partial<{ + service_name: ECSField; + start: ECSField; + start_address: ECSField; + start_address_module: ECSField; + }>; +}>; -interface DllFields { +type DllFields = Partial<{ hash: Hashes; - path: string; - pe: { - architecture: string; - }; - code_signature: { - subject_name: string; - trusted: boolean; - }; - Ext: { - compile_time: number; + path: ECSField; + pe: Partial<{ + architecture: ECSField; + }>; + code_signature: Partial<{ + subject_name: ECSField; + trusted: ECSField; + }>; + Ext: Partial<{ + compile_time: ECSField; malware_classification: MalwareClassification; - mapped_address: number; - mapped_size: number; - }; -} + mapped_address: ECSField; + mapped_size: ECSField; + }>; +}>; /** * Describes an Alert Event. */ -export interface AlertEvent { - '@timestamp': number; - agent: { - id: string; - version: string; - type: string; - }; - ecs: { - version: string; - }; - event: { - id: string; - action: string; - category: string; - kind: string; - dataset: string; - module: string; - type: string; - sequence: number; - }; - Endpoint: { - policy: { - applied: { - id: string; - status: HostPolicyResponseActionStatus; - name: string; - }; - }; - }; - process: { - command_line?: string; - pid: number; - ppid?: number; - entity_id: string; - parent?: { - pid: number; - entity_id: string; - }; - name: string; - hash: Hashes; - executable: string; - start: number; - thread?: ThreadFields[]; - uptime: number; - Ext?: { - /* - * The array has a special format. The entity_ids towards the beginning of the array are closer ancestors and the - * values towards the end of the array are more distant ancestors (grandparents). Therefore - * ancestry_array[0] == process.parent.entity_id and ancestry_array[1] == process.parent.parent.entity_id - */ - ancestry?: string[]; - code_signature: Array<{ - subject_name: string; - trusted: boolean; +export type AlertEvent = Partial<{ + event: Partial<{ + action: ECSField; + dataset: ECSField; + module: ECSField; + }>; + Endpoint: Partial<{ + policy: Partial<{ + applied: Partial<{ + id: ECSField; + status: ECSField; + name: ECSField; }>; - malware_classification?: MalwareClassification; - token: { - domain: string; - type: string; - user: string; - sid: string; - integrity_level: number; - integrity_level_name: string; - privileges?: Array<{ - description: string; - name: string; - enabled: boolean; - }>; - }; - user: string; - }; - }; - file: { - owner: string; - name: string; - path: string; - accessed: number; - mtime: number; - created: number; - size: number; - hash: Hashes; - Ext: { + }>; + }>; + process: Partial<{ + command_line: ECSField; + ppid: ECSField; + start: ECSField; + // Using ECSField as the outer because the object is expected to be an array + thread: ECSField; + uptime: ECSField; + Ext: Partial<{ + // Using ECSField as the outer because the object is expected to be an array + code_signature: ECSField< + Partial<{ + subject_name: ECSField; + trusted: ECSField; + }> + >; malware_classification: MalwareClassification; - temp_file_path: string; - code_signature: Array<{ - trusted: boolean; - subject_name: string; + token: Partial<{ + domain: ECSField; + type: ECSField; + user: ECSField; + sid: ECSField; + integrity_level: ECSField; + integrity_level_name: ECSField; + // Using ECSField as the outer because the object is expected to be an array + privileges: ECSField< + Partial<{ + description: ECSField; + name: ECSField; + enabled: ECSField; + }> + >; }>; - }; - }; - host: Host; - dll?: DllFields[]; -} + user: ECSField; + }>; + }>; + file: Partial<{ + owner: ECSField; + name: ECSField; + accessed: ECSField; + mtime: ECSField; + created: ECSField; + size: ECSField; + hash: Hashes; + Ext: Partial<{ + malware_classification: MalwareClassification; + temp_file_path: ECSField; + // Using ECSField as the outer because the object is expected to be an array + code_signature: ECSField< + Partial<{ + trusted: ECSField; + subject_name: ECSField; + }> + >; + }>; + }>; + // Using ECSField as the outer because the object is expected to be an array + dll: ECSField; +}> & + SafeEndpointEvent; /** * The status of the Endpoint Agent as reported by the Agent or the @@ -585,7 +649,7 @@ export type ResolverEvent = EndpointEvent | LegacyEndpointEvent; * All mappings in Elasticsearch support arrays. They can also return null values or be missing. For example, a `keyword` mapping could return `null` or `[null]` or `[]` or `'hi'`, or `['hi', 'there']`. We need to handle these cases in order to avoid throwing an error. * When dealing with an value that comes from ES, wrap the underlying type in `ECSField`. For example, if you have a `keyword` or `text` value coming from ES, cast it to `ECSField`. */ -export type ECSField = T | null | Array; +export type ECSField = T | null | undefined | Array; /** * A more conservative version of `ResolverEvent` that treats fields as optional and use `ECSField` to type all ECS fields. @@ -648,9 +712,7 @@ export type SafeEndpointEvent = Partial<{ subject_name: ECSField; }>; pid: ECSField; - hash: Partial<{ - md5: ECSField; - }>; + hash: Hashes; parent: Partial<{ entity_id: ECSField; name: ECSField; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts index 79756621ef50..5ec5bb97250d 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts_detection_rules.ts @@ -80,9 +80,9 @@ export const selectNumberOfRules = (numberOfRules: number) => { }; export const sortByActivatedRules = () => { - cy.get(SORT_RULES_BTN).click({ force: true }); + cy.get(SORT_RULES_BTN).contains('Activated').click({ force: true }); waitForRulesToBeLoaded(); - cy.get(SORT_RULES_BTN).click({ force: true }); + cy.get(SORT_RULES_BTN).contains('Activated').click({ force: true }); waitForRulesToBeLoaded(); }; diff --git a/x-pack/plugins/security_solution/package.json b/x-pack/plugins/security_solution/package.json index 70dbaa0d3168..fd7941fb17cc 100644 --- a/x-pack/plugins/security_solution/package.json +++ b/x-pack/plugins/security_solution/package.json @@ -9,7 +9,7 @@ "build-graphql-types": "node scripts/generate_types_from_graphql.js", "cypress:open": "cypress open --config-file ./cypress/cypress.json", "cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/visual_config.ts", - "cypress:run": "cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge --reportDir ../../../target/kibana-security-solution/cypress/results > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;", + "cypress:run": "cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;", "cypress:run-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts", "test:generate": "node scripts/endpoint/resolver_generator" }, diff --git a/x-pack/plugins/security_solution/public/cases/containers/configure/mock.ts b/x-pack/plugins/security_solution/public/cases/containers/configure/mock.ts index 9b9e978ffca4..2fc761f4dc42 100644 --- a/x-pack/plugins/security_solution/public/cases/containers/configure/mock.ts +++ b/x-pack/plugins/security_solution/public/cases/containers/configure/mock.ts @@ -77,7 +77,7 @@ export const connectorsMock: Connector[] = [ name: 'Jira', config: { apiUrl: 'https://instance.atlassian.ne', - casesConfiguration: { + incidentConfiguration: { mapping: [ { source: 'title', diff --git a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts index 190009440529..943b30925a54 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/endpoint/use_navigate_to_app_event_handler.ts @@ -12,7 +12,7 @@ type NavigateToAppHandlerOptions = NavigateToAppOptions & { state?: S; onClick?: EventHandlerCallback; }; -type EventHandlerCallback = MouseEventHandler; +type EventHandlerCallback = MouseEventHandler; /** * Provides an event handlers that can be used with (for example) `onClick` to prevent the diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx deleted file mode 100644 index 30e2c650a70c..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/components/connector_flyout/index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useCallback, useEffect } from 'react'; -import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer } from '@elastic/eui'; - -import { isEmpty, get } from 'lodash/fp'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ActionConnectorFieldsProps } from '../../../../../../../triggers_actions_ui/public/types'; -import { FieldMapping } from '../../../../../cases/components/configure_cases/field_mapping'; - -import { CasesConfigurationMapping } from '../../../../../cases/containers/configure/types'; - -import * as i18n from '../../translations'; -import { ActionConnector, ConnectorFlyoutHOCProps } from '../../types'; -import { createDefaultMapping } from '../../utils'; -import { connectorsConfiguration } from '../../config'; - -export const withConnectorFlyout = ({ - ConnectorFormComponent, - connectorActionTypeId, - secretKeys = [], - configKeys = [], -}: ConnectorFlyoutHOCProps) => { - const ConnectorFlyout: React.FC> = ({ - 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 configKeysWithDefault = [...configKeys, 'apiUrl']; - - const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; - - /** - * We need to distinguish between the add flyout and the edit flyout. - * useEffect will run only once on component mount. - * This guarantees that the function below will run only once. - * On the first render of the component the apiUrl can be either undefined or filled. - * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. - */ - - useEffect(() => { - if (!isEmpty(apiUrl)) { - secretKeys.forEach((key: string) => editActionSecrets(key, '')); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - if (isEmpty(mapping)) { - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: createDefaultMapping(connectorsConfiguration[connectorActionTypeId].fields), - }); - } - - const handleOnChangeActionConfig = useCallback( - (key: string, value: string) => editActionConfig(key, value), - // eslint-disable-next-line react-hooks/exhaustive-deps - [] - ); - - const handleOnBlurActionConfig = useCallback( - (key: string) => { - if (configKeysWithDefault.includes(key) && get(key, action.config) == null) { - editActionConfig(key, ''); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [action.config] - ); - - const handleOnChangeSecretConfig = useCallback( - (key: string, value: string) => editActionSecrets(key, value), - // eslint-disable-next-line react-hooks/exhaustive-deps - [] - ); - - const handleOnBlurSecretConfig = useCallback( - (key: string) => { - if (secretKeys.includes(key) && get(key, action.secrets) == null) { - editActionSecrets(key, ''); - } - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [action.secrets] - ); - - const handleOnChangeMappingConfig = useCallback( - (newMapping: CasesConfigurationMapping[]) => - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: newMapping, - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [action.config] - ); - - return ( - <> - - - - handleOnChangeActionConfig('apiUrl', evt.target.value)} - onBlur={handleOnBlurActionConfig.bind(null, 'apiUrl')} - /> - - - - - - - - - - - - - ); - }; - - return ConnectorFlyout; -}; diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/config.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/config.ts index 9e6982ea2030..3aca18637882 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/config.ts +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/config.ts @@ -9,12 +9,12 @@ import { ServiceNowConnectorConfiguration, JiraConnectorConfiguration, + ResilientConnectorConfiguration, } from '../../../../../triggers_actions_ui/public/common'; -import { connector as resilientConnectorConfig } from './resilient/config'; import { ConnectorConfiguration } from './types'; export const connectorsConfiguration: Record = { '.servicenow': ServiceNowConnectorConfiguration as ConnectorConfiguration, '.jira': JiraConnectorConfiguration as ConnectorConfiguration, - '.resilient': resilientConnectorConfig, + '.resilient': ResilientConnectorConfiguration as ConnectorConfiguration, }; diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/flyout.tsx b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/flyout.tsx deleted file mode 100644 index 31bf0a4dfc34..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/flyout.tsx +++ /dev/null @@ -1,114 +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 { - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiFieldPassword, - EuiSpacer, -} from '@elastic/eui'; - -import * as i18n from './translations'; -import { ConnectorFlyoutFormProps } from '../types'; -import { ResilientActionConnector } from './types'; -import { withConnectorFlyout } from '../components/connector_flyout'; - -const resilientConnectorForm: React.FC> = ({ - errors, - action, - onChangeSecret, - onBlurSecret, - onChangeConfig, - onBlurConfig, -}) => { - const { orgId } = action.config; - const { apiKeyId, apiKeySecret } = action.secrets; - const isOrgIdInvalid: boolean = errors.orgId.length > 0 && orgId != null; - const isApiKeyIdInvalid: boolean = errors.apiKeyId.length > 0 && apiKeyId != null; - const isApiKeySecretInvalid: boolean = errors.apiKeySecret.length > 0 && apiKeySecret != null; - - return ( - <> - - - - onChangeConfig('orgId', evt.target.value)} - onBlur={() => onBlurConfig('orgId')} - /> - - - - - - - - onChangeSecret('apiKeyId', evt.target.value)} - onBlur={() => onBlurSecret('apiKeyId')} - /> - - - - - - - - onChangeSecret('apiKeySecret', evt.target.value)} - onBlur={() => onBlurSecret('apiKeySecret')} - /> - - - - - ); -}; - -export const resilientConnectorFlyout = withConnectorFlyout({ - ConnectorFormComponent: resilientConnectorForm, - secretKeys: ['apiKeyId', 'apiKeySecret'], - configKeys: ['orgId'], - connectorActionTypeId: '.resilient', -}); - -// eslint-disable-next-line import/no-default-export -export { resilientConnectorFlyout as default }; diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx deleted file mode 100644 index ba4879e87a1f..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/index.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { lazy } from 'react'; -import { - ValidationResult, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../triggers_actions_ui/public/types'; - -import { connector } from './config'; -import { createActionType } from '../utils'; -import logo from './logo.svg'; -import { ResilientActionConnector } from './types'; -import * as i18n from './translations'; - -interface Errors { - orgId: string[]; - apiKeyId: string[]; - apiKeySecret: string[]; -} - -const validateConnector = (action: ResilientActionConnector): ValidationResult => { - const errors: Errors = { - orgId: [], - apiKeyId: [], - apiKeySecret: [], - }; - - if (!action.config.orgId) { - errors.orgId = [...errors.orgId, i18n.RESILIENT_PROJECT_KEY_REQUIRED]; - } - - if (!action.secrets.apiKeyId) { - errors.apiKeyId = [...errors.apiKeyId, i18n.RESILIENT_API_KEY_ID_REQUIRED]; - } - - if (!action.secrets.apiKeySecret) { - errors.apiKeySecret = [...errors.apiKeySecret, i18n.RESILIENT_API_KEY_SECRET_REQUIRED]; - } - - return { errors }; -}; - -export const getActionType = createActionType({ - id: connector.id, - iconClass: logo, - selectMessage: i18n.RESILIENT_DESC, - actionTypeTitle: connector.name, - validateConnector, - actionConnectorFields: lazy(() => import('./flyout')), -}); diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.ts deleted file mode 100644 index 2ff97ad35409..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/translations.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 { i18n } from '@kbn/i18n'; - -export * from '../translations'; - -export const RESILIENT_DESC = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.selectMessageText', - { - defaultMessage: 'Push or update Security case data to a new issue in Resilient', - } -); - -export const RESILIENT_TITLE = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.actionTypeTitle', - { - defaultMessage: 'IBM Resilient', - } -); - -export const RESILIENT_PROJECT_KEY_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.orgId', - { - defaultMessage: 'Organization ID', - } -); - -export const RESILIENT_PROJECT_KEY_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.requiredOrgIdTextField', - { - defaultMessage: 'Organization ID is required', - } -); - -export const RESILIENT_API_KEY_ID_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.apiKeyId', - { - defaultMessage: 'API key ID', - } -); - -export const RESILIENT_API_KEY_ID_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.requiredApiKeyIdTextField', - { - defaultMessage: 'API key ID is required', - } -); - -export const RESILIENT_API_KEY_SECRET_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.apiKeySecret', - { - defaultMessage: 'API key secret', - } -); - -export const RESILIENT_API_KEY_SECRET_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.resilient.requiredApiKeySecretTextField', - { - defaultMessage: 'API key secret is required', - } -); - -export const MAPPING_FIELD_NAME = i18n.translate( - 'xpack.securitySolution.case.configureCases.mappingFieldName', - { - defaultMessage: 'Name', - } -); diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/types.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/types.ts deleted file mode 100644 index fe6dbb2b3674..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/types.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. - */ - -/* eslint-disable no-restricted-imports */ -/* eslint-disable @kbn/eslint/no-restricted-paths */ - -import { - ResilientPublicConfigurationType, - ResilientSecretConfigurationType, -} from '../../../../../../actions/server/builtin_action_types/resilient/types'; - -export { ResilientFieldsType } from '../../../../../../case/common/api/connectors'; - -export * from '../types'; - -export interface ResilientActionConnector { - config: ResilientPublicConfigurationType; - secrets: ResilientSecretConfigurationType; -} diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/translations.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/translations.ts deleted file mode 100644 index 6dd1247d40fc..000000000000 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/translations.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const API_URL_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.common.apiUrlTextFieldLabel', - { - defaultMessage: 'URL', - } -); - -export const API_URL_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.common.requiredApiUrlTextField', - { - defaultMessage: 'URL is required', - } -); - -export const API_URL_INVALID = i18n.translate( - 'xpack.securitySolution.case.connectors.common.invalidApiUrlTextField', - { - defaultMessage: 'URL is invalid', - } -); - -export const USERNAME_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.common.usernameTextFieldLabel', - { - defaultMessage: 'Username', - } -); - -export const USERNAME_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.common.requiredUsernameTextField', - { - defaultMessage: 'Username is required', - } -); - -export const PASSWORD_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.common.passwordTextFieldLabel', - { - defaultMessage: 'Password', - } -); - -export const PASSWORD_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.common.requiredPasswordTextField', - { - defaultMessage: 'Password is required', - } -); - -export const API_TOKEN_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.common.apiTokenTextFieldLabel', - { - defaultMessage: 'API token', - } -); - -export const API_TOKEN_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.common.requiredApiTokenTextField', - { - defaultMessage: 'API token is required', - } -); - -export const EMAIL_LABEL = i18n.translate( - 'xpack.securitySolution.case.connectors.common.emailTextFieldLabel', - { - defaultMessage: 'Email', - } -); - -export const EMAIL_REQUIRED = i18n.translate( - 'xpack.securitySolution.case.connectors.common.requiredEmailTextField', - { - defaultMessage: 'Email is required', - } -); - -export const MAPPING_FIELD_DESC = i18n.translate( - 'xpack.securitySolution.case.configureCases.mappingFieldDescription', - { - defaultMessage: 'Description', - } -); - -export const MAPPING_FIELD_COMMENTS = i18n.translate( - 'xpack.securitySolution.case.configureCases.mappingFieldComments', - { - defaultMessage: 'Comments', - } -); diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/types.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/types.ts index 1d688ad9b1d6..5d83c226bfec 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/types.ts @@ -4,12 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-restricted-imports */ -/* eslint-disable @kbn/eslint/no-restricted-paths */ - import { ActionType } from '../../../../../triggers_actions_ui/public'; -import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; -import { ExternalIncidentServiceConfiguration } from '../../../../../actions/server/builtin_action_types/case/types'; import { ActionType as ThirdPartySupportedActions, @@ -29,34 +24,3 @@ export interface ConnectorConfiguration extends ActionType { logo: string; fields: Record; } - -export interface ActionConnector { - config: ExternalIncidentServiceConfiguration; - secrets: {}; -} - -export interface ActionConnectorParams { - message: string; -} - -export interface ActionConnectorValidationErrors { - apiUrl: string[]; -} - -export type Optional = Omit & Partial; - -export interface ConnectorFlyoutFormProps { - errors: IErrorObject; - action: T; - onChangeSecret: (key: string, value: string) => void; - onBlurSecret: (key: string) => void; - onChangeConfig: (key: string, value: string) => void; - onBlurConfig: (key: string) => void; -} - -export interface ConnectorFlyoutHOCProps { - ConnectorFormComponent: React.FC>; - connectorActionTypeId: string; - configKeys?: string[]; - secretKeys?: string[]; -} diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/utils.ts b/x-pack/plugins/security_solution/public/common/lib/connectors/utils.ts index 6e72205c145a..0a6dd37d9f9e 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/utils.ts +++ b/x-pack/plugins/security_solution/public/common/lib/connectors/utils.ts @@ -4,63 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - ActionTypeModel, - ValidationResult, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../triggers_actions_ui/public/types'; - -import { - ActionConnector, - ActionConnectorParams, - ActionConnectorValidationErrors, - Optional, - ThirdPartyField, -} from './types'; -import { isUrlInvalid } from './validators'; - -import * as i18n from './translations'; import { CasesConfigurationMapping } from '../../../cases/containers/configure/types'; -export const createActionType = ({ - id, - actionTypeTitle, - selectMessage, - iconClass, - validateConnector, - validateParams = connectorParamsValidator, - actionConnectorFields, - actionParamsFields = null, -}: Optional) => (): ActionTypeModel => { - return { - id, - iconClass, - selectMessage, - actionTypeTitle, - validateConnector: (action: ActionConnector): ValidationResult => { - const errors: ActionConnectorValidationErrors = { - apiUrl: [], - }; - - if (!action.config.apiUrl) { - errors.apiUrl = [...errors.apiUrl, i18n.API_URL_REQUIRED]; - } - - if (isUrlInvalid(action.config.apiUrl)) { - errors.apiUrl = [...errors.apiUrl, i18n.API_URL_INVALID]; - } - - return { errors: { ...errors, ...validateConnector(action).errors } }; - }, - validateParams, - actionConnectorFields, - actionParamsFields, - }; -}; - -const connectorParamsValidator = (actionParams: ActionConnectorParams): ValidationResult => { - return { errors: {} }; -}; +import { ThirdPartyField } from './types'; export const createDefaultMapping = ( fields: Record diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx index 8fd3f648bc81..bfb23ff6af6a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/all_rules_tables/index.tsx @@ -20,7 +20,7 @@ import { RulesColumns, RuleStatusRowItemType, } from '../../../pages/detection_engine/rules/all/columns'; -import { Rule, Rules } from '../../../containers/detection_engine/rules/types'; +import { Rule, Rules, RulesSortingFields } from '../../../containers/detection_engine/rules/types'; import { AllRulesTabs } from '../../../pages/detection_engine/rules/all'; // EuiBasicTable give me a hardtime with adding the ref attributes so I went the easy way @@ -30,7 +30,7 @@ const MyEuiBasicTable = styled(EuiBasicTable as any)`` as any; export interface SortingType { sort: { - field: 'enabled'; + field: RulesSortingFields; direction: Direction; }; } @@ -48,12 +48,7 @@ interface AllRulesTablesProps { rules: Rules; rulesColumns: RulesColumns[]; rulesStatuses: RuleStatusRowItemType[]; - sorting: { - sort: { - field: 'enabled'; - direction: Direction; - }; - }; + sorting: SortingType; tableOnChangeCallback: ({ page, sort }: EuiBasicTableOnChange) => void; tableRef?: React.MutableRefObject; selectedTab: AllRulesTabs; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap index 1ed55774f935..4d21a983c970 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_overflow/__snapshots__/index.test.tsx.snap @@ -40,7 +40,7 @@ exports[`RuleActionsOverflow snapshots renders correctly against snapshot 1`] = icon="copy" onClick={[Function]} > - Duplicate rule… + Duplicate rule , - Delete rule… + Delete rule , ] } diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts index cd1ded544cfe..2a15cf7b95ce 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.test.ts @@ -202,7 +202,7 @@ describe('Detections Rules API', () => { expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/_find', { method: 'GET', query: { - filter: 'alert.attributes.tags: "hello" AND alert.attributes.tags: "world"', + filter: 'alert.attributes.tags: "hello" OR alert.attributes.tags: "world"', page: 1, per_page: 20, sort_field: 'enabled', @@ -297,7 +297,7 @@ describe('Detections Rules API', () => { method: 'GET', query: { filter: - 'alert.attributes.name: ruleName AND alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND alert.attributes.tags: "hello" AND alert.attributes.tags: "world"', + 'alert.attributes.name: ruleName AND alert.attributes.tags: "__internal_immutable:false" AND alert.attributes.tags: "__internal_immutable:true" AND (alert.attributes.tags: "hello" OR alert.attributes.tags: "world")', page: 1, per_page: 20, sort_field: 'enabled', diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts index e254516d1107..b66154fbb57d 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/api.ts @@ -107,7 +107,7 @@ export const fetchRules = async ({ }, signal, }: FetchRulesProps): Promise => { - const filters = [ + const filtersWithoutTags = [ ...(filterOptions.filter.length ? [`alert.attributes.name: ${filterOptions.filter}`] : []), ...(filterOptions.showCustomRules ? [`alert.attributes.tags: "__internal_immutable:false"`] @@ -115,15 +115,27 @@ export const fetchRules = async ({ ...(filterOptions.showElasticRules ? [`alert.attributes.tags: "__internal_immutable:true"`] : []), + ].join(' AND '); + + const tags = [ ...(filterOptions.tags?.map((t) => `alert.attributes.tags: "${t.replace(/"/g, '\\"')}"`) ?? []), - ]; + ].join(' OR '); + + const filterString = + filtersWithoutTags !== '' && tags !== '' + ? `${filtersWithoutTags} AND (${tags})` + : filtersWithoutTags + tags; + + const getFieldNameForSortField = (field: string) => { + return field === 'name' ? `${field}.keyword` : field; + }; const query = { page: pagination.page, per_page: pagination.perPage, - sort_field: filterOptions.sortField, + sort_field: getFieldNameForSortField(filterOptions.sortField), sort_order: filterOptions.sortOrder, - ...(filters.length ? { filter: filters.join(' AND ') } : {}), + ...(filterString !== '' ? { filter: filterString } : {}), }; return KibanaServices.get().http.fetch( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts index e94e57ad82bc..49579e893029 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts @@ -149,9 +149,10 @@ export interface FetchRulesProps { signal: AbortSignal; } +export type RulesSortingFields = 'enabled' | 'updated_at' | 'name' | 'created_at'; export interface FilterOptions { filter: string; - sortField: string; + sortField: RulesSortingFields; sortOrder: SortOrder; showCustomRules?: boolean; showElasticRules?: boolean; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index ea36a0cb0b48..866d3e896a71 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -99,7 +99,6 @@ interface GetColumns { reFetchRules: (refreshPrePackagedRule?: boolean) => void; } -// Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes? export const getColumns = ({ dispatch, dispatchToaster, @@ -127,7 +126,8 @@ export const getColumns = ({ ), truncateText: true, - width: '24%', + width: '20%', + sortable: true, }, { field: 'risk_score', @@ -138,14 +138,14 @@ export const getColumns = ({ ), truncateText: true, - width: '14%', + width: '10%', }, { field: 'severity', name: i18n.COLUMN_SEVERITY, render: (value: Rule['severity']) => , truncateText: true, - width: '16%', + width: '12%', }, { field: 'status_date', @@ -160,7 +160,7 @@ export const getColumns = ({ ); }, truncateText: true, - width: '20%', + width: '14%', }, { field: 'status', @@ -174,9 +174,40 @@ export const getColumns = ({ ); }, - width: '16%', + width: '12%', truncateText: true, }, + { + field: 'updated_at', + name: i18n.COLUMN_LAST_UPDATE, + render: (value: Rule['updated_at']) => { + return value == null ? ( + getEmptyTagValue() + ) : ( + + + + ); + }, + sortable: true, + truncateText: true, + width: '14%', + }, + { + field: 'version', + name: i18n.COLUMN_VERSION, + render: (value: Rule['version']) => { + return value == null ? ( + getEmptyTagValue() + ) : ( + + {value} + + ); + }, + truncateText: true, + width: '10%', + }, { field: 'tags', name: i18n.COLUMN_TAGS, @@ -190,7 +221,7 @@ export const getColumns = ({ ), truncateText: true, - width: '20%', + width: '14%', }, { align: 'center', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.tsx index 110691328b13..306adbd63ee7 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.tsx @@ -24,6 +24,7 @@ import { Rule, PaginationOptions, exportRules, + RulesSortingFields, } from '../../../../containers/detection_engine/rules'; import { HeaderSection } from '../../../../../common/components/header_section'; import { @@ -53,12 +54,12 @@ import { hasMlLicense } from '../../../../../../common/machine_learning/has_ml_l import { SecurityPageName } from '../../../../../app/types'; import { useFormatUrl } from '../../../../../common/components/link_to'; -const SORT_FIELD = 'enabled'; +const INITIAL_SORT_FIELD = 'enabled'; const initialState: State = { exportRuleIds: [], filterOptions: { filter: '', - sortField: SORT_FIELD, + sortField: INITIAL_SORT_FIELD, sortOrder: 'desc', }, loadingRuleIds: [], @@ -164,8 +165,13 @@ export const AllRules = React.memo( }); const sorting = useMemo( - (): SortingType => ({ sort: { field: 'enabled', direction: filterOptions.sortOrder } }), - [filterOptions.sortOrder] + (): SortingType => ({ + sort: { + field: filterOptions.sortField, + direction: filterOptions.sortOrder, + }, + }), + [filterOptions] ); const prePackagedRuleStatus = getPrePackagedRuleStatus( @@ -215,7 +221,7 @@ export const AllRules = React.memo( dispatch({ type: 'updateFilterOptions', filterOptions: { - sortField: SORT_FIELD, // Only enabled is supported for sorting currently + sortField: (sort?.field as RulesSortingFields) ?? INITIAL_SORT_FIELD, // Narrowing EuiBasicTable sorting types sortOrder: sort?.direction ?? 'desc', }, pagination: { page: page.index + 1, perPage: page.size }, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx index 49fe3438664c..4fe0bc8f835d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Dispatch, SetStateAction, useState } from 'react'; +import React, { + ChangeEvent, + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { EuiFilterButton, EuiFilterSelectItem, @@ -13,6 +21,8 @@ import { EuiPanel, EuiPopover, EuiText, + EuiFieldSearch, + EuiPopoverTitle, } from '@elastic/eui'; import styled from 'styled-components'; import * as i18n from '../../translations'; @@ -37,12 +47,39 @@ const ScrollableDiv = styled.div` * @param tags to display for filtering * @param onSelectedTagsChanged change listener to be notified when tag selection changes */ -export const TagsFilterPopoverComponent = ({ +const TagsFilterPopoverComponent = ({ tags, selectedTags, onSelectedTagsChanged, }: TagsFilterPopoverProps) => { + const sortedTags = useMemo(() => { + return tags.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase())); // Case insensitive + }, [tags]); const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); + const [searchInput, setSearchInput] = useState(''); + const [filterTags, setFilterTags] = useState(sortedTags); + + const tagsComponent = useMemo(() => { + return filterTags.map((tag, index) => ( + toggleSelectedGroup(tag, selectedTags, onSelectedTagsChanged)} + > + {`${tag}`} + + )); + }, [onSelectedTagsChanged, selectedTags, filterTags]); + + const onSearchInputChange = useCallback((event: ChangeEvent) => { + setSearchInput(event.target.value); + }, []); + + useEffect(() => { + setFilterTags( + sortedTags.filter((tag) => tag.toLowerCase().includes(searchInput.toLowerCase())) + ); + }, [sortedTags, searchInput]); return ( - - {tags.map((tag, index) => ( - toggleSelectedGroup(tag, selectedTags, onSelectedTagsChanged)} - > - {`${tag}`} - - ))} - - {tags.length === 0 && ( + + + + {tagsComponent} + {filterTags.length === 0 && ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index b20c8de8ed58..09503fcf1ef0 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -16,7 +16,7 @@ export const BACK_TO_DETECTIONS = i18n.translate( export const IMPORT_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.importRuleTitle', { - defaultMessage: 'Import rule…', + defaultMessage: 'Import rule', } ); @@ -100,7 +100,7 @@ export const BATCH_ACTION_ACTIVATE_SELECTED_ERROR = (totalRules: number) => 'xpack.securitySolution.detectionEngine.rules.allRules.batchActions.activateSelectedErrorTitle', { values: { totalRules }, - defaultMessage: 'Error activating {totalRules, plural, =1 {rule} other {rules}}…', + defaultMessage: 'Error activating {totalRules, plural, =1 {rule} other {rules}}', } ); @@ -116,7 +116,7 @@ export const BATCH_ACTION_DEACTIVATE_SELECTED_ERROR = (totalRules: number) => 'xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deactivateSelectedErrorTitle', { values: { totalRules }, - defaultMessage: 'Error deactivating {totalRules, plural, =1 {rule} other {rules}}…', + defaultMessage: 'Error deactivating {totalRules, plural, =1 {rule} other {rules}}', } ); @@ -130,14 +130,14 @@ export const BATCH_ACTION_EXPORT_SELECTED = i18n.translate( export const BATCH_ACTION_DUPLICATE_SELECTED = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.batchActions.duplicateSelectedTitle', { - defaultMessage: 'Duplicate selected…', + defaultMessage: 'Duplicate selected', } ); export const BATCH_ACTION_DELETE_SELECTED = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deleteSelectedTitle', { - defaultMessage: 'Delete selected…', + defaultMessage: 'Delete selected', } ); @@ -153,7 +153,7 @@ export const BATCH_ACTION_DELETE_SELECTED_ERROR = (totalRules: number) => 'xpack.securitySolution.detectionEngine.rules.allRules.batchActions.deleteSelectedErrorTitle', { values: { totalRules }, - defaultMessage: 'Error deleting {totalRules, plural, =1 {rule} other {rules}}…', + defaultMessage: 'Error deleting {totalRules, plural, =1 {rule} other {rules}}', } ); @@ -224,7 +224,7 @@ export const DUPLICATE = i18n.translate( export const DUPLICATE_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.actions.duplicateRuleDescription', { - defaultMessage: 'Duplicate rule…', + defaultMessage: 'Duplicate rule', } ); @@ -241,7 +241,7 @@ export const SUCCESSFULLY_DUPLICATED_RULES = (totalRules: number) => export const DUPLICATE_RULE_ERROR = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.actions.duplicateRuleErrorDescription', { - defaultMessage: 'Error duplicating rule…', + defaultMessage: 'Error duplicating rule', } ); @@ -255,7 +255,7 @@ export const EXPORT_RULE = i18n.translate( export const DELETE_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.actions.deleteeRuleDescription', { - defaultMessage: 'Delete rule…', + defaultMessage: 'Delete rule', } ); @@ -287,6 +287,13 @@ export const COLUMN_LAST_COMPLETE_RUN = i18n.translate( } ); +export const COLUMN_LAST_UPDATE = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.lastUpdateTitle', + { + defaultMessage: 'Last updated', + } +); + export const COLUMN_LAST_RESPONSE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.lastResponseTitle', { @@ -294,6 +301,13 @@ export const COLUMN_LAST_RESPONSE = i18n.translate( } ); +export const COLUMN_VERSION = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.versionTitle', + { + defaultMessage: 'Version', + } +); + export const COLUMN_TAGS = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.tagsTitle', { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/models/index_pattern.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/models/index_pattern.ts new file mode 100644 index 000000000000..064a591d0f3f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/models/index_pattern.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 { all } from 'deepmerge'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/common'; +import { Immutable } from '../../../../../common/endpoint/types'; + +export function clone(value: IIndexPattern | Immutable): IIndexPattern { + return all([value]) as IIndexPattern; +} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts index 5f36af2a2d8e..c2a838404b0b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts @@ -13,6 +13,7 @@ import { ServerApiError } from '../../../../common/types'; import { GetPolicyListResponse } from '../../policy/types'; import { GetPackagesResponse } from '../../../../../../ingest_manager/common'; import { EndpointState } from '../types'; +import { IIndexPattern } from '../../../../../../../../src/plugins/data/public'; interface ServerReturnedEndpointList { type: 'serverReturnedEndpointList'; @@ -81,11 +82,25 @@ interface ServerReturnedEndpointNonExistingPolicies { payload: EndpointState['nonExistingPolicies']; } +interface ServerReturnedEndpointAgentPolicies { + type: 'serverReturnedEndpointAgentPolicies'; + payload: EndpointState['agentPolicies']; +} + interface ServerReturnedEndpointExistValue { type: 'serverReturnedEndpointExistValue'; payload: boolean; } +interface ServerReturnedMetadataPatterns { + type: 'serverReturnedMetadataPatterns'; + payload: IIndexPattern[]; +} + +interface ServerFailedToReturnMetadataPatterns { + type: 'serverFailedToReturnMetadataPatterns'; + payload: ServerApiError; +} interface UserUpdatedEndpointListRefreshOptions { type: 'userUpdatedEndpointListRefreshOptions'; payload: { @@ -112,6 +127,9 @@ export type EndpointAction = | ServerReturnedEndpointExistValue | ServerCancelledPolicyItemsLoading | ServerReturnedEndpointPackageInfo + | ServerReturnedMetadataPatterns + | ServerFailedToReturnMetadataPatterns | AppRequestedEndpointList | ServerReturnedEndpointNonExistingPolicies + | ServerReturnedEndpointAgentPolicies | UserUpdatedEndpointListRefreshOptions; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts index 0fd970f4bed1..b4e00319485e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/endpoint_pagination.test.ts @@ -77,6 +77,7 @@ describe('endpoint list pagination: ', () => { expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/metadata', { body: JSON.stringify({ paging_properties: [{ page_index: '0' }, { page_size: '10' }], + filters: { kql: '' }, }), }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index 3a095644b3b4..4faef85afbdc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -52,7 +52,10 @@ describe('EndpointList store concerns', () => { policyItemsLoading: false, endpointPackageInfo: undefined, nonExistingPolicies: {}, + agentPolicies: {}, endpointsExist: true, + patterns: [], + patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index 15e89f977138..c4d2886f3e8e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -72,6 +72,7 @@ describe('endpoint list middleware', () => { expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/metadata', { body: JSON.stringify({ paging_properties: [{ page_index: '0' }, { page_size: '10' }], + filters: { kql: '' }, }), }); expect(listData(getState())).toEqual(apiResponse.hosts); @@ -100,6 +101,7 @@ describe('endpoint list middleware', () => { expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/metadata', { body: JSON.stringify({ paging_properties: [{ page_index: '0' }, { page_size: '10' }], + filters: { kql: '' }, }), }); expect(listData(getState())).toEqual(apiResponse.hosts); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index 2650aa486522..7673702f5437 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -15,16 +15,34 @@ import { listData, endpointPackageInfo, nonExistingPolicies, + patterns, + searchBarQuery, } from './selectors'; -import { EndpointState } from '../types'; +import { EndpointState, PolicyIds } from '../types'; import { sendGetEndpointSpecificPackagePolicies, sendGetEndpointSecurityPackage, sendGetAgentPolicyList, } from '../../policy/store/policy_list/services/ingest'; import { AGENT_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../ingest_manager/common'; +import { metadataCurrentIndexPattern } from '../../../../../common/endpoint/constants'; +import { IIndexPattern, Query } from '../../../../../../../../src/plugins/data/public'; -export const endpointMiddlewareFactory: ImmutableMiddlewareFactory = (coreStart) => { +export const endpointMiddlewareFactory: ImmutableMiddlewareFactory = ( + coreStart, + depsStart +) => { + async function fetchIndexPatterns(): Promise { + const { indexPatterns } = depsStart.data; + const fields = await indexPatterns.getFieldsForWildcard({ + pattern: metadataCurrentIndexPattern, + }); + const indexPattern: IIndexPattern = { + title: metadataCurrentIndexPattern, + fields, + }; + return [indexPattern]; + } // eslint-disable-next-line complexity return ({ getState, dispatch }) => (next) => async (action) => { next(action); @@ -52,10 +70,31 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory('/api/endpoint/metadata', { body: JSON.stringify({ paging_properties: [{ page_index: pageIndex }, { page_size: pageSize }], + filters: { kql: decodedQuery.query }, }), }); endpointResponse.request_page_index = Number(pageIndex); @@ -66,15 +105,21 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory => { +): Promise => { if (hosts.length === 0) { return; } @@ -279,29 +336,38 @@ const getNonExistingPoliciesForEndpointsList = async ( )})`, }, }) - ).items.reduce((list, agentPolicy) => { - (agentPolicy.package_policies as string[]).forEach((packagePolicy) => { - list[packagePolicy as string] = true; - }); - return list; - }, {}); + ).items.reduce( + (list, agentPolicy) => { + (agentPolicy.package_policies as string[]).forEach((packagePolicy) => { + list.packagePolicy[packagePolicy as string] = true; + list.agentPolicy[packagePolicy as string] = agentPolicy.id; + }); + return list; + }, + { packagePolicy: {}, agentPolicy: {} } + ); - const nonExisting = policyIdsToCheck.reduce( - (list, policyId) => { - if (policiesFound[policyId]) { + // packagePolicy contains non-existing packagePolicy ids whereas agentPolicy contains existing agentPolicy ids + const nonExistingPackagePoliciesAndExistingAgentPolicies = policyIdsToCheck.reduce( + (list, policyId: string) => { + if (policiesFound.packagePolicy[policyId as string]) { + list.agentPolicy[policyId as string] = policiesFound.agentPolicy[policyId]; return list; } - list[policyId] = true; + list.packagePolicy[policyId as string] = true; return list; }, - {} + { packagePolicy: {}, agentPolicy: {} } ); - if (Object.keys(nonExisting).length === 0) { + if ( + Object.keys(nonExistingPackagePoliciesAndExistingAgentPolicies.packagePolicy).length === 0 && + Object.keys(nonExistingPackagePoliciesAndExistingAgentPolicies.agentPolicy).length === 0 + ) { return; } - return nonExisting; + return nonExistingPackagePoliciesAndExistingAgentPolicies; }; const doEndpointsExist = async (http: HttpStart): Promise => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts index cfde474c6290..c5363a5ae952 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/mock_endpoint_result_list.ts @@ -20,6 +20,7 @@ import { } from '../../policy/store/policy_list/services/ingest'; import { GetAgentPoliciesResponse, + GetAgentPoliciesResponseItem, GetPackagesResponse, } from '../../../../../../ingest_manager/common/types/rest_spec'; import { GetPolicyListResponse } from '../../policy/types'; @@ -43,7 +44,7 @@ export const mockEndpointResultList: (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 hosts = []; + const hosts: HostInfo[] = []; for (let index = 0; index < actualCountToReturn; index++) { hosts.push({ metadata: generator.generateHostMetadata(), @@ -78,12 +79,14 @@ const endpointListApiPathHandlerMocks = ({ epmPackages = [generator.generateEpmPackage()], endpointPackagePolicies = [], policyResponse = generator.generatePolicyResponse(), + agentPolicy = generator.generateAgentPolicy(), }: { /** route handlers will be setup for each individual host in this array */ endpointsResults?: HostResultList['hosts']; epmPackages?: GetPackagesResponse['response']; endpointPackagePolicies?: GetPolicyListResponse['items']; policyResponse?: HostPolicyResponse; + agentPolicy?: GetAgentPoliciesResponseItem; } = {}) => { const apiHandlers = { // endpoint package info @@ -106,7 +109,6 @@ const endpointListApiPathHandlerMocks = ({ // Do policies referenced in endpoint list exist // just returns 1 single agent policy that includes all of the packagePolicy IDs provided [INGEST_API_AGENT_POLICIES]: (): GetAgentPoliciesResponse => { - const agentPolicy = generator.generateAgentPolicy(); (agentPolicy.package_policies as string[]).push( ...endpointPackagePolicies.map((packagePolicy) => packagePolicy.id) ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 060321fa4040..99a1df7eb400 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -30,7 +30,10 @@ export const initialEndpointListState: Immutable = { policyItemsLoading: false, endpointPackageInfo: undefined, nonExistingPolicies: {}, + agentPolicies: {}, endpointsExist: true, + patterns: [], + patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, }; @@ -70,6 +73,26 @@ export const endpointListReducer: ImmutableReducer = ( ...action.payload, }, }; + } else if (action.type === 'serverReturnedEndpointAgentPolicies') { + return { + ...state, + agentPolicies: { + ...state.agentPolicies, + ...action.payload, + }, + }; + } else if (action.type === 'serverReturnedMetadataPatterns') { + // handle error case + return { + ...state, + patterns: action.payload, + patternsError: undefined, + }; + } else if (action.type === 'serverFailedToReturnMetadataPatterns') { + return { + ...state, + patternsError: action.payload, + }; } else if (action.type === 'serverReturnedEndpointDetails') { return { ...state, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index e8abe37cf0a8..852bc9791fc9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -8,6 +8,7 @@ import querystring from 'querystring'; import { createSelector } from 'reselect'; import { matchPath } from 'react-router-dom'; +import { decode } from 'rison-node'; import { Immutable, HostPolicyResponseAppliedAction, @@ -21,6 +22,7 @@ import { MANAGEMENT_DEFAULT_PAGE_SIZE, MANAGEMENT_ROUTING_ENDPOINTS_PATH, } from '../../../common/constants'; +import { Query } from '../../../../../../../../src/plugins/data/common/query/types'; export const listData = (state: Immutable) => state.hosts; @@ -57,6 +59,13 @@ export const endpointPackageVersion = createSelector( (info) => info?.version ?? undefined ); +/** + * Returns the index patterns for the SearchBar to use for autosuggest + */ +export const patterns = (state: Immutable) => state.patterns; + +export const patternsError = (state: Immutable) => state.patternsError; + /** * Returns the full policy response from the endpoint after a user modifies a policy. */ @@ -142,7 +151,11 @@ export const uiQueryParams: ( const query = querystring.parse(location.search.slice(1)); const paginationParams = extractListPaginationParams(query); - const keys: Array = ['selected_endpoint', 'show']; + const keys: Array = [ + 'selected_endpoint', + 'show', + 'admin_query', + ]; for (const key of keys) { const value: string | undefined = @@ -204,9 +217,40 @@ export const nonExistingPolicies: ( state: Immutable ) => Immutable = (state) => state.nonExistingPolicies; +/** + * returns the list of known existing agent policies + */ +export const agentPolicies: ( + state: Immutable +) => Immutable = (state) => state.agentPolicies; + /** * Return boolean that indicates whether endpoints exist * @param state */ export const endpointsExist: (state: Immutable) => boolean = (state) => state.endpointsExist; + +/** + * Returns query text from query bar + */ +export const searchBarQuery: (state: Immutable) => Query = createSelector( + uiQueryParams, + ({ admin_query: adminQuery }) => { + const decodedQuery: Query = { query: '', language: 'kuery' }; + if (adminQuery) { + const urlDecodedQuery = (decode(adminQuery) as unknown) as Query; + if (urlDecodedQuery && typeof urlDecodedQuery.query === 'string') { + decodedQuery.query = urlDecodedQuery.query; + } + if ( + urlDecodedQuery && + typeof urlDecodedQuery.language === 'string' && + (urlDecodedQuery.language === 'kuery' || urlDecodedQuery.language === 'lucene') + ) { + decodedQuery.language = urlDecodedQuery.language; + } + } + return decodedQuery; + } +); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index 5a6a1af7bd7e..77f21243ea12 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -14,6 +14,7 @@ import { } from '../../../../common/endpoint/types'; import { ServerApiError } from '../../../common/types'; import { GetPackagesResponse } from '../../../../../ingest_manager/common'; +import { IIndexPattern } from '../../../../../../../src/plugins/data/public'; export interface EndpointState { /** list of host **/ @@ -50,16 +51,31 @@ export interface EndpointState { selectedPolicyId?: string; /** Endpoint package info */ endpointPackageInfo?: GetPackagesResponse['response'][0]; - /** tracks the list of policies IDs used in Host metadata that may no longer exist */ - nonExistingPolicies: Record; + /** Tracks the list of policies IDs used in Host metadata that may no longer exist */ + nonExistingPolicies: PolicyIds['packagePolicy']; + /** List of Package Policy Ids mapped to an associated Fleet Parent Agent Policy Id*/ + agentPolicies: PolicyIds['agentPolicy']; /** Tracks whether hosts exist and helps control if onboarding should be visible */ endpointsExist: boolean; + /** index patterns for query bar */ + patterns: IIndexPattern[]; + /** api error from retrieving index patters for query bar */ + patternsError?: ServerApiError; /** Is auto-refresh enabled */ isAutoRefreshEnabled: boolean; /** The current auto refresh interval for data in ms */ autoRefreshInterval: number; } +/** + * packagePolicy contains a list of Package Policy IDs (received via Endpoint metadata policy response) mapped to a boolean whether they exist or not. + * agentPolicy contains a list of existing Package Policy Ids mapped to an associated Fleet parent Agent Config. + */ +export interface PolicyIds { + packagePolicy: Record; + agentPolicy: Record; +} + /** * Query params on the host page parsed from the URL */ @@ -72,4 +88,6 @@ export interface EndpointIndexUIQueryParams { page_index?: string; /** show the policy response or host details */ show?: 'policy_response' | 'details'; + /** Query text from search bar*/ + admin_query?: string; } diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx new file mode 100644 index 000000000000..b6349a45f383 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/search_bar.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo, useCallback, useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; +import { encode, RisonValue } from 'rison-node'; +import styled from 'styled-components'; +import { Query, SearchBar, TimeHistory } from '../../../../../../../../../src/plugins/data/public'; +import { Storage } from '../../../../../../../../../src/plugins/kibana_utils/public'; +import { urlFromQueryParams } from '../url_from_query_params'; +import { useEndpointSelector } from '../hooks'; +import * as selectors from '../../store/selectors'; +import { clone } from '../../models/index_pattern'; + +const AdminQueryBar = styled.div` + .globalQueryBar { + padding: 0; + } +`; + +export const AdminSearchBar = memo(() => { + const history = useHistory(); + const queryParams = useEndpointSelector(selectors.uiQueryParams); + const searchBarIndexPatterns = useEndpointSelector(selectors.patterns); + const searchBarQuery = useEndpointSelector(selectors.searchBarQuery); + const clonedIndexPatterns = useMemo( + () => searchBarIndexPatterns.map((pattern) => clone(pattern)), + [searchBarIndexPatterns] + ); + + const onQuerySubmit = useCallback( + (params: { query?: Query }) => { + history.push( + urlFromQueryParams({ + ...queryParams, + admin_query: encode((params.query as unknown) as RisonValue), + }) + ); + }, + [history, queryParams] + ); + + const timeHistory = useMemo(() => new TimeHistory(new Storage(localStorage)), []); + + return ( +
+ {searchBarIndexPatterns && searchBarIndexPatterns.length > 0 && ( + + + + )} +
+ ); +}); + +AdminSearchBar.displayName = 'AdminSearchBar'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 6e3736793046..14167f25d5b9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import * as reactTestingLibrary from '@testing-library/react'; - import { EndpointList } from './index'; import '../../../../common/mock/match_media.ts'; import { @@ -669,4 +668,98 @@ describe('when on the list page', () => { }); }); }); + + describe('when the more actions column is opened', () => { + let hostInfo: HostInfo; + let agentId: string; + let agentPolicyId: string; + const generator = new EndpointDocGenerator('seed'); + let renderAndWaitForData: () => Promise>; + + const mockEndpointListApi = () => { + const { hosts } = mockEndpointResultList(); + hostInfo = { + host_status: hosts[0].host_status, + metadata: hosts[0].metadata, + }; + const packagePolicy = docGenerator.generatePolicyPackagePolicy(); + packagePolicy.id = hosts[0].metadata.Endpoint.policy.applied.id; + const agentPolicy = generator.generateAgentPolicy(); + agentPolicyId = agentPolicy.id; + agentId = hosts[0].metadata.elastic.agent.id; + + setEndpointListApiMockImplementation(coreStart.http, { + endpointsResults: [hostInfo], + endpointPackagePolicies: [packagePolicy], + agentPolicy, + }); + }; + + beforeEach(() => { + mockEndpointListApi(); + + reactTestingLibrary.act(() => { + history.push('/endpoints'); + }); + + renderAndWaitForData = async () => { + const renderResult = render(); + await middlewareSpy.waitForAction('serverReturnedEndpointList'); + await middlewareSpy.waitForAction('serverReturnedEndpointAgentPolicies'); + return renderResult; + }; + + coreStart.application.getUrlForApp.mockImplementation((appName) => { + switch (appName) { + case 'securitySolution': + return '/app/security'; + case 'ingestManager': + return '/app/ingestManager'; + } + return appName; + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('navigates to the Security Solution Host Details page', async () => { + const renderResult = await renderAndWaitForData(); + // open the endpoint actions menu + const endpointActionsButton = await renderResult.findByTestId('endpointTableRowActions'); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(endpointActionsButton); + }); + + const hostLink = await renderResult.findByTestId('hostLink'); + expect(hostLink.getAttribute('href')).toEqual( + `/app/security/hosts/${hostInfo.metadata.host.hostname}` + ); + }); + it('navigates to the Ingest Agent Policy page', async () => { + const renderResult = await renderAndWaitForData(); + const endpointActionsButton = await renderResult.findByTestId('endpointTableRowActions'); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(endpointActionsButton); + }); + + const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink'); + expect(agentPolicyLink.getAttribute('href')).toEqual( + `/app/ingestManager#/policies/${agentPolicyId}` + ); + }); + it('navigates to the Ingest Agent Details page', async () => { + const renderResult = await renderAndWaitForData(); + const endpointActionsButton = await renderResult.findByTestId('endpointTableRowActions'); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(endpointActionsButton); + }); + + const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink'); + expect(agentDetailsLink.getAttribute('href')).toEqual( + `/app/ingestManager#/fleet/agents/${agentId}` + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index a569c4f02604..166f1660bf3d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useCallback, memo } from 'react'; +import React, { useMemo, useCallback, memo, useState } from 'react'; import { EuiHorizontalRule, EuiBasicTable, @@ -16,12 +16,21 @@ import { EuiSelectableProps, EuiSuperDatePicker, EuiSpacer, + EuiPopover, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiContextMenuPanelProps, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { createStructuredSelector } from 'reselect'; import { useDispatch } from 'react-redux'; +import { EuiContextMenuItemProps } from '@elastic/eui/src/components/context_menu/context_menu_item'; +import { NavigateToAppOptions } from 'kibana/public'; import { EndpointDetailsFlyout } from './details'; import * as selectors from '../store/selectors'; import { useEndpointSelector } from './hooks'; @@ -40,13 +49,17 @@ import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/ import { CreatePackagePolicyRouteState, AgentPolicyDetailsDeployAgentAction, + pagePathGetters, } from '../../../../../../ingest_manager/public'; import { SecurityPageName } from '../../../../app/types'; import { getEndpointListPath, getEndpointDetailsPath } from '../../../common/routing'; import { useFormatUrl } from '../../../../common/components/link_to'; import { EndpointAction } from '../store/action'; import { EndpointPolicyLink } from './components/endpoint_policy_link'; +import { AdminSearchBar } from './components/search_bar'; import { AdministrationListPage } from '../../../components/administration_list_page'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { APP_ID } from '../../../../../common/constants'; const EndpointListNavLink = memo<{ name: string; @@ -70,9 +83,40 @@ const EndpointListNavLink = memo<{ }); EndpointListNavLink.displayName = 'EndpointListNavLink'; +const TableRowActions = memo<{ + items: EuiContextMenuPanelProps['items']; +}>(({ items }) => { + const [isOpen, setIsOpen] = useState(false); + const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); + const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); + + return ( + + } + isOpen={isOpen} + closePopover={handleCloseMenu} + > + + + ); +}); +TableRowActions.displayName = 'EndpointTableRowActions'; + const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); export const EndpointList = () => { const history = useHistory(); + const { services } = useKibana(); const { listData, pageIndex, @@ -87,8 +131,10 @@ export const EndpointList = () => { policyItemsLoading, endpointPackageVersion, endpointsExist, + agentPolicies, autoRefreshInterval, isAutoRefreshEnabled, + patternsError, } = useEndpointSelector(selector); const { formatUrl, search } = useFormatUrl(SecurityPageName.administration); @@ -346,8 +392,87 @@ export const EndpointList = () => { ); }, }, + { + field: '', + name: i18n.translate('xpack.securitySolution.endpoint.list.actions', { + defaultMessage: 'Actions', + }), + actions: [ + { + // eslint-disable-next-line react/display-name + render: (item: HostInfo) => { + return ( + + + , + + + , + + + , + ]} + /> + ); + }, + }, + ], + }, ]; - }, [formatUrl, queryParams, search]); + }, [formatUrl, queryParams, search, agentPolicies, services?.application?.getUrlForApp]); const renderTableOrEmptyState = useMemo(() => { if (endpointsExist) { @@ -397,16 +522,16 @@ export const EndpointList = () => { const hasListData = listData && listData.length > 0; const refreshStyle = useMemo(() => { - return { display: hasListData ? 'flex' : 'none', maxWidth: 200 }; - }, [hasListData]); + return { display: endpointsExist ? 'flex' : 'none', maxWidth: 200 }; + }, [endpointsExist]); const refreshIsPaused = useMemo(() => { - return !hasListData ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled; - }, [hasListData, hasSelectedEndpoint, isAutoRefreshEnabled]); + return !endpointsExist ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled; + }, [endpointsExist, hasSelectedEndpoint, isAutoRefreshEnabled]); const refreshInterval = useMemo(() => { - return !hasListData ? DEFAULT_POLL_INTERVAL : autoRefreshInterval; - }, [hasListData, autoRefreshInterval]); + return !endpointsExist ? DEFAULT_POLL_INTERVAL : autoRefreshInterval; + }, [endpointsExist, autoRefreshInterval]); return ( { } > {hasSelectedEndpoint && } - { - <> -
+ <> + + {endpointsExist && !patternsError && ( + + + + )} + { onRefreshChange={onRefreshChange} isAutoRefreshOnly={true} /> -
- - - } +
+
+ + {hasListData && ( <> @@ -458,3 +588,20 @@ export const EndpointList = () => { ); }; + +const EuiContextMenuItemNavByRouter = memo< + Omit & { + navigateAppId: string; + navigateOptions: NavigateToAppOptions; + children: React.ReactNode; + } +>(({ navigateAppId, navigateOptions, children, ...otherMenuItemProps }) => { + const handleOnClick = useNavigateToAppEventHandler(navigateAppId, navigateOptions); + + return ( + + {children} + + ); +}); +EuiContextMenuItemNavByRouter.displayName = 'EuiContextMenuItemNavByRouter'; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 10bbbbfa7271..9b4eb6a25d1e 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -21,7 +21,6 @@ import { import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { initTelemetry } from './common/lib/telemetry'; import { KibanaServices } from './common/lib/kibana/services'; -import { resilientActionType } from './common/lib/connectors'; import { PluginSetup, PluginStart, @@ -96,8 +95,6 @@ export class Plugin implements IPlugin { const storage = new Storage(localStorage); const [coreStart, startPlugins] = await core.getStartServices(); diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap index db8d047c2ce8..fc0d646fd62c 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/__snapshots__/isometric_taxi_layout.test.ts.snap @@ -212,6 +212,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:0:1", }, "points": Array [ @@ -227,6 +231,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:0:2", }, "points": Array [ @@ -242,6 +250,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:0:8", }, "points": Array [ @@ -287,6 +299,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:1:3", }, "points": Array [ @@ -302,6 +318,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:1:4", }, "points": Array [ @@ -347,6 +367,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:2:5", }, "points": Array [ @@ -362,6 +386,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:2:6", }, "points": Array [ @@ -377,6 +405,10 @@ Object { }, Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:6:7", }, "points": Array [ @@ -584,6 +616,10 @@ Object { "edgeLineSegments": Array [ Object { "metadata": Object { + "elapsedTime": Object { + "duration": "<1", + "durationType": "millisecond", + }, "uniqueId": "edge:0:1", }, "points": Array [ diff --git a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.test.ts b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.test.ts index e6e525334e81..1e2de06ea4af 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/data/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/data/reducer.test.ts @@ -10,8 +10,9 @@ import { dataReducer } from './reducer'; import * as selectors from './selectors'; import { DataState } from '../../types'; import { DataAction } from './action'; -import { ResolverChildNode, ResolverTree } from '../../../../common/endpoint/types'; +import { ResolverChildNode, ResolverEvent, ResolverTree } from '../../../../common/endpoint/types'; import * as eventModel from '../../../../common/endpoint/models/event'; +import { values } from '../../../../common/endpoint/models/ecs_safety_helpers'; import { mockTreeFetcherParameters } from '../../mocks/tree_fetcher_parameters'; /** @@ -40,7 +41,9 @@ describe('Resolver Data Middleware', () => { // Generate a 'tree' using the Resolver generator code. This structure isn't the same as what the API returns. const baseTree = generateBaseTree(); const tree = mockResolverTree({ - events: baseTree.allEvents, + // Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with + // a lot of the frontend functions. So casting it back to the unsafe type for now. + events: baseTree.allEvents as ResolverEvent[], cursors: { childrenNextChild: 'aValidChildCursor', ancestryNextAncestor: 'aValidAncestorCursor', @@ -89,7 +92,9 @@ describe('Resolver Data Middleware', () => { type: 'serverReturnedRelatedEventData', payload: { entityID: firstChildNodeInTree.id, - events: firstChildNodeInTree.relatedEvents, + // Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with + // a lot of the frontend functions. So casting it back to the unsafe type for now. + events: firstChildNodeInTree.relatedEvents as ResolverEvent[], nextEvent: null, }, }; @@ -162,7 +167,9 @@ describe('Resolver Data Middleware', () => { type: 'serverReturnedRelatedEventData', payload: { entityID: firstChildNodeInTree.id, - events: firstChildNodeInTree.relatedEvents, + // Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with + // a lot of the frontend functions. So casting it back to the unsafe type for now. + events: firstChildNodeInTree.relatedEvents as ResolverEvent[], nextEvent: 'aValidNextEventCursor', }, }; @@ -232,7 +239,9 @@ function mockedTree() { const statsResults = compileStatsForChild(firstChildNodeInTree); const tree = mockResolverTree({ - events: baseTree.allEvents, + // Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with + // a lot of the frontend functions. So casting it back to the unsafe type for now. + events: baseTree.allEvents as ResolverEvent[], /** * Calculate children from the ResolverTree response using the children of the `Tree` we generated using the Resolver data generator code. * Compile (and attach) stats to the first child node. @@ -243,14 +252,15 @@ function mockedTree() { * related event limits should be shown. */ children: [...baseTree.children.values()].map((node: TreeNode) => { - // Treat each `TreeNode` as a `ResolverChildNode`. - // These types are almost close enough to be used interchangably (for the purposes of this test.) - const childNode: Partial = node; + const childNode: Partial = {}; + // Casting here because the generator returns the SafeResolverEvent type which isn't yet compatible with + // a lot of the frontend functions. So casting it back to the unsafe type for now. + childNode.lifecycle = node.lifecycle as ResolverEvent[]; // `TreeNode` has `id` which is the same as `entityID`. // The `ResolverChildNode` calls the entityID as `entityID`. // Set `entityID` on `childNode` since the code in test relies on it. - childNode.entityID = (childNode as TreeNode).id; + childNode.entityID = node.id; // This should only be true for the first child. if (node.id === firstChildNodeInTree.id) { @@ -315,10 +325,8 @@ function compileStatsForChild( const compiledStats = node.relatedEvents.reduce( (counts: Record, relatedEvent) => { - // `relatedEvent.event.category` is `string | string[]`. - // Wrap it in an array and flatten that array to get a `string[] | [string]` - // which we can loop over. - const categories: string[] = [relatedEvent.event.category].flat(); + // get an array of categories regardless of whether category is a string or string[] + const categories: string[] = values(relatedEvent.event?.category); for (const category of categories) { // Set the first category as 'categoryToOverCount' diff --git a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx index 6962d300f707..a066eb9421fc 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/assets.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/assets.tsx @@ -24,7 +24,8 @@ type ResolverColorNames = | 'resolverBackground' | 'resolverEdge' | 'resolverEdgeText' - | 'resolverBreadcrumbBackground'; + | 'resolverBreadcrumbBackground' + | 'pillStroke'; type ColorMap = Record; interface NodeStyleConfig { @@ -438,6 +439,7 @@ export const useResolverTheme = (): { resolverBreadcrumbBackground: theme.euiColorLightestShade, resolverEdgeText: getThemedOption(theme.euiColorDarkShade, theme.euiColorFullShade), triggerBackingFill: `${theme.euiColorDanger}${getThemedOption('0F', '1F')}`, + pillStroke: theme.euiColorLightShade, }; const nodeAssets: NodeStyleMap = { @@ -475,7 +477,7 @@ export const useResolverTheme = (): { ), isLabelFilled: false, labelButtonFill: 'primary', - strokeColor: `${theme.euiColorPrimary}33`, // 33 = 20% opacity + strokeColor: theme.euiColorPrimary, }, terminatedTriggerCube: { backingFill: colorMap.triggerBackingFill, @@ -489,7 +491,7 @@ export const useResolverTheme = (): { ), isLabelFilled: false, labelButtonFill: 'danger', - strokeColor: `${theme.euiColorDanger}33`, + strokeColor: theme.euiColorDanger, }, }; diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index 2aacc5f9176c..f4a7ad120e7d 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -10,6 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { htmlIdGenerator, EuiButton, EuiI18nNumber, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSelector } from 'react-redux'; +import { FormattedMessage } from '@kbn/i18n/react'; import { NodeSubMenu, subMenuAssets } from './submenu'; import { applyMatrix3 } from '../models/vector2'; import { Vector2, Matrix3, ResolverState } from '../types'; @@ -38,6 +39,7 @@ const StyledActionsContainer = styled.div` position: absolute; top: ${(props) => `${props.topPct}%`}; width: auto; + pointer-events: all; `; interface StyledDescriptionText { @@ -61,6 +63,11 @@ const StyledDescriptionText = styled.div` width: fit-content; `; +const StyledOuterGroup = styled.g` + fill: none; + pointer-events: visiblePainted; +`; + /** * An artifact that represents a process node and the things associated with it in the Resolver */ @@ -113,6 +120,7 @@ const UnstyledProcessEventDot = React.memo( // Node (html id=) IDs const ariaActiveDescendant = useSelector(selectors.ariaActiveDescendant); const selectedNode = useSelector(selectors.selectedNode); + const originID = useSelector(selectors.originID); const nodeID: string | undefined = eventModel.entityIDSafeVersion(event); if (nodeID === undefined) { // NB: this component should be taking nodeID as a `string` instead of handling this logic here @@ -225,6 +233,7 @@ const UnstyledProcessEventDot = React.memo( const isAriaCurrent = nodeID === ariaActiveDescendant; const isAriaSelected = nodeID === selectedNode; + const isOrigin = nodeID === originID; const dispatch = useResolverDispatch(); @@ -329,6 +338,7 @@ const UnstyledProcessEventDot = React.memo( } role="img" aria-labelledby={labelHTMLID} + fill="none" style={{ display: 'block', width: '100%', @@ -338,9 +348,10 @@ const UnstyledProcessEventDot = React.memo( left: '0', outline: 'transparent', border: 'none', + pointerEvents: 'none', }} > - + + {isOrigin && ( + + )} - + - {descriptionText} +
= 2 ? 'euiButton' : 'euiButton euiButton--small'} @@ -464,6 +496,7 @@ export const ProcessEventDot = styled(UnstyledProcessEventDot)` min-width: 280px; min-height: 90px; overflow-y: visible; + pointer-events: none; //dasharray & dashoffset should be equal to "pull" the stroke back //when it is transitioned. diff --git a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx index 14d6470c9520..adff11ee94cf 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/submenu.tsx @@ -4,23 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-duplicate-imports */ - /* eslint-disable react/display-name */ import { i18n } from '@kbn/i18n'; -import React, { useState, useMemo, useCallback, useRef, useLayoutEffect } from 'react'; -import { - EuiI18nNumber, - EuiSelectable, - EuiButton, - EuiPopover, - ButtonColor, - htmlIdGenerator, -} from '@elastic/eui'; +import React, { useState, useCallback, useRef, useLayoutEffect, useMemo } from 'react'; +import { EuiI18nNumber, EuiButton, EuiPopover, ButtonColor } from '@elastic/eui'; import styled from 'styled-components'; -import { EuiSelectableOption } from '@elastic/eui'; import { Matrix3 } from '../types'; +import { useResolverTheme } from './assets'; /** * i18n-translated titles for submenus and identifiers for display of states: @@ -43,7 +34,7 @@ export const subMenuAssets = { }), }, }; -const idGenerator = htmlIdGenerator(); + interface ResolverSubmenuOption { optionTitle: string; action: () => unknown; @@ -52,73 +43,51 @@ interface ResolverSubmenuOption { export type ResolverSubmenuOptionList = ResolverSubmenuOption[] | string; -const OptionListItem = styled.div` - width: 175px; +const StyledActionButton = styled(EuiButton)` + &.euiButton--small { + height: fit-content; + line-height: 1; + padding: 0.25em; + font-size: 0.85rem; + } `; -const OptionList = React.memo( +/** + * This will be the "host button" that displays the "total number of related events" and opens + * the sumbmenu (with counts by category) when clicked. + */ +const SubButton = React.memo( ({ - subMenuOptions, - isLoading, + hasMenu, + menuIsOpen, + action, + count, + title, + nodeID, }: { - subMenuOptions: ResolverSubmenuOptionList; - isLoading: boolean; + hasMenu: boolean; + menuIsOpen?: boolean; + action: (evt: React.MouseEvent) => void; + count?: number; + title: string; + nodeID: string; }) => { - const [options, setOptions] = useState(() => - typeof subMenuOptions !== 'object' - ? [] - : subMenuOptions.map((option: ResolverSubmenuOption) => { - const dataTestSubj = 'resolver:map:node-submenu-item'; - return option.prefix - ? { - label: option.optionTitle, - prepend: {option.prefix} , - 'data-test-subj': dataTestSubj, - } - : { - label: option.optionTitle, - prepend: , - 'data-test-subj': dataTestSubj, - }; - }) - ); - - const actionsByLabel: Record unknown> = useMemo(() => { - if (typeof subMenuOptions !== 'object') { - return {}; - } - return subMenuOptions.reduce((titleActionRecord, opt) => { - const { optionTitle, action } = opt; - return { ...titleActionRecord, [optionTitle]: action }; - }, {}); - }, [subMenuOptions]); - - const selectableProps = useMemo(() => { - return { - listProps: { showIcons: true, bordered: true }, - onChange: (newOptions: EuiSelectableOption[]) => { - const selectedOption = newOptions.find((opt) => opt.checked === 'on'); - if (selectedOption) { - const { label } = selectedOption; - const actionToTake = actionsByLabel[label]; - if (typeof actionToTake === 'function') { - actionToTake(); - } - } - setOptions(newOptions); - }, - }; - }, [actionsByLabel]); - + const iconType = menuIsOpen === true ? 'arrowUp' : 'arrowDown'; return ( - - {(list) => {list}} - + {count ? : ''} {title} + ); } ); @@ -177,11 +146,6 @@ const NodeSubMenuComponents = React.memo( [menuAction] ); - const closePopover = useCallback(() => setMenuOpen(false), []); - const popoverId = idGenerator('submenu-popover'); - - const isMenuLoading = optionsWithActions === 'waitingForRelatedEventData'; - // The last projection matrix that was used to position the popover const projectionMatrixAtLastRender = useRef(); @@ -204,6 +168,16 @@ const NodeSubMenuComponents = React.memo( projectionMatrixAtLastRender.current = projectionMatrix; }, [projectionMatrixAtLastRender, projectionMatrix]); + const { + colorMap: { pillStroke: pillBorderStroke, resolverBackground: pillFill }, + } = useResolverTheme(); + const listStylesFromTheme = useMemo(() => { + return { + border: `1.5px solid ${pillBorderStroke}`, + backgroundColor: pillFill, + }; + }, [pillBorderStroke, pillFill]); + if (!optionsWithActions) { /** * When called with a `menuAction` @@ -222,44 +196,47 @@ const NodeSubMenuComponents = React.memo(
); } - /** - * When called with a set of `optionsWithActions`: - * Render with a panel of options that appear when the menu host button is clicked - */ - const submenuPopoverButton = ( - - {count ? : ''} {menuTitle} - - ); + if (typeof optionsWithActions === 'string') { + return <>; + } return ( -
- - {menuIsOpen && typeof optionsWithActions === 'object' && ( - - )} - -
+ <> + + {menuIsOpen ? ( +
    + {optionsWithActions + .sort((opta, optb) => { + return opta.optionTitle.localeCompare(optb.optionTitle); + }) + .map((opt) => { + return ( +
  • + +
  • + ); + })} +
+ ) : null} + ); } ); @@ -271,6 +248,48 @@ export const NodeSubMenu = styled(NodeSubMenuComponents)` display: flex; flex-flow: column; + &.options { + font-size: 0.8rem; + display: flex; + flex-flow: row wrap; + background: transparent; + position: absolute; + top: 6.5em; + contain: content; + width: 12em; + z-index: 2; + } + + &.options .item { + margin: 0.25ch 0.35ch 0.35ch 0; + padding: 0.35em 0.5em; + height: fit-content; + width: fit-content; + border-radius: 2px; + line-height: 0.8; + } + + &.options .item button { + appearance: none; + height: fit-content; + width: fit-content; + line-height: 0.8; + outline-style: none; + border-color: transparent; + box-shadow: none; + } + + &.options .item button:focus { + outline-style: none; + border-color: transparent; + box-shadow: none; + text-decoration: underline; + } + + &.options .item button:active { + transform: scale(0.95); + } + & .euiButton { background-color: ${(props) => props.buttonFill}; border-color: ${(props) => props.buttonBorderColor}; @@ -283,16 +302,4 @@ export const NodeSubMenu = styled(NodeSubMenuComponents)` background-color: ${(props) => props.buttonFill}; } } - - & .euiPopover__anchor { - display: flex; - } - - &.is-open .euiButton { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } - &.is-open .euiSelectableListItem__prepend { - color: white; - } `; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts index 54c6cf432aa8..8f68cba89310 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/alerts.ts @@ -5,7 +5,7 @@ */ import { SearchResponse } from 'elasticsearch'; import { esKuery } from '../../../../../../../../src/plugins/data/server'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { ResolverQuery } from './base'; import { PaginationBuilder } from '../utils/pagination'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; @@ -13,7 +13,7 @@ import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/com /** * Builds a query for retrieving alerts for a node. */ -export class AlertsQuery extends ResolverQuery { +export class AlertsQuery extends ResolverQuery { private readonly kqlQuery: JsonObject[] = []; constructor( private readonly pagination: PaginationBuilder, @@ -68,7 +68,7 @@ export class AlertsQuery extends ResolverQuery { }; } - formatResponse(response: SearchResponse): ResolverEvent[] { + formatResponse(response: SearchResponse): SafeResolverEvent[] { return this.getResults(response); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts index 0d8a42d7a26f..a2bdf358745c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/base.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; import { legacyEventIndexPattern } from './legacy_event_index_pattern'; import { MSearchQuery } from './multi_searcher'; @@ -19,7 +19,7 @@ import { MSearchQuery } from './multi_searcher'; * @param R the is the type after transforming ES's response. Making this definable let's us set whether it is a resolver event * or something else. */ -export abstract class ResolverQuery implements MSearchQuery { +export abstract class ResolverQuery implements MSearchQuery { /** * * @param indexPattern the index pattern to use in the query for finding indices with documents in ES. @@ -77,7 +77,7 @@ export abstract class ResolverQuery implements MSearchQuer * @param ids a single more multiple unique node ids (e.g. entity_id or unique_pid) */ async searchAndFormat(client: ILegacyScopedClusterClient, ids: string | string[]): Promise { - const res: SearchResponse = await this.search(client, ids); + const res: SearchResponse = await this.search(client, ids); return this.formatResponse(res); } @@ -113,5 +113,5 @@ export abstract class ResolverQuery implements MSearchQuer * @param response a SearchResponse from ES resulting from executing this query * @returns the translated ES response into a structured object */ - public abstract formatResponse(response: SearchResponse): T; + public abstract formatResponse(response: SearchResponse): T; } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts index 6fb38a32f958..8c7daf945121 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/children.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { SearchResponse } from 'elasticsearch'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { ResolverQuery } from './base'; import { ChildrenPaginationBuilder } from '../utils/children_pagination'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; @@ -12,7 +12,7 @@ import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/com /** * Builds a query for retrieving descendants of a node. */ -export class ChildrenQuery extends ResolverQuery { +export class ChildrenQuery extends ResolverQuery { constructor( private readonly pagination: ChildrenPaginationBuilder, indexPattern: string | string[], @@ -126,7 +126,7 @@ export class ChildrenQuery extends ResolverQuery { }; } - formatResponse(response: SearchResponse): ResolverEvent[] { + formatResponse(response: SearchResponse): SafeResolverEvent[] { return this.getResults(response); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts index 0969a3c360e4..bd054d548a93 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts @@ -5,7 +5,7 @@ */ import { SearchResponse } from 'elasticsearch'; import { esKuery } from '../../../../../../../../src/plugins/data/server'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { ResolverQuery } from './base'; import { PaginationBuilder } from '../utils/pagination'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; @@ -13,7 +13,7 @@ import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/com /** * Builds a query for retrieving related events for a node. */ -export class EventsQuery extends ResolverQuery { +export class EventsQuery extends ResolverQuery { private readonly kqlQuery: JsonObject[] = []; constructor( @@ -83,7 +83,7 @@ export class EventsQuery extends ResolverQuery { }; } - formatResponse(response: SearchResponse): ResolverEvent[] { + formatResponse(response: SearchResponse): SafeResolverEvent[] { return this.getResults(response); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts index 0b5728958e91..ecbc5d834492 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/lifecycle.ts @@ -6,12 +6,12 @@ import { SearchResponse } from 'elasticsearch'; import { ResolverQuery } from './base'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; /** * Builds a query for retrieving life cycle information about a node (start, stop, etc). */ -export class LifecycleQuery extends ResolverQuery { +export class LifecycleQuery extends ResolverQuery { protected legacyQuery(endpointID: string, uniquePIDs: string[]): JsonObject { return { query: { @@ -59,7 +59,7 @@ export class LifecycleQuery extends ResolverQuery { }; } - formatResponse(response: SearchResponse): ResolverEvent[] { + formatResponse(response: SearchResponse): SafeResolverEvent[] { return this.getResults(response); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts index 02dbd92d9252..76203973a621 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/multi_searcher.ts @@ -6,7 +6,7 @@ import { ILegacyScopedClusterClient } from 'kibana/server'; import { MSearchResponse, SearchResponse } from 'elasticsearch'; -import { ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; /** @@ -37,7 +37,7 @@ export interface QueryInfo { /** * a function to handle the response */ - handler: (response: SearchResponse) => void; + handler: (response: SearchResponse) => void; } /** @@ -65,7 +65,7 @@ export class MultiSearcher { for (const info of queries) { searchQuery.push(...info.query.buildMSearch(info.ids)); } - const res: MSearchResponse = await this.client.callAsCurrentUser('msearch', { + const res: MSearchResponse = await this.client.callAsCurrentUser('msearch', { body: searchQuery, }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts index b8fa409e2ca2..50e56258b744 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/stats.ts @@ -5,7 +5,7 @@ */ import { SearchResponse } from 'elasticsearch'; import { ResolverQuery } from './base'; -import { ResolverEvent, EventStats } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent, EventStats } from '../../../../../common/endpoint/types'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; export interface StatsResult { @@ -185,7 +185,7 @@ export class StatsQuery extends ResolverQuery { }; } - public formatResponse(response: SearchResponse): StatsResult { + public formatResponse(response: SearchResponse): StatsResult { let alerts: Record = {}; if (response.aggregations?.alerts?.ids?.buckets) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts index efffbc10473d..f34218ddbde9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/alerts_query_handler.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverRelatedAlerts, ResolverEvent } from '../../../../../common/endpoint/types'; +import { ResolverRelatedAlerts, SafeResolverEvent } from '../../../../../common/endpoint/types'; import { createRelatedAlerts } from './node'; import { AlertsQuery } from '../queries/alerts'; import { PaginationBuilder } from './pagination'; @@ -45,7 +45,7 @@ export class RelatedAlertsQueryHandler implements SingleQueryHandler) => { + private handleResponse = (response: SearchResponse) => { const results = this.query.formatResponse(response); this.relatedAlerts = createRelatedAlerts( this.entityID, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts index 7dd47658bc4c..b796913118c9 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/ancestry_query_handler.ts @@ -7,14 +7,14 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; import { - parentEntityId, - entityId, + parentEntityIDSafeVersion, + entityIDSafeVersion, getAncestryAsArray, } from '../../../../../common/endpoint/models/event'; import { - ResolverAncestry, - ResolverEvent, - ResolverLifecycleNode, + SafeResolverAncestry, + SafeResolverEvent, + SafeResolverLifecycleNode, } from '../../../../../common/endpoint/types'; import { createAncestry, createLifecycle } from './node'; import { LifecycleQuery } from '../queries/lifecycle'; @@ -24,8 +24,8 @@ import { QueryHandler } from './fetch'; /** * Retrieve the ancestry portion of a resolver tree. */ -export class AncestryQueryHandler implements QueryHandler { - private readonly ancestry: ResolverAncestry = createAncestry(); +export class AncestryQueryHandler implements QueryHandler { + private readonly ancestry: SafeResolverAncestry = createAncestry(); private ancestorsToFind: string[]; private readonly query: LifecycleQuery; @@ -33,7 +33,7 @@ export class AncestryQueryHandler implements QueryHandler { private levels: number, indexPattern: string, legacyEndpointID: string | undefined, - originNode: ResolverLifecycleNode | undefined + originNode: SafeResolverLifecycleNode | undefined ) { this.ancestorsToFind = getAncestryAsArray(originNode?.lifecycle[0]).slice(0, levels); this.query = new LifecycleQuery(indexPattern, legacyEndpointID); @@ -41,21 +41,28 @@ export class AncestryQueryHandler implements QueryHandler { // add the origin node to the response if it exists if (originNode) { this.ancestry.ancestors.push(originNode); - this.ancestry.nextAncestor = parentEntityId(originNode.lifecycle[0]) || null; + this.ancestry.nextAncestor = parentEntityIDSafeVersion(originNode.lifecycle[0]) || null; } } - private toMapOfNodes(results: ResolverEvent[]) { - return results.reduce((nodes: Map, event: ResolverEvent) => { - const nodeId = entityId(event); - let node = nodes.get(nodeId); - if (!node) { - node = createLifecycle(nodeId, []); - } + private toMapOfNodes(results: SafeResolverEvent[]) { + return results.reduce( + (nodes: Map, event: SafeResolverEvent) => { + const nodeId = entityIDSafeVersion(event); + if (!nodeId) { + return nodes; + } + + let node = nodes.get(nodeId); + if (!node) { + node = createLifecycle(nodeId, []); + } - node.lifecycle.push(event); - return nodes.set(nodeId, node); - }, new Map()); + node.lifecycle.push(event); + return nodes.set(nodeId, node); + }, + new Map() + ); } private setNoMore() { @@ -64,7 +71,7 @@ export class AncestryQueryHandler implements QueryHandler { this.levels = 0; } - private handleResponse = (searchResp: SearchResponse) => { + private handleResponse = (searchResp: SearchResponse) => { const results = this.query.formatResponse(searchResp); if (results.length === 0) { this.setNoMore(); @@ -97,7 +104,7 @@ export class AncestryQueryHandler implements QueryHandler { * Hence: [D, E, B, C, A] */ this.ancestry.ancestors.push(...ancestryNodes.values()); - this.ancestry.nextAncestor = parentEntityId(results[0]) || null; + this.ancestry.nextAncestor = parentEntityIDSafeVersion(results[0]) || null; this.levels = this.levels - ancestryNodes.size; // the results come back in ascending order on timestamp so the first entry in the // results should be the further ancestor (most distant grandparent) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts index 78e4219aad75..d33e9a2d70af 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.test.ts @@ -10,7 +10,7 @@ import { TreeNode, } from '../../../../../common/endpoint/generate_data'; import { ChildrenNodesHelper } from './children_helper'; -import { eventId, isProcessRunning } from '../../../../../common/endpoint/models/event'; +import { eventIDSafeVersion, isProcessRunning } from '../../../../../common/endpoint/models/event'; function getStartEvents(events: Event[]): Event[] { const startEvents: Event[] = []; @@ -179,7 +179,9 @@ describe('Children helper', () => { childrenNodes.childNodes.forEach((node) => { node.lifecycle.forEach((event) => { - expect(childrenEvents.find((child) => child.event.id === eventId(event))).toEqual(event); + expect( + childrenEvents.find((child) => eventIDSafeVersion(child) === eventIDSafeVersion(event)) + ).toEqual(event); }); }); }); @@ -191,7 +193,9 @@ describe('Children helper', () => { const childrenNodes = helper.getNodes(); childrenNodes.childNodes.forEach((node) => { node.lifecycle.forEach((event) => { - expect(childrenEvents.find((child) => child.event.id === eventId(event))).toEqual(event); + expect( + childrenEvents.find((child) => eventIDSafeVersion(child) === eventIDSafeVersion(event)) + ).toEqual(event); }); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts index b82b972b887b..e9174548898d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_helper.ts @@ -5,15 +5,15 @@ */ import { - entityId, - parentEntityId, + parentEntityIDSafeVersion, isProcessRunning, getAncestryAsArray, + entityIDSafeVersion, } from '../../../../../common/endpoint/models/event'; import { - ResolverChildNode, - ResolverEvent, - ResolverChildren, + SafeResolverChildren, + SafeResolverChildNode, + SafeResolverEvent, } from '../../../../../common/endpoint/types'; import { createChild } from './node'; import { ChildrenPaginationBuilder } from './children_pagination'; @@ -22,7 +22,7 @@ import { ChildrenPaginationBuilder } from './children_pagination'; * This class helps construct the children structure when building a resolver tree. */ export class ChildrenNodesHelper { - private readonly entityToNodeCache: Map = new Map(); + private readonly entityToNodeCache: Map = new Map(); constructor(private readonly rootID: string, private readonly limit: number) { this.entityToNodeCache.set(rootID, createChild(rootID)); @@ -31,8 +31,8 @@ export class ChildrenNodesHelper { /** * Constructs a ResolverChildren response based on the children that were previously add. */ - getNodes(): ResolverChildren { - const cacheCopy: Map = new Map(this.entityToNodeCache); + getNodes(): SafeResolverChildren { + const cacheCopy: Map = new Map(this.entityToNodeCache); const rootNode = cacheCopy.get(this.rootID); let rootNextChild = null; @@ -51,7 +51,7 @@ export class ChildrenNodesHelper { * Get the entity_ids of the nodes that are cached. */ getEntityIDs(): string[] { - const cacheCopy: Map = new Map(this.entityToNodeCache); + const cacheCopy: Map = new Map(this.entityToNodeCache); cacheCopy.delete(this.rootID); return Array.from(cacheCopy.keys()); } @@ -69,9 +69,9 @@ export class ChildrenNodesHelper { * * @param lifecycle an array of resolver lifecycle events for different process nodes returned from ES. */ - addLifecycleEvents(lifecycle: ResolverEvent[]) { + addLifecycleEvents(lifecycle: SafeResolverEvent[]) { for (const event of lifecycle) { - const entityID = entityId(event); + const entityID = entityIDSafeVersion(event); if (entityID) { const cachedChild = this.getOrCreateChildNode(entityID); cachedChild.lifecycle.push(event); @@ -86,19 +86,22 @@ export class ChildrenNodesHelper { * @param queriedNodes the entity_ids of the nodes that returned these start events * @param startEvents an array of start events returned by ES */ - addStartEvents(queriedNodes: Set, startEvents: ResolverEvent[]): Set | undefined { + addStartEvents( + queriedNodes: Set, + startEvents: SafeResolverEvent[] + ): Set | undefined { let largestAncestryArray = 0; const nodesToQueryNext: Map> = new Map(); - const nonLeafNodes: Set = new Set(); + const nonLeafNodes: Set = new Set(); - const isDistantGrandchild = (event: ResolverEvent) => { + const isDistantGrandchild = (event: SafeResolverEvent) => { const ancestry = getAncestryAsArray(event); return ancestry.length > 0 && queriedNodes.has(ancestry[ancestry.length - 1]); }; for (const event of startEvents) { - const parentID = parentEntityId(event); - const entityID = entityId(event); + const parentID = parentEntityIDSafeVersion(event); + const entityID = entityIDSafeVersion(event); if (parentID && entityID && isProcessRunning(event)) { // don't actually add the start event to the node, because that'll be done in // a different call @@ -158,7 +161,7 @@ export class ChildrenNodesHelper { return nodesToQueryNext.get(largestAncestryArray); } - private setPaginationForNodes(nodes: Set, startEvents: ResolverEvent[]) { + private setPaginationForNodes(nodes: Set, startEvents: SafeResolverEvent[]) { for (const nodeEntityID of nodes.values()) { const cachedNode = this.entityToNodeCache.get(nodeEntityID); if (cachedNode) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts index ab610dc9776c..f9f73c2ad75f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/children_lifecycle_query_handler.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverEvent, ResolverChildren } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent, SafeResolverChildren } from '../../../../../common/endpoint/types'; import { LifecycleQuery } from '../queries/lifecycle'; import { QueryInfo } from '../queries/multi_searcher'; import { SingleQueryHandler } from './fetch'; @@ -16,8 +16,8 @@ import { createChildren } from './node'; /** * Returns the children of a resolver tree. */ -export class ChildrenLifecycleQueryHandler implements SingleQueryHandler { - private lifecycle: ResolverChildren | undefined; +export class ChildrenLifecycleQueryHandler implements SingleQueryHandler { + private lifecycle: SafeResolverChildren | undefined; private readonly query: LifecycleQuery; constructor( private readonly childrenHelper: ChildrenNodesHelper, @@ -27,7 +27,7 @@ export class ChildrenLifecycleQueryHandler implements SingleQueryHandler) => { + private handleResponse = (response: SearchResponse) => { this.childrenHelper.addLifecycleEvents(this.query.formatResponse(response)); this.lifecycle = this.childrenHelper.getNodes(); }; @@ -50,7 +50,7 @@ export class ChildrenLifecycleQueryHandler implements SingleQueryHandler) => { + private handleResponse = (response: SearchResponse) => { const results = this.query.formatResponse(response); this.nodesToQuery = this.childrenHelper.addStartEvents(this.nodesToQuery, results) ?? new Set(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/events_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/events_query_handler.ts index 8792f917fb4d..5c4d9a4741ad 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/events_query_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/events_query_handler.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverRelatedEvents, ResolverEvent } from '../../../../../common/endpoint/types'; +import { SafeResolverRelatedEvents, SafeResolverEvent } from '../../../../../common/endpoint/types'; import { createRelatedEvents } from './node'; import { EventsQuery } from '../queries/events'; import { PaginationBuilder } from './pagination'; @@ -28,8 +28,8 @@ export interface RelatedEventsParams { /** * This retrieves the related events for the origin node of a resolver tree. */ -export class RelatedEventsQueryHandler implements SingleQueryHandler { - private relatedEvents: ResolverRelatedEvents | undefined; +export class RelatedEventsQueryHandler implements SingleQueryHandler { + private relatedEvents: SafeResolverRelatedEvents | undefined; private readonly query: EventsQuery; private readonly limit: number; private readonly entityID: string; @@ -46,7 +46,7 @@ export class RelatedEventsQueryHandler implements SingleQueryHandler) => { + private handleResponse = (response: SearchResponse) => { const results = this.query.formatResponse(response); this.relatedEvents = createRelatedEvents( this.entityID, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts index 1b88f965909e..15a9639872f2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/fetch.ts @@ -6,11 +6,11 @@ import { ILegacyScopedClusterClient } from 'kibana/server'; import { - ResolverChildren, - ResolverRelatedEvents, - ResolverAncestry, + SafeResolverChildren, + SafeResolverRelatedEvents, + SafeResolverAncestry, ResolverRelatedAlerts, - ResolverLifecycleNode, + SafeResolverLifecycleNode, } from '../../../../../common/endpoint/types'; import { Tree } from './tree'; import { LifecycleQuery } from '../queries/lifecycle'; @@ -190,7 +190,7 @@ export class Fetcher { * * @param limit upper limit of ancestors to retrieve */ - public async ancestors(limit: number): Promise { + public async ancestors(limit: number): Promise { const originNode = await this.getNode(this.id); const ancestryHandler = new AncestryQueryHandler( limit, @@ -207,7 +207,7 @@ export class Fetcher { * @param limit the number of children to retrieve for a single level * @param after a cursor to use as the starting point for retrieving children */ - public async children(limit: number, after?: string): Promise { + public async children(limit: number, after?: string): Promise { const childrenHandler = new ChildrenStartQueryHandler( limit, this.id, @@ -237,7 +237,7 @@ export class Fetcher { limit: number, after?: string, filter?: string - ): Promise { + ): Promise { const eventsHandler = new RelatedEventsQueryHandler({ limit, entityID: this.id, @@ -285,7 +285,7 @@ export class Fetcher { return tree; } - private async getNode(entityID: string): Promise { + private async getNode(entityID: string): Promise { const query = new LifecycleQuery(this.eventsIndexPattern, this.endpointID); const results = await query.searchAndFormat(this.client, entityID); if (results.length === 0) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts index ab0501e09949..d4dc12d5e8b6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/lifecycle_query_handler.ts @@ -6,7 +6,7 @@ import { SearchResponse } from 'elasticsearch'; import { ILegacyScopedClusterClient } from 'kibana/server'; -import { ResolverEvent, ResolverLifecycleNode } from '../../../../../common/endpoint/types'; +import { SafeResolverEvent, SafeResolverLifecycleNode } from '../../../../../common/endpoint/types'; import { LifecycleQuery } from '../queries/lifecycle'; import { QueryInfo } from '../queries/multi_searcher'; import { SingleQueryHandler } from './fetch'; @@ -15,8 +15,8 @@ import { createLifecycle } from './node'; /** * Retrieve the lifecycle events for a node. */ -export class LifecycleQueryHandler implements SingleQueryHandler { - private lifecycle: ResolverLifecycleNode | undefined; +export class LifecycleQueryHandler implements SingleQueryHandler { + private lifecycle: SafeResolverLifecycleNode | undefined; private readonly query: LifecycleQuery; constructor( private readonly entityID: string, @@ -26,7 +26,7 @@ export class LifecycleQueryHandler implements SingleQueryHandler) => { + private handleResponse = (response: SearchResponse) => { const results = this.query.formatResponse(response); if (results.length !== 0) { this.lifecycle = createLifecycle(this.entityID, results); @@ -51,7 +51,7 @@ export class LifecycleQueryHandler implements SingleQueryHandler { const generator = new EndpointDocGenerator(); - const getSearchAfterInfo = (events: EndpointEvent[]) => { + const getSearchAfterInfo = (events: SafeEndpointEvent[]) => { const lastEvent = events[events.length - 1]; - return [lastEvent['@timestamp'], lastEvent.event.id]; + return [timestampSafeVersion(lastEvent), eventIDSafeVersion(lastEvent)]; }; describe('cursor', () => { const root = generator.generateEvent(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts index 4a6c65e55a6b..af0311a262f3 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ResolverEvent } from '../../../../../common/endpoint/types'; -import { eventId } from '../../../../../common/endpoint/models/event'; +import { SafeResolverEvent } from '../../../../../common/endpoint/types'; +import { + eventIDSafeVersion, + timestampSafeVersion, +} from '../../../../../common/endpoint/models/event'; import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; import { ChildrenPaginationCursor } from './children_pagination'; @@ -116,11 +119,12 @@ export class PaginationBuilder { * * @param results the events that were returned by the ES query */ - static buildCursor(results: ResolverEvent[]): string | null { + static buildCursor(results: SafeResolverEvent[]): string | null { const lastResult = results[results.length - 1]; const cursor = { - timestamp: lastResult['@timestamp'], - eventID: eventId(lastResult) === undefined ? '' : String(eventId(lastResult)), + timestamp: timestampSafeVersion(lastResult) ?? 0, + eventID: + eventIDSafeVersion(lastResult) === undefined ? '' : String(eventIDSafeVersion(lastResult)), }; return urlEncodeCursor(cursor); } @@ -131,7 +135,10 @@ export class PaginationBuilder { * @param requestLimit the request limit for a query. * @param results the events that were returned by the ES query */ - static buildCursorRequestLimit(requestLimit: number, results: ResolverEvent[]): string | null { + static buildCursorRequestLimit( + requestLimit: number, + results: SafeResolverEvent[] + ): string | null { if (requestLimit <= results.length && results.length > 0) { return PaginationBuilder.buildCursor(results); } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts index 21db11f3affd..290af87a61b1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.test.ts @@ -7,28 +7,28 @@ import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { Tree } from './tree'; import { - ResolverAncestry, - ResolverEvent, - ResolverRelatedEvents, + SafeResolverAncestry, + SafeResolverEvent, + SafeResolverRelatedEvents, } from '../../../../../common/endpoint/types'; -import { entityId } from '../../../../../common/endpoint/models/event'; +import { entityIDSafeVersion } from '../../../../../common/endpoint/models/event'; describe('Tree', () => { const generator = new EndpointDocGenerator(); describe('ancestry', () => { // transform the generator's array of events into the format expected by the tree class - const ancestorInfo: ResolverAncestry = { + const ancestorInfo: SafeResolverAncestry = { ancestors: generator .createAlertEventAncestry({ ancestors: 5, percentTerminated: 0, percentWithRelated: 0 }) .filter((event) => { - return event.event.kind === 'event'; + return event.event?.kind === 'event'; }) .map((event) => { return { - entityID: event.process.entity_id, + entityID: entityIDSafeVersion(event) ?? '', // The generator returns Events, but the tree needs a ResolverEvent - lifecycle: [event as ResolverEvent], + lifecycle: [event as SafeResolverEvent], }; }), nextAncestor: 'hello', @@ -39,7 +39,7 @@ describe('Tree', () => { const ids = tree.ids(); ids.forEach((id) => { const foundAncestor = ancestorInfo.ancestors.find( - (ancestor) => entityId(ancestor.lifecycle[0]) === id + (ancestor) => entityIDSafeVersion(ancestor.lifecycle[0]) === id ); expect(foundAncestor).not.toBeUndefined(); }); @@ -50,12 +50,12 @@ describe('Tree', () => { describe('related events', () => { it('adds related events to the tree', () => { const root = generator.generateEvent(); - const events: ResolverRelatedEvents = { - entityID: root.process.entity_id, + const events: SafeResolverRelatedEvents = { + entityID: entityIDSafeVersion(root) ?? '', events: Array.from(generator.relatedEventsGenerator(root)), nextEvent: null, }; - const tree = new Tree(root.process.entity_id, { relatedEvents: events }); + const tree = new Tree(entityIDSafeVersion(root) ?? '', { relatedEvents: events }); const rendered = tree.render(); expect(rendered.relatedEvents.nextEvent).toBeNull(); expect(rendered.relatedEvents.events).toStrictEqual(events.events); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts index 3f941851a414..dd493d70ffcd 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/tree.ts @@ -6,26 +6,26 @@ import _ from 'lodash'; import { - ResolverEvent, + SafeResolverEvent, ResolverNodeStats, - ResolverRelatedEvents, - ResolverAncestry, - ResolverTree, - ResolverChildren, + SafeResolverRelatedEvents, + SafeResolverAncestry, + SafeResolverTree, + SafeResolverChildren, ResolverRelatedAlerts, } from '../../../../../common/endpoint/types'; import { createTree } from './node'; interface Node { entityID: string; - lifecycle: ResolverEvent[]; + lifecycle: SafeResolverEvent[]; stats?: ResolverNodeStats; } export interface Options { - relatedEvents?: ResolverRelatedEvents; - ancestry?: ResolverAncestry; - children?: ResolverChildren; + relatedEvents?: SafeResolverRelatedEvents; + ancestry?: SafeResolverAncestry; + children?: SafeResolverChildren; relatedAlerts?: ResolverRelatedAlerts; } @@ -37,7 +37,7 @@ export interface Options { */ export class Tree { protected cache: Map = new Map(); - protected tree: ResolverTree; + protected tree: SafeResolverTree; constructor(protected readonly id: string, options: Options = {}) { const tree = createTree(this.id); @@ -55,7 +55,7 @@ export class Tree { * * @returns the origin ResolverNode */ - public render(): ResolverTree { + public render(): SafeResolverTree { return this.tree; } @@ -73,7 +73,7 @@ export class Tree { * * @param relatedEventsInfo is the related events and pagination information to add to the tree. */ - private addRelatedEvents(relatedEventsInfo: ResolverRelatedEvents | undefined) { + private addRelatedEvents(relatedEventsInfo: SafeResolverRelatedEvents | undefined) { if (!relatedEventsInfo) { return; } @@ -101,7 +101,7 @@ export class Tree { * * @param ancestorInfo is the ancestors and pagination information to add to the tree. */ - private addAncestors(ancestorInfo: ResolverAncestry | undefined) { + private addAncestors(ancestorInfo: SafeResolverAncestry | undefined) { if (!ancestorInfo) { return; } @@ -132,7 +132,7 @@ export class Tree { } } - private addChildren(children: ResolverChildren | undefined) { + private addChildren(children: SafeResolverChildren | undefined) { if (!children) { return; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 1f4790a8981c..d203c6dcc48c 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -171,7 +171,7 @@ export class Plugin implements IPlugin, params: {}, + hostName: 'bastion00.siem.estc.dev', } as HostDetailsRequestOptions; export const mockSearchStrategyResponse: IEsSearchResponse = { @@ -589,860 +590,7 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { { key: 'tsg', doc_count: 1, - failures: { - doc_count: 0, - lastFailure: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - successes: { - doc_count: 1, - lastSuccess: { - hits: { - total: 1, - max_score: 0, - hits: [ - { - _index: '.ds-logs-system.auth-default-000001', - _id: '9_sfWXQBc39KFIJbIsDh', - _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20764 }, - log: { file: { path: '/var/log/auth.log' }, offset: 552463 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'DE-BE', - city_name: 'Berlin', - country_iso_code: 'DE', - region_name: 'Land Berlin', - location: { lon: 13.3512, lat: 52.5727 }, - }, - as: { number: 6805, organization: { name: 'Telefonica Germany' } }, - port: 57457, - ip: '77.183.42.188', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:49:21.000Z', - system: { - auth: { - ssh: { - method: 'publickey', - signature: 'RSA SHA256:vv64JNLzKZWYA9vonnGWuW7zxWhyZrL/BFxyIGbISx8', - event: 'Accepted', - }, - }, - }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_success', - category: 'authentication', - dataset: 'system.auth', - outcome: 'success', - }, - user: { name: 'tsg' }, - }, - sort: [1599220161000], - }, - ], - }, - }, - }, - }, - { - key: 'admin', - doc_count: 23, - failures: { - doc_count: 23, - lastFailure: { - hits: { - total: 23, - max_score: 0, - hits: [ - { - _index: '.ds-logs-system.auth-default-000001', - _id: 'ZfxZWXQBc39KFIJbLN5U', - _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 22913 }, - log: { file: { path: '/var/log/auth.log' }, offset: 562910 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'KR-28', - city_name: 'Incheon', - country_iso_code: 'KR', - region_name: 'Incheon', - location: { lon: 126.7288, lat: 37.4562 }, - }, - as: { number: 4766, organization: { name: 'Korea Telecom' } }, - ip: '59.15.3.197', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:40:46.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'admin' }, - }, - sort: [1599226846000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'user', - doc_count: 21, - failures: { - doc_count: 21, - lastFailure: { - hits: { - total: 21, - max_score: 0, - hits: [ - { - _index: 'filebeat-8.0.0-2020.09.02-000001', - _id: 'M_xLWXQBc39KFIJbY7Cb', - _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20671 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1028103 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:43.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['user'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:47.034172Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'user' }, - }, - sort: [1599225943000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'ubuntu', - doc_count: 18, - failures: { - doc_count: 18, - lastFailure: { - hits: { - total: 18, - max_score: 0, - hits: [ - { - _index: 'filebeat-8.0.0-2020.09.02-000001', - _id: 'nPxKWXQBc39KFIJb7q4w', - _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - type: 'filebeat', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20665 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1027372 }, - source: { - geo: { - continent_name: 'North America', - region_iso_code: 'US-NY', - city_name: 'New York', - country_iso_code: 'US', - region_name: 'New York', - location: { lon: -74, lat: 40.7157 }, - }, - ip: '64.227.88.245', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T13:25:07.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['64.227.88.245'], user: ['ubuntu'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T13:25:16.974606Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'ubuntu' }, - }, - sort: [1599225907000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'odoo', - doc_count: 17, - failures: { - doc_count: 17, - lastFailure: { - hits: { - total: 17, - max_score: 0, - hits: [ - { - _index: '.ds-logs-system.auth-default-000001', - _id: 'mPsfWXQBc39KFIJbI8HI', - _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - type: 'filebeat', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 21506 }, - log: { file: { path: '/var/log/auth.log' }, offset: 556761 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'IN-DL', - city_name: 'New Delhi', - country_iso_code: 'IN', - region_name: 'National Capital Territory of Delhi', - location: { lon: 77.2245, lat: 28.6358 }, - }, - as: { number: 10029, organization: { name: 'SHYAM SPECTRA PVT LTD' } }, - ip: '180.151.228.166', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T12:26:36.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'odoo' }, - }, - sort: [1599222396000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'pi', - doc_count: 17, - failures: { - doc_count: 17, - lastFailure: { - hits: { - total: 17, - max_score: 0, - hits: [ - { - _index: 'filebeat-8.0.0-2020.09.02-000001', - _id: 'aaToWHQBA6bGZw2uR-St', - _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 20475 }, - log: { file: { path: '/var/log/auth.log' }, offset: 1019218 }, - source: { - geo: { - continent_name: 'Europe', - region_iso_code: 'SE-AB', - city_name: 'Stockholm', - country_iso_code: 'SE', - region_name: 'Stockholm', - location: { lon: 17.7833, lat: 59.25 }, - }, - as: { number: 8473, organization: { name: 'Bahnhof AB' } }, - ip: '178.174.148.58', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:37:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['178.174.148.58'], user: ['pi'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T11:37:31.797423Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'pi' }, - }, - sort: [1599219442000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'demo', - doc_count: 14, - failures: { - doc_count: 14, - lastFailure: { - hits: { - total: 14, - max_score: 0, - hits: [ - { - _index: 'filebeat-8.0.0-2020.09.02-000001', - _id: 'VaP_V3QBA6bGZw2upUbg', - _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19849 }, - log: { file: { path: '/var/log/auth.log' }, offset: 981036 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:23:22.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['demo'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:23:26.046346Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'demo' }, - }, - sort: [1599204202000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'git', - doc_count: 13, - failures: { - doc_count: 13, - lastFailure: { - hits: { - total: 13, - max_score: 0, - hits: [ - { - _index: '.ds-logs-system.auth-default-000001', - _id: 'PqYfWXQBA6bGZw2uIhVU', - _score: null, - _source: { - agent: { - hostname: 'siem-kibana', - name: 'siem-kibana', - id: 'aa3d9dc7-fef1-4c2f-a68d-25785d624e35', - ephemeral_id: 'e503bd85-11c7-4bc9-ae7d-70be1d919fb7', - type: 'filebeat', - version: '7.9.1', - }, - process: { name: 'sshd', pid: 20396 }, - log: { file: { path: '/var/log/auth.log' }, offset: 550795 }, - source: { - geo: { - continent_name: 'Asia', - region_iso_code: 'CN-BJ', - city_name: 'Beijing', - country_iso_code: 'CN', - region_name: 'Beijing', - location: { lon: 116.3889, lat: 39.9288 }, - }, - as: { - number: 45090, - organization: { - name: 'Shenzhen Tencent Computer Systems Company Limited', - }, - }, - ip: '123.206.30.76', - }, - cloud: { - availability_zone: 'us-east1-b', - instance: { name: 'siem-kibana', id: '5412578377715150143' }, - provider: 'gcp', - machine: { type: 'n1-standard-2' }, - project: { id: 'elastic-beats' }, - }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T11:20:26.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - data_stream: { namespace: 'default', type: 'logs', dataset: 'system.auth' }, - host: { - hostname: 'siem-kibana', - os: { - kernel: '4.9.0-8-amd64', - codename: 'stretch', - name: 'Debian GNU/Linux', - family: 'debian', - version: '9 (stretch)', - platform: 'debian', - }, - containerized: false, - ip: ['10.142.0.7', 'fe80::4001:aff:fe8e:7'], - name: 'siem-kibana', - id: 'aa7ca589f1b8220002f2fc61c64cfbf1', - mac: ['42:01:0a:8e:00:07'], - architecture: 'x86_64', - }, - event: { - timezone: '+00:00', - action: 'ssh_login', - type: 'authentication_failure', - category: 'authentication', - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'git' }, - }, - sort: [1599218426000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - { - key: 'webadmin', - doc_count: 13, - failures: { - doc_count: 13, - lastFailure: { - hits: { - total: 13, - max_score: 0, - hits: [ - { - _index: 'filebeat-8.0.0-2020.09.02-000001', - _id: 'iMABWHQBB-gskclyitP-', - _score: null, - _source: { - agent: { - name: 'bastion00.siem.estc.dev', - id: 'f9a321c1-ec27-49fa-aacf-6a50ef6d836f', - type: 'filebeat', - ephemeral_id: '734ee3da-1a4f-4bc9-b400-e0cf0e5eeebc', - version: '8.0.0', - }, - process: { name: 'sshd', pid: 19870 }, - log: { file: { path: '/var/log/auth.log' }, offset: 984133 }, - source: { - geo: { - continent_name: 'Europe', - country_iso_code: 'HR', - location: { lon: 15.5, lat: 45.1667 }, - }, - as: { - number: 42864, - organization: { name: 'Giganet Internet Szolgaltato Kft' }, - }, - ip: '45.95.168.157', - }, - fileset: { name: 'auth' }, - input: { type: 'log' }, - '@timestamp': '2020-09-04T07:25:28.000Z', - system: { auth: { ssh: { event: 'Invalid' } } }, - ecs: { version: '1.5.0' }, - related: { ip: ['45.95.168.157'], user: ['webadmin'] }, - service: { type: 'system' }, - host: { hostname: 'bastion00', name: 'bastion00.siem.estc.dev' }, - event: { - ingested: '2020-09-04T07:25:30.236651Z', - timezone: '+00:00', - kind: 'event', - module: 'system', - action: 'ssh_login', - type: ['authentication_failure', 'info'], - category: ['authentication'], - dataset: 'system.auth', - outcome: 'failure', - }, - user: { name: 'webadmin' }, - }, - sort: [1599204328000], - }, - ], - }, - }, - }, - successes: { - doc_count: 0, - lastSuccess: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - }, - ], - }, - user_count: { value: 188 }, - }, - }, - total: 21, - loaded: 21, -}; - -export const formattedSearchStrategyResponse = { - isPartial: false, - isRunning: false, - rawResponse: { - took: 14, - timed_out: false, - _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, - hits: { total: -1, max_score: 0, hits: [] }, - aggregations: { - group_by_users: { - doc_count_error_upper_bound: -1, - sum_other_doc_count: 408, - buckets: [ - { - key: 'SYSTEM', - doc_count: 281, - failures: { - meta: {}, - doc_count: 0, - lastFailure: { hits: { total: 0, max_score: 0, hits: [] } }, - }, - successes: { - meta: {}, - doc_count: 4, - lastSuccess: { - hits: { - total: 4, - max_score: 0, - hits: [ - { - _index: 'winlogbeat-8.0.0-2020.09.02-000001', - _id: 'zqY7WXQBA6bGZw2uLeKI', - _score: null, - _source: { - process: { - name: 'services.exe', - pid: 564, - executable: 'C:\\Windows\\System32\\services.exe', - }, - agent: { - build_date: '2020-07-16 09:16:27 +0000 UTC ', - name: 'siem-windows', - commit: '4dcbde39492bdc3843034bba8db811c68cb44b97 ', - id: '05e1bff7-d7a8-416a-8554-aa10288fa07d', - type: 'winlogbeat', - ephemeral_id: '655abd6c-6c33-435d-a2eb-79b2a01e6d61', - version: '8.0.0', - user: { name: 'inside_winlogbeat_user' }, - }, - winlog: { - computer_name: 'siem-windows', - process: { pid: 576, thread: { id: 880 } }, - keywords: ['Audit Success'], - logon: { id: '0x3e7', type: 'Service' }, - channel: 'Security', - event_data: { - LogonGuid: '{00000000-0000-0000-0000-000000000000}', - TargetOutboundDomainName: '-', - VirtualAccount: '%%1843', - LogonType: '5', - IpPort: '-', - TransmittedServices: '-', - SubjectLogonId: '0x3e7', - LmPackageName: '-', - TargetOutboundUserName: '-', - KeyLength: '0', - TargetLogonId: '0x3e7', - RestrictedAdminMode: '-', - SubjectUserName: 'SIEM-WINDOWS$', - TargetLinkedLogonId: '0x0', - ElevatedToken: '%%1842', - SubjectDomainName: 'WORKGROUP', - IpAddress: '-', - ImpersonationLevel: '%%1833', - TargetUserName: 'SYSTEM', - LogonProcessName: 'Advapi ', - TargetDomainName: 'NT AUTHORITY', - SubjectUserSid: 'S-1-5-18', - TargetUserSid: 'S-1-5-18', - AuthenticationPackageName: 'Negotiate', - }, - opcode: 'Info', - version: 2, - record_id: 57818, - task: 'Logon', - event_id: 4624, - provider_guid: '{54849625-5478-4994-a5ba-3e3b0328c30d}', - activity_id: '{d2485217-6bac-0000-8fbb-3f7e2571d601}', - api: 'wineventlog', - provider_name: 'Microsoft-Windows-Security-Auditing', - }, - log: { level: 'information' }, - source: { domain: '-' }, - message: - 'An account was successfully logged on.\n\nSubject:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSIEM-WINDOWS$\n\tAccount Domain:\t\tWORKGROUP\n\tLogon ID:\t\t0x3E7\n\nLogon Information:\n\tLogon Type:\t\t5\n\tRestricted Admin Mode:\t-\n\tVirtual Account:\t\tNo\n\tElevated Token:\t\tYes\n\nImpersonation Level:\t\tImpersonation\n\nNew Logon:\n\tSecurity ID:\t\tS-1-5-18\n\tAccount Name:\t\tSYSTEM\n\tAccount Domain:\t\tNT AUTHORITY\n\tLogon ID:\t\t0x3E7\n\tLinked Logon ID:\t\t0x0\n\tNetwork Account Name:\t-\n\tNetwork Account Domain:\t-\n\tLogon GUID:\t\t{00000000-0000-0000-0000-000000000000}\n\nProcess Information:\n\tProcess ID:\t\t0x234\n\tProcess Name:\t\tC:\\Windows\\System32\\services.exe\n\nNetwork Information:\n\tWorkstation Name:\t-\n\tSource Network Address:\t-\n\tSource Port:\t\t-\n\nDetailed Authentication Information:\n\tLogon Process:\t\tAdvapi \n\tAuthentication Package:\tNegotiate\n\tTransited Services:\t-\n\tPackage Name (NTLM only):\t-\n\tKey Length:\t\t0\n\nThis event is generated when a logon session is created. It is generated on the computer that was accessed.\n\nThe subject fields indicate the account on the local system which requested the logon. This is most commonly a service such as the Server service, or a local process such as Winlogon.exe or Services.exe.\n\nThe logon type field indicates the kind of logon that occurred. The most common types are 2 (interactive) and 3 (network).\n\nThe New Logon fields indicate the account for whom the new logon was created, i.e. the account that was logged on.\n\nThe network fields indicate where a remote logon request originated. Workstation name is not always available and may be left blank in some cases.\n\nThe impersonation level field indicates the extent to which a process in the logon session can impersonate.\n\nThe authentication information fields provide detailed information about this specific logon request.\n\t- Logon GUID is a unique identifier that can be used to correlate this event with a KDC event.\n\t- Transited services indicate which intermediate services have participated in this logon request.\n\t- Package name indicates which sub-protocol was used among the NTLM protocols.\n\t- Key length indicates the length of the generated session key. This will be 0 if no session key was requested.', - cloud: { - availability_zone: 'us-central1-c', - instance: { name: 'siem-windows', id: '9156726559029788564' }, - provider: 'gcp', - machine: { type: 'g1-small' }, - project: { id: 'elastic-siem' }, - }, - '@timestamp': '2020-09-04T13:08:02.532Z', - related: { user: ['SYSTEM', 'SIEM-WINDOWS$'] }, - ecs: { version: '1.5.0' }, - host: { - hostname: 'siem-windows', - os: { - build: '17763.1397', - kernel: '10.0.17763.1397 (WinBuild.160101.0800)', - name: 'Windows Server 2019 Datacenter', - family: 'windows', - version: '10.0', - platform: 'windows', - }, - ip: ['fe80::ecf5:decc:3ec3:767e', '10.200.0.15'], - name: 'siem-windows', - id: 'ce1d3c9b-a815-4643-9641-ada0f2c00609', - mac: ['42:01:0a:c8:00:0f'], - architecture: 'x86_64', - }, - event: { - code: 4624, - provider: 'Microsoft-Windows-Security-Auditing', - created: '2020-09-04T13:08:03.638Z', - kind: 'event', - module: 'security', - action: 'logged-in', - category: 'authentication', - type: 'start', - outcome: 'success', - }, - user: { domain: 'NT AUTHORITY', name: 'SYSTEM', id: 'S-1-5-18' }, - }, - sort: [1599224882532], - }, - ], - }, - }, - }, - }, - { - key: 'tsg', - doc_count: 1, - failures: { - doc_count: 0, - lastFailure: { hits: { total: 0, max_score: 0, hits: [] } }, - }, + failures: { doc_count: 0, lastFailure: { hits: { total: 0, max_score: 0, hits: [] } } }, successes: { doc_count: 1, lastSuccess: { @@ -2143,192 +1291,235 @@ export const formattedSearchStrategyResponse = { }, total: 21, loaded: 21, +}; + +export const formattedSearchStrategyResponse = { inspect: { dsl: [ - '{\n "allowNoIndices": true,\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "user_count": {\n "cardinality": {\n "field": "user.name"\n }\n },\n "group_by_users": {\n "terms": {\n "size": 10,\n "field": "user.name",\n "order": [\n {\n "successes.doc_count": "desc"\n },\n {\n "failures.doc_count": "desc"\n }\n ]\n },\n "aggs": {\n "failures": {\n "filter": {\n "term": {\n "event.outcome": "failure"\n }\n },\n "aggs": {\n "lastFailure": {\n "top_hits": {\n "size": 1,\n "_source": [],\n "sort": [\n {\n "@timestamp": {\n "order": "desc"\n }\n }\n ]\n }\n }\n }\n },\n "successes": {\n "filter": {\n "term": {\n "event.outcome": "success"\n }\n },\n "aggs": {\n "lastSuccess": {\n "top_hits": {\n "size": 1,\n "_source": [],\n "sort": [\n {\n "@timestamp": {\n "order": "desc"\n }\n }\n ]\n }\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}}],\\"should\\":[],\\"must_not\\":[]}}",\n {\n "term": {\n "event.category": "authentication"\n }\n },\n {\n "range": {\n "@timestamp": {\n "gte": "2020-09-02T15:17:13.678Z",\n "lte": "2020-09-03T15:17:13.678Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0\n },\n "track_total_hits": false\n}', + '{\n "allowNoIndices": true,\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "host_architecture": {\n "terms": {\n "field": "host.architecture",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_id": {\n "terms": {\n "field": "host.id",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_ip": {\n "terms": {\n "field": "host.ip",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_mac": {\n "terms": {\n "field": "host.mac",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_name": {\n "terms": {\n "field": "host.name",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_family": {\n "terms": {\n "field": "host.os.family",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_name": {\n "terms": {\n "field": "host.os.name",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_platform": {\n "terms": {\n "field": "host.os.platform",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_version": {\n "terms": {\n "field": "host.os.version",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_instance_id": {\n "terms": {\n "field": "cloud.instance.id",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_machine_type": {\n "terms": {\n "field": "cloud.machine.type",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_provider": {\n "terms": {\n "field": "cloud.provider",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_region": {\n "terms": {\n "field": "cloud.region",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n {\n "term": {\n "host.name": "bastion00.siem.estc.dev"\n }\n },\n {\n "range": {\n "@timestamp": {\n "format": "strict_date_optional_time",\n "gte": "2020-09-02T15:17:13.678Z",\n "lte": "2020-09-03T15:17:13.678Z"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": false\n }\n}', ], }, - edges: [ - { - node: { - failures: 0, - successes: 4, - _id: 'SYSTEM+281', - user: { name: ['SYSTEM'] }, - lastSuccess: { - timestamp: ['2020-09-04T13:08:02.532Z'], - host: { id: ['ce1d3c9b-a815-4643-9641-ada0f2c00609'], name: ['siem-windows'] }, + hostDetails: {}, +}; + +export const expectedDsl = { + allowNoIndices: true, + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + ignoreUnavailable: true, + body: { + aggregations: { + host_architecture: { + terms: { + field: 'host.architecture', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, + }, + }, + host_id: { + terms: { + field: 'host.id', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 0, - successes: 1, - _id: 'tsg+1', - user: { name: ['tsg'] }, - lastSuccess: { - timestamp: ['2020-09-04T11:49:21.000Z'], - source: { ip: ['77.183.42.188'] }, - host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, + host_ip: { + terms: { + field: 'host.ip', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 23, - successes: 0, - _id: 'admin+23', - user: { name: ['admin'] }, - lastFailure: { - timestamp: ['2020-09-04T13:40:46.000Z'], - source: { ip: ['59.15.3.197'] }, - host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, + host_mac: { + terms: { + field: 'host.mac', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 21, - successes: 0, - _id: 'user+21', - user: { name: ['user'] }, - lastFailure: { - timestamp: ['2020-09-04T13:25:43.000Z'], - source: { ip: ['64.227.88.245'] }, - host: { name: ['bastion00.siem.estc.dev'] }, + host_name: { + terms: { + field: 'host.name', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 18, - successes: 0, - _id: 'ubuntu+18', - user: { name: ['ubuntu'] }, - lastFailure: { - timestamp: ['2020-09-04T13:25:07.000Z'], - source: { ip: ['64.227.88.245'] }, - host: { name: ['bastion00.siem.estc.dev'] }, + host_os_family: { + terms: { + field: 'host.os.family', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 17, - successes: 0, - _id: 'odoo+17', - user: { name: ['odoo'] }, - lastFailure: { - timestamp: ['2020-09-04T12:26:36.000Z'], - source: { ip: ['180.151.228.166'] }, - host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, + host_os_name: { + terms: { + field: 'host.os.name', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 17, - successes: 0, - _id: 'pi+17', - user: { name: ['pi'] }, - lastFailure: { - timestamp: ['2020-09-04T11:37:22.000Z'], - source: { ip: ['178.174.148.58'] }, - host: { name: ['bastion00.siem.estc.dev'] }, + host_os_platform: { + terms: { + field: 'host.os.platform', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 14, - successes: 0, - _id: 'demo+14', - user: { name: ['demo'] }, - lastFailure: { - timestamp: ['2020-09-04T07:23:22.000Z'], - source: { ip: ['45.95.168.157'] }, - host: { name: ['bastion00.siem.estc.dev'] }, + host_os_version: { + terms: { + field: 'host.os.version', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 13, - successes: 0, - _id: 'git+13', - user: { name: ['git'] }, - lastFailure: { - timestamp: ['2020-09-04T11:20:26.000Z'], - source: { ip: ['123.206.30.76'] }, - host: { id: ['aa7ca589f1b8220002f2fc61c64cfbf1'], name: ['siem-kibana'] }, + cloud_instance_id: { + terms: { + field: 'cloud.instance.id', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - { - node: { - failures: 13, - successes: 0, - _id: 'webadmin+13', - user: { name: ['webadmin'] }, - lastFailure: { - timestamp: ['2020-09-04T07:25:28.000Z'], - source: { ip: ['45.95.168.157'] }, - host: { name: ['bastion00.siem.estc.dev'] }, + cloud_machine_type: { + terms: { + field: 'cloud.machine.type', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', + }, + }, }, }, - cursor: { value: '', tiebreaker: null }, - }, - ], - totalCount: 188, - pageInfo: { activePage: 0, fakeTotalCount: 50, showMorePagesIndicator: true }, -}; - -export const expectedDsl = { - allowNoIndices: true, - index: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'logs-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - ignoreUnavailable: true, - body: { - aggregations: { - user_count: { cardinality: { field: 'user.name' } }, - group_by_users: { + cloud_provider: { terms: { + field: 'cloud.provider', size: 10, - field: 'user.name', - order: [{ 'successes.doc_count': 'desc' }, { 'failures.doc_count': 'desc' }], + order: { + timestamp: 'desc', + }, }, aggs: { - failures: { - filter: { term: { 'event.outcome': 'failure' } }, - aggs: { - lastFailure: { - top_hits: { size: 1, _source: [], sort: [{ '@timestamp': { order: 'desc' } }] }, - }, + timestamp: { + max: { + field: '@timestamp', }, }, - successes: { - filter: { term: { 'event.outcome': 'success' } }, - aggs: { - lastSuccess: { - top_hits: { size: 1, _source: [], sort: [{ '@timestamp': { order: 'desc' } }] }, - }, + }, + }, + cloud_region: { + terms: { + field: 'cloud.region', + size: 10, + order: { + timestamp: 'desc', + }, + }, + aggs: { + timestamp: { + max: { + field: '@timestamp', }, }, }, @@ -2337,14 +1528,17 @@ export const expectedDsl = { query: { bool: { filter: [ - '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', - { term: { 'event.category': 'authentication' } }, + { + term: { + 'host.name': 'bastion00.siem.estc.dev', + }, + }, { range: { '@timestamp': { + format: 'strict_date_optional_time', gte: '2020-09-02T15:17:13.678Z', lte: '2020-09-03T15:17:13.678Z', - format: 'strict_date_optional_time', }, }, }, @@ -2352,21 +1546,6 @@ export const expectedDsl = { }, }, size: 0, + track_total_hits: false, }, - track_total_hits: false, -}; - -export const mockHit: AuthenticationHit = { - _index: 'index-123', - _type: 'type-123', - _id: 'id-123', - _score: 10, - _source: { - '@timestamp': 'time-1', - }, - cursor: 'cursor-1', - sort: [0], - user: 'Evan', - failures: 10, - successes: 20, }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts new file mode 100644 index 000000000000..73cf74087aad --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts @@ -0,0 +1,1305 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEsSearchResponse } from '../../../../../../../../../src/plugins/data/common'; + +import { MatrixHistogramStrategyResponse } from '../../../../../../common/search_strategy'; + +export const mockAlertsSearchStrategyResponse: IEsSearchResponse = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 11, + timed_out: false, + _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, + hits: { total: 0, max_score: 0, hits: [] }, + aggregations: { + alertsGroup: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + }, + total: 21, + loaded: 21, +}; + +export const formattedAlertsSearchStrategyResponse: MatrixHistogramStrategyResponse = { + ...mockAlertsSearchStrategyResponse, + inspect: { + dsl: [ + '{\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "allowNoIndices": true,\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "alertsGroup": {\n "terms": {\n "field": "event.module",\n "missing": "All others",\n "order": {\n "_count": "desc"\n },\n "size": 10\n },\n "aggs": {\n "alerts": {\n "date_histogram": {\n "field": "@timestamp",\n "fixed_interval": "2700000ms",\n "min_doc_count": 0,\n "extended_bounds": {\n "min": 1599574984482,\n "max": 1599661384482\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"exists\\":{\\"field\\":\\"host.name\\"}}],\\"minimum_should_match\\":1}}]}}],\\"should\\":[],\\"must_not\\":[]}}",\n {\n "bool": {\n "filter": [\n {\n "bool": {\n "should": [\n {\n "match": {\n "event.kind": "alert"\n }\n }\n ],\n "minimum_should_match": 1\n }\n }\n ]\n }\n },\n {\n "range": {\n "@timestamp": {\n "gte": "2020-09-08T14:23:04.482Z",\n "lte": "2020-09-09T14:23:04.482Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": true\n }\n}', + ], + }, + matrixHistogramData: [], + totalCount: 0, +}; + +export const expectedDsl = { + allowNoIndices: true, + body: { + aggregations: { + host_count: { cardinality: { field: 'host.name' } }, + host_data: { + aggs: { + lastSeen: { max: { field: '@timestamp' } }, + os: { + top_hits: { + _source: { includes: ['host.os.*'] }, + size: 1, + sort: [{ '@timestamp': { order: 'desc' } }], + }, + }, + }, + terms: { field: 'host.name', order: { lastSeen: 'desc' }, size: 10 }, + }, + }, + query: { + bool: { + filter: [ + { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: '2020-09-03T09:15:21.415Z', + lte: '2020-09-04T09:15:21.415Z', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: false, + }, + ignoreUnavailable: true, + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], +}; + +export const mockAnomaliesSearchStrategyResponse: IEsSearchResponse = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 9, + timed_out: false, + _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, + hits: { total: 0, max_score: 0, hits: [] }, + aggregations: { + anomalyActionGroup: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + }, + total: 21, + loaded: 21, +}; + +export const formattedAnomaliesSearchStrategyResponse: MatrixHistogramStrategyResponse = { + ...mockAnomaliesSearchStrategyResponse, + inspect: { + dsl: [ + '{\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "allowNoIndices": true,\n "ignoreUnavailable": true,\n "body": {\n "aggs": {\n "anomalyActionGroup": {\n "terms": {\n "field": "job_id",\n "order": {\n "_count": "desc"\n },\n "size": 10\n },\n "aggs": {\n "anomalies": {\n "date_histogram": {\n "field": "timestamp",\n "fixed_interval": "2700000ms",\n "min_doc_count": 0,\n "extended_bounds": {\n "min": 1599578075566,\n "max": 1599664475566\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}},{\\"bool\\":{\\"should\\":[],\\"minimum_should_match\\":1}},{\\"match_phrase\\":{\\"result_type\\":\\"record\\"}},null,{\\"range\\":{\\"record_score\\":{\\"gte\\":50}}}],\\"should\\":[{\\"exists\\":{\\"field\\":\\"source.ip\\"}},{\\"exists\\":{\\"field\\":\\"destination.ip\\"}}],\\"must_not\\":[],\\"minimum_should_match\\":1}}",\n {\n "range": {\n "timestamp": {\n "gte": "2020-09-08T15:14:35.566Z",\n "lte": "2020-09-09T15:14:35.566Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": true\n }\n}', + ], + }, + matrixHistogramData: [], + totalCount: 0, +}; + +export const mockAuthenticationsSearchStrategyResponse: IEsSearchResponse = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 6, + timed_out: false, + _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, + hits: { total: 0, max_score: 0, hits: [] }, + aggregations: { + eventActionGroup: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'failure', + doc_count: 379, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:00:00.000Z', key: 1599577200000, doc_count: 1 }, + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 4 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 34 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 1 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 3 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 20 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 24 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 37 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 4 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 22 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 3 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 0 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 2 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 21 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 28 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 30 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 19 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 4 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 1 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 6 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 18 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 5 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 23 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 15 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 2 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 0 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 5 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 2 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 4 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 6 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 11 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 0 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 24 }, + ], + }, + }, + { + key: 'success', + doc_count: 191, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:00:00.000Z', key: 1599577200000, doc_count: 2 }, + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 5 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 5 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 2 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 4 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 6 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 4 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 13 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 6 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 3 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 1 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 9 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 5 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 6 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 8 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 2 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 9 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 2 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 5 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 2 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 14 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 7 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 13 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 10 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 5 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 2 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 6 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 7 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 5 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 6 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 5 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 10 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 2 }, + ], + }, + }, + ], + }, + }, + }, + total: 21, + loaded: 21, +}; + +export const formattedAuthenticationsSearchStrategyResponse: MatrixHistogramStrategyResponse = { + ...mockAuthenticationsSearchStrategyResponse, + inspect: { + dsl: [ + '{\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "allowNoIndices": true,\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "eventActionGroup": {\n "terms": {\n "field": "event.outcome",\n "include": [\n "success",\n "failure"\n ],\n "order": {\n "_count": "desc"\n },\n "size": 2\n },\n "aggs": {\n "events": {\n "date_histogram": {\n "field": "@timestamp",\n "fixed_interval": "2700000ms",\n "min_doc_count": 0,\n "extended_bounds": {\n "min": 1599578520325,\n "max": 1599664920325\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}}],\\"should\\":[],\\"must_not\\":[]}}",\n {\n "bool": {\n "must": [\n {\n "term": {\n "event.category": "authentication"\n }\n }\n ]\n }\n },\n {\n "range": {\n "@timestamp": {\n "gte": "2020-09-08T15:22:00.325Z",\n "lte": "2020-09-09T15:22:00.325Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": true\n }\n}', + ], + }, + matrixHistogramData: [ + { x: 1599577200000, y: 1, g: 'failure' }, + { x: 1599579900000, y: 4, g: 'failure' }, + { x: 1599582600000, y: 34, g: 'failure' }, + { x: 1599585300000, y: 1, g: 'failure' }, + { x: 1599588000000, y: 3, g: 'failure' }, + { x: 1599590700000, y: 20, g: 'failure' }, + { x: 1599593400000, y: 24, g: 'failure' }, + { x: 1599596100000, y: 37, g: 'failure' }, + { x: 1599598800000, y: 4, g: 'failure' }, + { x: 1599601500000, y: 22, g: 'failure' }, + { x: 1599604200000, y: 3, g: 'failure' }, + { x: 1599606900000, y: 0, g: 'failure' }, + { x: 1599609600000, y: 2, g: 'failure' }, + { x: 1599612300000, y: 21, g: 'failure' }, + { x: 1599615000000, y: 28, g: 'failure' }, + { x: 1599617700000, y: 30, g: 'failure' }, + { x: 1599620400000, y: 19, g: 'failure' }, + { x: 1599623100000, y: 4, g: 'failure' }, + { x: 1599625800000, y: 1, g: 'failure' }, + { x: 1599628500000, y: 6, g: 'failure' }, + { x: 1599631200000, y: 18, g: 'failure' }, + { x: 1599633900000, y: 5, g: 'failure' }, + { x: 1599636600000, y: 23, g: 'failure' }, + { x: 1599639300000, y: 15, g: 'failure' }, + { x: 1599642000000, y: 2, g: 'failure' }, + { x: 1599644700000, y: 0, g: 'failure' }, + { x: 1599647400000, y: 5, g: 'failure' }, + { x: 1599650100000, y: 2, g: 'failure' }, + { x: 1599652800000, y: 4, g: 'failure' }, + { x: 1599655500000, y: 6, g: 'failure' }, + { x: 1599658200000, y: 11, g: 'failure' }, + { x: 1599660900000, y: 0, g: 'failure' }, + { x: 1599663600000, y: 24, g: 'failure' }, + { x: 1599577200000, y: 2, g: 'success' }, + { x: 1599579900000, y: 5, g: 'success' }, + { x: 1599582600000, y: 5, g: 'success' }, + { x: 1599585300000, y: 2, g: 'success' }, + { x: 1599588000000, y: 4, g: 'success' }, + { x: 1599590700000, y: 6, g: 'success' }, + { x: 1599593400000, y: 4, g: 'success' }, + { x: 1599596100000, y: 13, g: 'success' }, + { x: 1599598800000, y: 6, g: 'success' }, + { x: 1599601500000, y: 3, g: 'success' }, + { x: 1599604200000, y: 1, g: 'success' }, + { x: 1599606900000, y: 9, g: 'success' }, + { x: 1599609600000, y: 5, g: 'success' }, + { x: 1599612300000, y: 6, g: 'success' }, + { x: 1599615000000, y: 8, g: 'success' }, + { x: 1599617700000, y: 2, g: 'success' }, + { x: 1599620400000, y: 9, g: 'success' }, + { x: 1599623100000, y: 2, g: 'success' }, + { x: 1599625800000, y: 5, g: 'success' }, + { x: 1599628500000, y: 2, g: 'success' }, + { x: 1599631200000, y: 14, g: 'success' }, + { x: 1599633900000, y: 7, g: 'success' }, + { x: 1599636600000, y: 13, g: 'success' }, + { x: 1599639300000, y: 10, g: 'success' }, + { x: 1599642000000, y: 5, g: 'success' }, + { x: 1599644700000, y: 2, g: 'success' }, + { x: 1599647400000, y: 6, g: 'success' }, + { x: 1599650100000, y: 7, g: 'success' }, + { x: 1599652800000, y: 5, g: 'success' }, + { x: 1599655500000, y: 6, g: 'success' }, + { x: 1599658200000, y: 5, g: 'success' }, + { x: 1599660900000, y: 10, g: 'success' }, + { x: 1599663600000, y: 2, g: 'success' }, + ], + totalCount: 0, +}; + +export const mockEventsSearchStrategyResponse: IEsSearchResponse = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 198, + timed_out: false, + _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, + hits: { total: 0, max_score: 0, hits: [] }, + aggregations: { + eventActionGroup: { + doc_count_error_upper_bound: 3, + sum_other_doc_count: 4090, + buckets: [ + { + key: 'All others', + doc_count: 1556741, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 26124 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 62910 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 60326 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 56144 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 53614 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53228 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 61195 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 52082 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 52697 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 41094 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 50164 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 41500 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 42373 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 49785 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 42237 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 43114 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 40716 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 39248 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 37674 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 41072 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 37049 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 38561 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 40895 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 45490 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 46559 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 40020 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 44335 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 47252 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 48744 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 55756 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 56887 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 66920 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 40976 }, + ], + }, + }, + { + key: 'end', + doc_count: 18413, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 226 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 547 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 532 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 551 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 543 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 547 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 656 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 543 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 616 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 539 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 539 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 547 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 616 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 640 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 614 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 545 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 537 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 544 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 571 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 743 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 560 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 598 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 613 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 563 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 540 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 538 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 549 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 561 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 554 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 561 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 542 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 712 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 326 }, + ], + }, + }, + { + key: 'fork', + doc_count: 18412, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 226 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 546 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 532 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 551 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 543 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 547 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 656 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 543 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 616 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 539 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 539 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 547 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 616 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 640 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 614 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 545 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 537 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 544 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 571 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 743 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 560 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 598 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 613 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 563 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 540 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 538 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 549 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 561 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 554 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 561 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 542 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 712 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 326 }, + ], + }, + }, + { + key: 'exec', + doc_count: 15183, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 189 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 456 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 445 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 458 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 455 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 457 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 511 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 455 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 493 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 451 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 453 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 460 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 521 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 504 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 490 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 457 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 447 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 454 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 469 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 642 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 465 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 481 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 489 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 466 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 452 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 448 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 457 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 471 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 460 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 463 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 455 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 547 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 262 }, + ], + }, + }, + { + key: 'disconnect_received', + doc_count: 4998, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 59 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 151 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 139 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 144 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 143 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 144 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 202 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 142 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 180 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 144 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 143 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 137 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 150 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 195 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 178 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 144 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 143 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 142 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 157 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 166 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 153 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 168 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 175 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 158 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 142 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 144 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 147 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 139 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 145 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 158 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 137 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 234 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 95 }, + ], + }, + }, + { + key: 'connection_attempted', + doc_count: 4534, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 60 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 145 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 138 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 144 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 140 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 144 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 145 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 137 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 142 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 142 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 143 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 132 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 153 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 143 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 142 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 143 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 142 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 140 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 140 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 148 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 142 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 139 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 139 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 142 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 142 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 143 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 141 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 137 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 141 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 144 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 138 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 145 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 78 }, + ], + }, + }, + { + key: 'creation', + doc_count: 1880, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 24 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 53 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 50 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 54 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 55 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 54 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 54 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 55 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 52 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 51 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 58 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 122 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 54 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 54 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 56 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 53 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 55 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 51 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 144 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 54 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 53 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 51 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 57 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 55 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 52 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 52 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 57 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 56 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 53 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 51 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 56 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 31 }, + ], + }, + }, + { + key: 'deletion', + doc_count: 1869, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 23 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 53 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 50 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 54 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 54 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 53 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 54 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 55 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 52 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 51 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 55 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 121 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 54 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 53 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 55 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 53 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 54 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 51 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 146 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 54 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 53 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 51 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 55 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 55 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 52 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 55 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 55 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 55 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 53 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 51 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 55 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 31 }, + ], + }, + }, + { + key: 'File Delete (rule: FileDelete)', + doc_count: 1831, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 19 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 46 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 47 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 47 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 47 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 45 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 48 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 46 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 45 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 47 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 47 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 60 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 45 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 46 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 46 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 47 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 88 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 53 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 46 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 49 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 45 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 48 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 46 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 46 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 45 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 47 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 47 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 45 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 331 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 45 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 47 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 47 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 28 }, + ], + }, + }, + { + key: 'session_id_change', + doc_count: 647, + events: { + buckets: [ + { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 3 }, + { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 9 }, + { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 7 }, + { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 10 }, + { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 7 }, + { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 10 }, + { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 63 }, + { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 7 }, + { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 45 }, + { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 4 }, + { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 5 }, + { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 6 }, + { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 6 }, + { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 55 }, + { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 43 }, + { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 8 }, + { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 9 }, + { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 7 }, + { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 21 }, + { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 26 }, + { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 17 }, + { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 34 }, + { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 41 }, + { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 18 }, + { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 4 }, + { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 11 }, + { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 9 }, + { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 7 }, + { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 12 }, + { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 16 }, + { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 7 }, + { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 99 }, + { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 21 }, + ], + }, + }, + ], + }, + }, + }, + total: 21, + loaded: 21, +}; + +export const formattedEventsSearchStrategyResponse: MatrixHistogramStrategyResponse = { + ...mockEventsSearchStrategyResponse, + inspect: { + dsl: [ + '{\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "allowNoIndices": true,\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "eventActionGroup": {\n "terms": {\n "field": "event.action",\n "missing": "All others",\n "order": {\n "_count": "desc"\n },\n "size": 10\n },\n "aggs": {\n "events": {\n "date_histogram": {\n "field": "@timestamp",\n "fixed_interval": "2700000ms",\n "min_doc_count": 0,\n "extended_bounds": {\n "min": 1599581486215,\n "max": 1599667886215\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}}],\\"should\\":[],\\"must_not\\":[]}}",\n {\n "range": {\n "@timestamp": {\n "gte": "2020-09-08T16:11:26.215Z",\n "lte": "2020-09-09T16:11:26.215Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": true\n }\n}', + ], + }, + totalCount: 0, + matrixHistogramData: [ + { x: 1599579900000, y: 26124, g: 'All others' }, + { x: 1599582600000, y: 62910, g: 'All others' }, + { x: 1599585300000, y: 60326, g: 'All others' }, + { x: 1599588000000, y: 56144, g: 'All others' }, + { x: 1599590700000, y: 53614, g: 'All others' }, + { x: 1599593400000, y: 53228, g: 'All others' }, + { x: 1599596100000, y: 61195, g: 'All others' }, + { x: 1599598800000, y: 52082, g: 'All others' }, + { x: 1599601500000, y: 52697, g: 'All others' }, + { x: 1599604200000, y: 41094, g: 'All others' }, + { x: 1599606900000, y: 50164, g: 'All others' }, + { x: 1599609600000, y: 41500, g: 'All others' }, + { x: 1599612300000, y: 42373, g: 'All others' }, + { x: 1599615000000, y: 49785, g: 'All others' }, + { x: 1599617700000, y: 42237, g: 'All others' }, + { x: 1599620400000, y: 43114, g: 'All others' }, + { x: 1599623100000, y: 40716, g: 'All others' }, + { x: 1599625800000, y: 39248, g: 'All others' }, + { x: 1599628500000, y: 37674, g: 'All others' }, + { x: 1599631200000, y: 41072, g: 'All others' }, + { x: 1599633900000, y: 37049, g: 'All others' }, + { x: 1599636600000, y: 38561, g: 'All others' }, + { x: 1599639300000, y: 40895, g: 'All others' }, + { x: 1599642000000, y: 45490, g: 'All others' }, + { x: 1599644700000, y: 46559, g: 'All others' }, + { x: 1599647400000, y: 40020, g: 'All others' }, + { x: 1599650100000, y: 44335, g: 'All others' }, + { x: 1599652800000, y: 47252, g: 'All others' }, + { x: 1599655500000, y: 48744, g: 'All others' }, + { x: 1599658200000, y: 55756, g: 'All others' }, + { x: 1599660900000, y: 56887, g: 'All others' }, + { x: 1599663600000, y: 66920, g: 'All others' }, + { x: 1599666300000, y: 40976, g: 'All others' }, + { x: 1599579900000, y: 226, g: 'end' }, + { x: 1599582600000, y: 547, g: 'end' }, + { x: 1599585300000, y: 532, g: 'end' }, + { x: 1599588000000, y: 551, g: 'end' }, + { x: 1599590700000, y: 543, g: 'end' }, + { x: 1599593400000, y: 547, g: 'end' }, + { x: 1599596100000, y: 656, g: 'end' }, + { x: 1599598800000, y: 543, g: 'end' }, + { x: 1599601500000, y: 616, g: 'end' }, + { x: 1599604200000, y: 539, g: 'end' }, + { x: 1599606900000, y: 539, g: 'end' }, + { x: 1599609600000, y: 547, g: 'end' }, + { x: 1599612300000, y: 616, g: 'end' }, + { x: 1599615000000, y: 640, g: 'end' }, + { x: 1599617700000, y: 614, g: 'end' }, + { x: 1599620400000, y: 545, g: 'end' }, + { x: 1599623100000, y: 537, g: 'end' }, + { x: 1599625800000, y: 544, g: 'end' }, + { x: 1599628500000, y: 571, g: 'end' }, + { x: 1599631200000, y: 743, g: 'end' }, + { x: 1599633900000, y: 560, g: 'end' }, + { x: 1599636600000, y: 598, g: 'end' }, + { x: 1599639300000, y: 613, g: 'end' }, + { x: 1599642000000, y: 563, g: 'end' }, + { x: 1599644700000, y: 540, g: 'end' }, + { x: 1599647400000, y: 538, g: 'end' }, + { x: 1599650100000, y: 549, g: 'end' }, + { x: 1599652800000, y: 561, g: 'end' }, + { x: 1599655500000, y: 554, g: 'end' }, + { x: 1599658200000, y: 561, g: 'end' }, + { x: 1599660900000, y: 542, g: 'end' }, + { x: 1599663600000, y: 712, g: 'end' }, + { x: 1599666300000, y: 326, g: 'end' }, + { x: 1599579900000, y: 226, g: 'fork' }, + { x: 1599582600000, y: 546, g: 'fork' }, + { x: 1599585300000, y: 532, g: 'fork' }, + { x: 1599588000000, y: 551, g: 'fork' }, + { x: 1599590700000, y: 543, g: 'fork' }, + { x: 1599593400000, y: 547, g: 'fork' }, + { x: 1599596100000, y: 656, g: 'fork' }, + { x: 1599598800000, y: 543, g: 'fork' }, + { x: 1599601500000, y: 616, g: 'fork' }, + { x: 1599604200000, y: 539, g: 'fork' }, + { x: 1599606900000, y: 539, g: 'fork' }, + { x: 1599609600000, y: 547, g: 'fork' }, + { x: 1599612300000, y: 616, g: 'fork' }, + { x: 1599615000000, y: 640, g: 'fork' }, + { x: 1599617700000, y: 614, g: 'fork' }, + { x: 1599620400000, y: 545, g: 'fork' }, + { x: 1599623100000, y: 537, g: 'fork' }, + { x: 1599625800000, y: 544, g: 'fork' }, + { x: 1599628500000, y: 571, g: 'fork' }, + { x: 1599631200000, y: 743, g: 'fork' }, + { x: 1599633900000, y: 560, g: 'fork' }, + { x: 1599636600000, y: 598, g: 'fork' }, + { x: 1599639300000, y: 613, g: 'fork' }, + { x: 1599642000000, y: 563, g: 'fork' }, + { x: 1599644700000, y: 540, g: 'fork' }, + { x: 1599647400000, y: 538, g: 'fork' }, + { x: 1599650100000, y: 549, g: 'fork' }, + { x: 1599652800000, y: 561, g: 'fork' }, + { x: 1599655500000, y: 554, g: 'fork' }, + { x: 1599658200000, y: 561, g: 'fork' }, + { x: 1599660900000, y: 542, g: 'fork' }, + { x: 1599663600000, y: 712, g: 'fork' }, + { x: 1599666300000, y: 326, g: 'fork' }, + { x: 1599579900000, y: 189, g: 'exec' }, + { x: 1599582600000, y: 456, g: 'exec' }, + { x: 1599585300000, y: 445, g: 'exec' }, + { x: 1599588000000, y: 458, g: 'exec' }, + { x: 1599590700000, y: 455, g: 'exec' }, + { x: 1599593400000, y: 457, g: 'exec' }, + { x: 1599596100000, y: 511, g: 'exec' }, + { x: 1599598800000, y: 455, g: 'exec' }, + { x: 1599601500000, y: 493, g: 'exec' }, + { x: 1599604200000, y: 451, g: 'exec' }, + { x: 1599606900000, y: 453, g: 'exec' }, + { x: 1599609600000, y: 460, g: 'exec' }, + { x: 1599612300000, y: 521, g: 'exec' }, + { x: 1599615000000, y: 504, g: 'exec' }, + { x: 1599617700000, y: 490, g: 'exec' }, + { x: 1599620400000, y: 457, g: 'exec' }, + { x: 1599623100000, y: 447, g: 'exec' }, + { x: 1599625800000, y: 454, g: 'exec' }, + { x: 1599628500000, y: 469, g: 'exec' }, + { x: 1599631200000, y: 642, g: 'exec' }, + { x: 1599633900000, y: 465, g: 'exec' }, + { x: 1599636600000, y: 481, g: 'exec' }, + { x: 1599639300000, y: 489, g: 'exec' }, + { x: 1599642000000, y: 466, g: 'exec' }, + { x: 1599644700000, y: 452, g: 'exec' }, + { x: 1599647400000, y: 448, g: 'exec' }, + { x: 1599650100000, y: 457, g: 'exec' }, + { x: 1599652800000, y: 471, g: 'exec' }, + { x: 1599655500000, y: 460, g: 'exec' }, + { x: 1599658200000, y: 463, g: 'exec' }, + { x: 1599660900000, y: 455, g: 'exec' }, + { x: 1599663600000, y: 547, g: 'exec' }, + { x: 1599666300000, y: 262, g: 'exec' }, + { x: 1599579900000, y: 59, g: 'disconnect_received' }, + { x: 1599582600000, y: 151, g: 'disconnect_received' }, + { x: 1599585300000, y: 139, g: 'disconnect_received' }, + { x: 1599588000000, y: 144, g: 'disconnect_received' }, + { x: 1599590700000, y: 143, g: 'disconnect_received' }, + { x: 1599593400000, y: 144, g: 'disconnect_received' }, + { x: 1599596100000, y: 202, g: 'disconnect_received' }, + { x: 1599598800000, y: 142, g: 'disconnect_received' }, + { x: 1599601500000, y: 180, g: 'disconnect_received' }, + { x: 1599604200000, y: 144, g: 'disconnect_received' }, + { x: 1599606900000, y: 143, g: 'disconnect_received' }, + { x: 1599609600000, y: 137, g: 'disconnect_received' }, + { x: 1599612300000, y: 150, g: 'disconnect_received' }, + { x: 1599615000000, y: 195, g: 'disconnect_received' }, + { x: 1599617700000, y: 178, g: 'disconnect_received' }, + { x: 1599620400000, y: 144, g: 'disconnect_received' }, + { x: 1599623100000, y: 143, g: 'disconnect_received' }, + { x: 1599625800000, y: 142, g: 'disconnect_received' }, + { x: 1599628500000, y: 157, g: 'disconnect_received' }, + { x: 1599631200000, y: 166, g: 'disconnect_received' }, + { x: 1599633900000, y: 153, g: 'disconnect_received' }, + { x: 1599636600000, y: 168, g: 'disconnect_received' }, + { x: 1599639300000, y: 175, g: 'disconnect_received' }, + { x: 1599642000000, y: 158, g: 'disconnect_received' }, + { x: 1599644700000, y: 142, g: 'disconnect_received' }, + { x: 1599647400000, y: 144, g: 'disconnect_received' }, + { x: 1599650100000, y: 147, g: 'disconnect_received' }, + { x: 1599652800000, y: 139, g: 'disconnect_received' }, + { x: 1599655500000, y: 145, g: 'disconnect_received' }, + { x: 1599658200000, y: 158, g: 'disconnect_received' }, + { x: 1599660900000, y: 137, g: 'disconnect_received' }, + { x: 1599663600000, y: 234, g: 'disconnect_received' }, + { x: 1599666300000, y: 95, g: 'disconnect_received' }, + { x: 1599579900000, y: 60, g: 'connection_attempted' }, + { x: 1599582600000, y: 145, g: 'connection_attempted' }, + { x: 1599585300000, y: 138, g: 'connection_attempted' }, + { x: 1599588000000, y: 144, g: 'connection_attempted' }, + { x: 1599590700000, y: 140, g: 'connection_attempted' }, + { x: 1599593400000, y: 144, g: 'connection_attempted' }, + { x: 1599596100000, y: 145, g: 'connection_attempted' }, + { x: 1599598800000, y: 137, g: 'connection_attempted' }, + { x: 1599601500000, y: 142, g: 'connection_attempted' }, + { x: 1599604200000, y: 142, g: 'connection_attempted' }, + { x: 1599606900000, y: 143, g: 'connection_attempted' }, + { x: 1599609600000, y: 132, g: 'connection_attempted' }, + { x: 1599612300000, y: 153, g: 'connection_attempted' }, + { x: 1599615000000, y: 143, g: 'connection_attempted' }, + { x: 1599617700000, y: 142, g: 'connection_attempted' }, + { x: 1599620400000, y: 143, g: 'connection_attempted' }, + { x: 1599623100000, y: 142, g: 'connection_attempted' }, + { x: 1599625800000, y: 140, g: 'connection_attempted' }, + { x: 1599628500000, y: 140, g: 'connection_attempted' }, + { x: 1599631200000, y: 148, g: 'connection_attempted' }, + { x: 1599633900000, y: 142, g: 'connection_attempted' }, + { x: 1599636600000, y: 139, g: 'connection_attempted' }, + { x: 1599639300000, y: 139, g: 'connection_attempted' }, + { x: 1599642000000, y: 142, g: 'connection_attempted' }, + { x: 1599644700000, y: 142, g: 'connection_attempted' }, + { x: 1599647400000, y: 143, g: 'connection_attempted' }, + { x: 1599650100000, y: 141, g: 'connection_attempted' }, + { x: 1599652800000, y: 137, g: 'connection_attempted' }, + { x: 1599655500000, y: 141, g: 'connection_attempted' }, + { x: 1599658200000, y: 144, g: 'connection_attempted' }, + { x: 1599660900000, y: 138, g: 'connection_attempted' }, + { x: 1599663600000, y: 145, g: 'connection_attempted' }, + { x: 1599666300000, y: 78, g: 'connection_attempted' }, + { x: 1599579900000, y: 24, g: 'creation' }, + { x: 1599582600000, y: 53, g: 'creation' }, + { x: 1599585300000, y: 50, g: 'creation' }, + { x: 1599588000000, y: 54, g: 'creation' }, + { x: 1599590700000, y: 55, g: 'creation' }, + { x: 1599593400000, y: 53, g: 'creation' }, + { x: 1599596100000, y: 54, g: 'creation' }, + { x: 1599598800000, y: 54, g: 'creation' }, + { x: 1599601500000, y: 55, g: 'creation' }, + { x: 1599604200000, y: 52, g: 'creation' }, + { x: 1599606900000, y: 51, g: 'creation' }, + { x: 1599609600000, y: 58, g: 'creation' }, + { x: 1599612300000, y: 122, g: 'creation' }, + { x: 1599615000000, y: 54, g: 'creation' }, + { x: 1599617700000, y: 54, g: 'creation' }, + { x: 1599620400000, y: 56, g: 'creation' }, + { x: 1599623100000, y: 53, g: 'creation' }, + { x: 1599625800000, y: 55, g: 'creation' }, + { x: 1599628500000, y: 51, g: 'creation' }, + { x: 1599631200000, y: 144, g: 'creation' }, + { x: 1599633900000, y: 54, g: 'creation' }, + { x: 1599636600000, y: 53, g: 'creation' }, + { x: 1599639300000, y: 51, g: 'creation' }, + { x: 1599642000000, y: 57, g: 'creation' }, + { x: 1599644700000, y: 55, g: 'creation' }, + { x: 1599647400000, y: 52, g: 'creation' }, + { x: 1599650100000, y: 52, g: 'creation' }, + { x: 1599652800000, y: 57, g: 'creation' }, + { x: 1599655500000, y: 56, g: 'creation' }, + { x: 1599658200000, y: 53, g: 'creation' }, + { x: 1599660900000, y: 51, g: 'creation' }, + { x: 1599663600000, y: 56, g: 'creation' }, + { x: 1599666300000, y: 31, g: 'creation' }, + { x: 1599579900000, y: 23, g: 'deletion' }, + { x: 1599582600000, y: 53, g: 'deletion' }, + { x: 1599585300000, y: 50, g: 'deletion' }, + { x: 1599588000000, y: 54, g: 'deletion' }, + { x: 1599590700000, y: 54, g: 'deletion' }, + { x: 1599593400000, y: 53, g: 'deletion' }, + { x: 1599596100000, y: 53, g: 'deletion' }, + { x: 1599598800000, y: 54, g: 'deletion' }, + { x: 1599601500000, y: 55, g: 'deletion' }, + { x: 1599604200000, y: 52, g: 'deletion' }, + { x: 1599606900000, y: 51, g: 'deletion' }, + { x: 1599609600000, y: 55, g: 'deletion' }, + { x: 1599612300000, y: 121, g: 'deletion' }, + { x: 1599615000000, y: 54, g: 'deletion' }, + { x: 1599617700000, y: 53, g: 'deletion' }, + { x: 1599620400000, y: 55, g: 'deletion' }, + { x: 1599623100000, y: 53, g: 'deletion' }, + { x: 1599625800000, y: 54, g: 'deletion' }, + { x: 1599628500000, y: 51, g: 'deletion' }, + { x: 1599631200000, y: 146, g: 'deletion' }, + { x: 1599633900000, y: 54, g: 'deletion' }, + { x: 1599636600000, y: 53, g: 'deletion' }, + { x: 1599639300000, y: 51, g: 'deletion' }, + { x: 1599642000000, y: 55, g: 'deletion' }, + { x: 1599644700000, y: 55, g: 'deletion' }, + { x: 1599647400000, y: 52, g: 'deletion' }, + { x: 1599650100000, y: 55, g: 'deletion' }, + { x: 1599652800000, y: 55, g: 'deletion' }, + { x: 1599655500000, y: 55, g: 'deletion' }, + { x: 1599658200000, y: 53, g: 'deletion' }, + { x: 1599660900000, y: 51, g: 'deletion' }, + { x: 1599663600000, y: 55, g: 'deletion' }, + { x: 1599666300000, y: 31, g: 'deletion' }, + { x: 1599579900000, y: 19, g: 'File Delete (rule: FileDelete)' }, + { x: 1599582600000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599585300000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599588000000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599590700000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599593400000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599596100000, y: 48, g: 'File Delete (rule: FileDelete)' }, + { x: 1599598800000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599601500000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599604200000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599606900000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599609600000, y: 60, g: 'File Delete (rule: FileDelete)' }, + { x: 1599612300000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599615000000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599617700000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599620400000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599623100000, y: 88, g: 'File Delete (rule: FileDelete)' }, + { x: 1599625800000, y: 53, g: 'File Delete (rule: FileDelete)' }, + { x: 1599628500000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599631200000, y: 49, g: 'File Delete (rule: FileDelete)' }, + { x: 1599633900000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599636600000, y: 48, g: 'File Delete (rule: FileDelete)' }, + { x: 1599639300000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599642000000, y: 46, g: 'File Delete (rule: FileDelete)' }, + { x: 1599644700000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599647400000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599650100000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599652800000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599655500000, y: 331, g: 'File Delete (rule: FileDelete)' }, + { x: 1599658200000, y: 45, g: 'File Delete (rule: FileDelete)' }, + { x: 1599660900000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599663600000, y: 47, g: 'File Delete (rule: FileDelete)' }, + { x: 1599666300000, y: 28, g: 'File Delete (rule: FileDelete)' }, + { x: 1599579900000, y: 3, g: 'session_id_change' }, + { x: 1599582600000, y: 9, g: 'session_id_change' }, + { x: 1599585300000, y: 7, g: 'session_id_change' }, + { x: 1599588000000, y: 10, g: 'session_id_change' }, + { x: 1599590700000, y: 7, g: 'session_id_change' }, + { x: 1599593400000, y: 10, g: 'session_id_change' }, + { x: 1599596100000, y: 63, g: 'session_id_change' }, + { x: 1599598800000, y: 7, g: 'session_id_change' }, + { x: 1599601500000, y: 45, g: 'session_id_change' }, + { x: 1599604200000, y: 4, g: 'session_id_change' }, + { x: 1599606900000, y: 5, g: 'session_id_change' }, + { x: 1599609600000, y: 6, g: 'session_id_change' }, + { x: 1599612300000, y: 6, g: 'session_id_change' }, + { x: 1599615000000, y: 55, g: 'session_id_change' }, + { x: 1599617700000, y: 43, g: 'session_id_change' }, + { x: 1599620400000, y: 8, g: 'session_id_change' }, + { x: 1599623100000, y: 9, g: 'session_id_change' }, + { x: 1599625800000, y: 7, g: 'session_id_change' }, + { x: 1599628500000, y: 21, g: 'session_id_change' }, + { x: 1599631200000, y: 26, g: 'session_id_change' }, + { x: 1599633900000, y: 17, g: 'session_id_change' }, + { x: 1599636600000, y: 34, g: 'session_id_change' }, + { x: 1599639300000, y: 41, g: 'session_id_change' }, + { x: 1599642000000, y: 18, g: 'session_id_change' }, + { x: 1599644700000, y: 4, g: 'session_id_change' }, + { x: 1599647400000, y: 11, g: 'session_id_change' }, + { x: 1599650100000, y: 9, g: 'session_id_change' }, + { x: 1599652800000, y: 7, g: 'session_id_change' }, + { x: 1599655500000, y: 12, g: 'session_id_change' }, + { x: 1599658200000, y: 16, g: 'session_id_change' }, + { x: 1599660900000, y: 7, g: 'session_id_change' }, + { x: 1599663600000, y: 99, g: 'session_id_change' }, + { x: 1599666300000, y: 21, g: 'session_id_change' }, + ], +}; + +export const mockDnsSearchStrategyResponse: IEsSearchResponse = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 150, + timed_out: false, + _shards: { total: 21, successful: 21, skipped: 0, failed: 0 }, + hits: { total: 0, max_score: 0, hits: [] }, + aggregations: { + NetworkDns: { + buckets: [ + { + key_as_string: '2020-09-08T15:00:00.000Z', + key: 1599577200000, + doc_count: 7083, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T15:45:00.000Z', + key: 1599579900000, + doc_count: 146148, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T16:30:00.000Z', + key: 1599582600000, + doc_count: 65025, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T17:15:00.000Z', + key: 1599585300000, + doc_count: 62317, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T18:00:00.000Z', + key: 1599588000000, + doc_count: 58223, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T18:45:00.000Z', + key: 1599590700000, + doc_count: 55712, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T19:30:00.000Z', + key: 1599593400000, + doc_count: 55328, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T20:15:00.000Z', + key: 1599596100000, + doc_count: 63878, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T21:00:00.000Z', + key: 1599598800000, + doc_count: 54151, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T21:45:00.000Z', + key: 1599601500000, + doc_count: 55170, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T22:30:00.000Z', + key: 1599604200000, + doc_count: 43115, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-08T23:15:00.000Z', + key: 1599606900000, + doc_count: 52204, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T00:00:00.000Z', + key: 1599609600000, + doc_count: 43609, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T00:45:00.000Z', + key: 1599612300000, + doc_count: 44825, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T01:30:00.000Z', + key: 1599615000000, + doc_count: 52374, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T02:15:00.000Z', + key: 1599617700000, + doc_count: 44667, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T03:00:00.000Z', + key: 1599620400000, + doc_count: 45231, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T03:45:00.000Z', + key: 1599623100000, + doc_count: 42871, + dns: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'google.com', doc_count: 1, orderAgg: { value: 1 } }, + { key: 'google.internal', doc_count: 1, orderAgg: { value: 1 } }, + ], + }, + }, + { + key_as_string: '2020-09-09T04:30:00.000Z', + key: 1599625800000, + doc_count: 41327, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T05:15:00.000Z', + key: 1599628500000, + doc_count: 39860, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T06:00:00.000Z', + key: 1599631200000, + doc_count: 44061, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T06:45:00.000Z', + key: 1599633900000, + doc_count: 39193, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T07:30:00.000Z', + key: 1599636600000, + doc_count: 40909, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T08:15:00.000Z', + key: 1599639300000, + doc_count: 43293, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T09:00:00.000Z', + key: 1599642000000, + doc_count: 47640, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T09:45:00.000Z', + key: 1599644700000, + doc_count: 48605, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T10:30:00.000Z', + key: 1599647400000, + doc_count: 42072, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T11:15:00.000Z', + key: 1599650100000, + doc_count: 46398, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T12:00:00.000Z', + key: 1599652800000, + doc_count: 49378, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T12:45:00.000Z', + key: 1599655500000, + doc_count: 51171, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T13:30:00.000Z', + key: 1599658200000, + doc_count: 57911, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T14:15:00.000Z', + key: 1599660900000, + doc_count: 58909, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + { + key_as_string: '2020-09-09T15:00:00.000Z', + key: 1599663600000, + doc_count: 62358, + dns: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + ], + }, + }, + }, + total: 21, + loaded: 21, +}; + +export const formattedDnsSearchStrategyResponse: MatrixHistogramStrategyResponse = { + ...mockDnsSearchStrategyResponse, + inspect: { + dsl: [ + '{\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "allowNoIndices": true,\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "NetworkDns": {\n "date_histogram": {\n "field": "@timestamp",\n "fixed_interval": "2700000ms"\n },\n "aggs": {\n "dns": {\n "terms": {\n "field": "dns.question.registered_domain",\n "order": {\n "orderAgg": "desc"\n },\n "size": 10\n },\n "aggs": {\n "orderAgg": {\n "cardinality": {\n "field": "dns.question.name"\n }\n }\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n "{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"match_all\\":{}}],\\"should\\":[],\\"must_not\\":[]}}",\n {\n "range": {\n "@timestamp": {\n "gte": "2020-09-08T15:41:15.528Z",\n "lte": "2020-09-09T15:41:15.529Z",\n "format": "strict_date_optional_time"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": true\n }\n}', + ], + }, + matrixHistogramData: [ + { x: 1599623100000, y: 1, g: 'google.com' }, + { x: 1599623100000, y: 1, g: 'google.internal' }, + ], + totalCount: 0, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts new file mode 100644 index 000000000000..8b2e666ad010 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MatrixHistogramType } from '../../../../../../../common/search_strategy'; + +export const mockOptions = { + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', + histogramType: MatrixHistogramType.alerts, + timerange: { interval: '12h', from: '2020-09-08T14:23:04.482Z', to: '2020-09-09T14:23:04.482Z' }, + stackByField: 'event.module', +}; + +export const expectedDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggregations: { + alertsGroup: { + terms: { + field: 'event.module', + missing: 'All others', + order: { _count: 'desc' }, + size: 10, + }, + aggs: { + alerts: { + date_histogram: { + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + extended_bounds: { min: 1599574984482, max: 1599661384482 }, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', + { + bool: { + filter: [ + { + bool: { should: [{ match: { 'event.kind': 'alert' } }], minimum_should_match: 1 }, + }, + ], + }, + }, + { + range: { + '@timestamp': { + gte: '2020-09-08T14:23:04.482Z', + lte: '2020-09-09T14:23:04.482Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: true, + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts new file mode 100644 index 000000000000..3b1e57ea50a8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { alertsMatrixHistogramConfig } from '.'; +import { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl'; + +jest.mock('./query.alerts_histogram.dsl', () => ({ + buildAlertsHistogramQuery: jest.fn(), +})); + +describe('alertsMatrixHistogramConfig', () => { + test('should export alertsMatrixHistogramConfig corrrectly', () => { + expect(alertsMatrixHistogramConfig).toEqual({ + aggName: 'aggregations.alertsGroup.buckets', + parseKey: 'alerts.buckets', + buildDsl: buildAlertsHistogramQuery, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.ts new file mode 100644 index 000000000000..89a28b10dd68 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.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 { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl'; +import { mockOptions, expectedDsl } from './__mocks__/'; + +describe('buildAlertsHistogramQuery', () => { + test('build query from options correctly', () => { + expect(buildAlertsHistogramQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts new file mode 100644 index 000000000000..6ca3c785e2e7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.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 { MatrixHistogramType } from '../../../../../../../common/search_strategy'; + +export const mockOptions = { + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"should":[],"minimum_should_match":1}},{"match_phrase":{"result_type":"record"}},null,{"range":{"record_score":{"gte":50}}}],"should":[{"exists":{"field":"source.ip"}},{"exists":{"field":"destination.ip"}}],"must_not":[],"minimum_should_match":1}}', + histogramType: MatrixHistogramType.anomalies, + timerange: { interval: '12h', from: '2020-09-08T15:14:35.566Z', to: '2020-09-09T15:14:35.566Z' }, + stackByField: 'job_id', +}; + +export const expectedDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggs: { + anomalyActionGroup: { + terms: { field: 'job_id', order: { _count: 'desc' }, size: 10 }, + aggs: { + anomalies: { + date_histogram: { + field: 'timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + extended_bounds: { min: 1599578075566, max: 1599664475566 }, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"should":[],"minimum_should_match":1}},{"match_phrase":{"result_type":"record"}},null,{"range":{"record_score":{"gte":50}}}],"should":[{"exists":{"field":"source.ip"}},{"exists":{"field":"destination.ip"}}],"must_not":[],"minimum_should_match":1}}', + { + range: { + timestamp: { + gte: '2020-09-08T15:14:35.566Z', + lte: '2020-09-09T15:14:35.566Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: true, + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts new file mode 100644 index 000000000000..d1466a057553 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { anomaliesMatrixHistogramConfig } from '.'; +import { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl'; + +jest.mock('./query.anomalies_histogram.dsl', () => ({ + buildAnomaliesHistogramQuery: jest.fn(), +})); + +describe('anomaliesMatrixHistogramConfig', () => { + test('should export anomaliesMatrixHistogramConfig corrrectly', () => { + expect(anomaliesMatrixHistogramConfig).toEqual({ + aggName: 'aggregations.anomalyActionGroup.buckets', + parseKey: 'anomalies.buckets', + buildDsl: buildAnomaliesHistogramQuery, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.ts new file mode 100644 index 000000000000..7c10005853b2 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.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 { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl'; +import { mockOptions, expectedDsl } from './__mocks__'; + +describe('buildAnomaliesHistogramQuery', () => { + test('build query from options correctly', () => { + expect(buildAnomaliesHistogramQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts new file mode 100644 index 000000000000..1fd420dbb94c --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MatrixHistogramType } from '../../../../../../../common/search_strategy'; + +export const mockOptions = { + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + histogramType: MatrixHistogramType.authentications, + timerange: { interval: '12h', from: '2020-09-08T15:22:00.325Z', to: '2020-09-09T15:22:00.325Z' }, + stackByField: 'event.outcome', +}; + +export const expectedDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggregations: { + eventActionGroup: { + terms: { + field: 'event.outcome', + include: ['success', 'failure'], + order: { _count: 'desc' }, + size: 2, + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + extended_bounds: { min: 1599578520325, max: 1599664920325 }, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + { bool: { must: [{ term: { 'event.category': 'authentication' } }] } }, + { + range: { + '@timestamp': { + gte: '2020-09-08T15:22:00.325Z', + lte: '2020-09-09T15:22:00.325Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: true, + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts new file mode 100644 index 000000000000..54f1459b2493 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { authenticationsMatrixHistogramConfig } from '.'; +import { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl'; + +jest.mock('./query.authentications_histogram.dsl', () => ({ + buildAuthenticationsHistogramQuery: jest.fn(), +})); + +describe('authenticationsMatrixHistogramConfig', () => { + test('should export authenticationsMatrixHistogramConfig corrrectly', () => { + expect(authenticationsMatrixHistogramConfig).toEqual({ + aggName: 'aggregations.eventActionGroup.buckets', + parseKey: 'events.buckets', + buildDsl: buildAuthenticationsHistogramQuery, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.ts new file mode 100644 index 000000000000..67d681d8ba87 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.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 { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl'; +import { mockOptions, expectedDsl } from './__mocks__/'; + +describe('buildAuthenticationsHistogramQuery', () => { + test('build query from options correctly', () => { + expect(buildAuthenticationsHistogramQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts new file mode 100644 index 000000000000..94ba20327a40 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.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 { MatrixHistogramType } from '../../../../../../../common/search_strategy'; + +export const mockOptions = { + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + histogramType: MatrixHistogramType.dns, + timerange: { interval: '12h', from: '2020-09-08T15:41:15.528Z', to: '2020-09-09T15:41:15.529Z' }, + stackByField: 'dns.question.registered_domain', +}; + +export const expectedDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggregations: { + NetworkDns: { + date_histogram: { field: '@timestamp', fixed_interval: '2700000ms' }, + aggs: { + dns: { + terms: { + field: 'dns.question.registered_domain', + order: { orderAgg: 'desc' }, + size: 10, + }, + aggs: { orderAgg: { cardinality: { field: 'dns.question.name' } } }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + { + range: { + '@timestamp': { + gte: '2020-09-08T15:41:15.528Z', + lte: '2020-09-09T15:41:15.529Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: true, + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts new file mode 100644 index 000000000000..8afc764d97f8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { dnsMatrixHistogramConfig } from '.'; +import { buildDnsHistogramQuery } from './query.dns_histogram.dsl'; +import { getDnsParsedData } from './helpers'; + +jest.mock('./query.dns_histogram.dsl', () => ({ + buildDnsHistogramQuery: jest.fn(), +})); + +jest.mock('./helpers', () => ({ + getDnsParsedData: jest.fn(), +})); + +describe('dnsMatrixHistogramConfig', () => { + test('should export dnsMatrixHistogramConfig corrrectly', () => { + expect(dnsMatrixHistogramConfig).toEqual({ + aggName: 'aggregations.NetworkDns.buckets', + parseKey: 'dns.buckets', + buildDsl: buildDnsHistogramQuery, + parser: getDnsParsedData, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.ts new file mode 100644 index 000000000000..a3d562a28d07 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.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 { buildDnsHistogramQuery } from './query.dns_histogram.dsl'; +import { mockOptions, expectedDsl } from './__mocks__/'; + +describe('buildDnsHistogramQuery', () => { + test('build query from options correctly', () => { + expect(buildDnsHistogramQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts new file mode 100644 index 000000000000..09b710ab33c7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + MatrixHistogramQuery, + MatrixHistogramRequestOptions, + MatrixHistogramType, +} from '../../../../../../../common/search_strategy'; + +export const mockOptions: MatrixHistogramRequestOptions = { + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: MatrixHistogramQuery, + filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + histogramType: MatrixHistogramType.events, + timerange: { interval: '12h', from: '2020-09-08T16:11:26.215Z', to: '2020-09-09T16:11:26.215Z' }, + stackByField: 'event.action', +}; + +export const expectedDsl = { + index: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggregations: { + eventActionGroup: { + terms: { + field: 'event.action', + missing: 'All others', + order: { _count: 'desc' }, + size: 10, + }, + aggs: { + events: { + date_histogram: { + field: '@timestamp', + fixed_interval: '2700000ms', + min_doc_count: 0, + extended_bounds: { min: 1599581486215, max: 1599667886215 }, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + { + range: { + '@timestamp': { + gte: '2020-09-08T16:11:26.215Z', + lte: '2020-09-09T16:11:26.215Z', + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + size: 0, + track_total_hits: true, + }, +}; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts new file mode 100644 index 000000000000..f67307eac67e --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { eventsMatrixHistogramConfig } from '.'; +import { buildEventsHistogramQuery } from './query.events_histogram.dsl'; + +jest.mock('./query.events_histogram.dsl.ts', () => ({ + buildEventsHistogramQuery: jest.fn(), +})); + +describe('eventsMatrixHistogramConfig', () => { + test('should export eventsMatrixHistogramConfig corrrectly', () => { + expect(eventsMatrixHistogramConfig).toEqual({ + aggName: 'aggregations.eventActionGroup.buckets', + parseKey: 'events.buckets', + buildDsl: buildEventsHistogramQuery, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts new file mode 100644 index 000000000000..72cb9de9f0e7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.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 { buildEventsHistogramQuery } from './query.events_histogram.dsl'; +import { mockOptions, expectedDsl } from './__mocks__/'; + +describe('buildEventsHistogramQuery', () => { + test('build query from options correctly', () => { + expect(buildEventsHistogramQuery(mockOptions)).toEqual(expectedDsl); + }); +}); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts new file mode 100644 index 000000000000..3fd7240eba93 --- /dev/null +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + MatrixHistogramRequestOptions, + MatrixHistogramType, +} from '../../../../../common/search_strategy/security_solution'; +import { matrixHistogram } from '.'; +import { + formattedAlertsSearchStrategyResponse, + formattedAnomaliesSearchStrategyResponse, + formattedAuthenticationsSearchStrategyResponse, + formattedEventsSearchStrategyResponse, + formattedDnsSearchStrategyResponse, + mockAlertsSearchStrategyResponse, + mockAnomaliesSearchStrategyResponse, + mockAuthenticationsSearchStrategyResponse, + mockEventsSearchStrategyResponse, + mockDnsSearchStrategyResponse, +} from './__mocks__'; +import { alertsMatrixHistogramConfig } from './alerts'; +import { anomaliesMatrixHistogramConfig } from './anomalies'; +import { authenticationsMatrixHistogramConfig } from './authentications'; +import { eventsMatrixHistogramConfig } from './events'; +import { dnsMatrixHistogramConfig } from './dns'; + +import { mockOptions as mockAlertsOptions } from './alerts/__mocks__'; +import { mockOptions as mockAnomaliesOptions } from './anomalies/__mocks__'; +import { mockOptions as mockAuthenticationsOptions } from './authentications/__mocks__'; +import { mockOptions as mockEventsOptions } from './events/__mocks__'; +import { mockOptions as mockDnsOptions } from './dns/__mocks__'; + +describe('Alerts matrixHistogram search strategy', () => { + const buildMatrixHistogramQuery = jest.spyOn(alertsMatrixHistogramConfig, 'buildDsl'); + + afterEach(() => { + buildMatrixHistogramQuery.mockClear(); + }); + + describe('buildDsl', () => { + test('should build dsl query', () => { + matrixHistogram.buildDsl(mockAlertsOptions); + expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAlertsOptions); + }); + + test('should throw error if histogramType is invalid', () => { + const invalidOptions: MatrixHistogramRequestOptions = { + ...mockAlertsOptions, + histogramType: 'xxx' as MatrixHistogramType, + } as MatrixHistogramRequestOptions; + + expect(() => { + matrixHistogram.buildDsl(invalidOptions); + }).toThrowError(`This histogram type xxx is unknown to the server side`); + }); + }); + + describe('parse', () => { + test('should parse data correctly', async () => { + const result = await matrixHistogram.parse( + mockAlertsOptions, + mockAlertsSearchStrategyResponse + ); + expect(result).toMatchObject(formattedAlertsSearchStrategyResponse); + }); + }); +}); + +describe('Anomalies matrixHistogram search strategy', () => { + const buildMatrixHistogramQuery = jest.spyOn(anomaliesMatrixHistogramConfig, 'buildDsl'); + + afterEach(() => { + buildMatrixHistogramQuery.mockClear(); + }); + + describe('buildDsl', () => { + test('should build dsl query', () => { + matrixHistogram.buildDsl(mockAnomaliesOptions); + expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAnomaliesOptions); + }); + + test('should throw error if histogramType is invalid', () => { + const invalidOptions: MatrixHistogramRequestOptions = { + ...mockAnomaliesOptions, + histogramType: 'xxx' as MatrixHistogramType, + } as MatrixHistogramRequestOptions; + + expect(() => { + matrixHistogram.buildDsl(invalidOptions); + }).toThrowError(`This histogram type xxx is unknown to the server side`); + }); + }); + + describe('parse', () => { + test('should parse data correctly', async () => { + const result = await matrixHistogram.parse( + mockAnomaliesOptions, + mockAnomaliesSearchStrategyResponse + ); + expect(result).toMatchObject(formattedAnomaliesSearchStrategyResponse); + }); + }); +}); + +describe('Authentications matrixHistogram search strategy', () => { + const buildMatrixHistogramQuery = jest.spyOn(authenticationsMatrixHistogramConfig, 'buildDsl'); + + afterEach(() => { + buildMatrixHistogramQuery.mockClear(); + }); + + describe('buildDsl', () => { + test('should build dsl query', () => { + matrixHistogram.buildDsl(mockAuthenticationsOptions); + expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAuthenticationsOptions); + }); + + test('should throw error if histogramType is invalid', () => { + const invalidOptions = { + ...mockAuthenticationsOptions, + histogramType: 'xxx' as MatrixHistogramType, + } as MatrixHistogramRequestOptions; + + expect(() => { + matrixHistogram.buildDsl(invalidOptions); + }).toThrowError(`This histogram type xxx is unknown to the server side`); + }); + }); + + describe('parse', () => { + test('should parse data correctly', async () => { + const result = await matrixHistogram.parse( + mockAuthenticationsOptions, + mockAuthenticationsSearchStrategyResponse + ); + expect(result).toMatchObject(formattedAuthenticationsSearchStrategyResponse); + }); + }); +}); + +describe('Events matrixHistogram search strategy', () => { + const buildMatrixHistogramQuery = jest.spyOn(eventsMatrixHistogramConfig, 'buildDsl'); + + afterEach(() => { + buildMatrixHistogramQuery.mockClear(); + }); + + describe('buildDsl', () => { + test('should build dsl query', () => { + matrixHistogram.buildDsl(mockEventsOptions); + expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockEventsOptions); + }); + + test('should throw error if histogramType is invalid', () => { + const invalidOptions = { + ...mockEventsOptions, + histogramType: 'xxx' as MatrixHistogramType, + } as MatrixHistogramRequestOptions; + + expect(() => { + matrixHistogram.buildDsl(invalidOptions); + }).toThrowError(`This histogram type xxx is unknown to the server side`); + }); + }); + + describe('parse', () => { + test('should parse data correctly', async () => { + const result = await matrixHistogram.parse( + mockEventsOptions, + mockEventsSearchStrategyResponse + ); + expect(result).toMatchObject(formattedEventsSearchStrategyResponse); + }); + }); +}); + +describe('Dns matrixHistogram search strategy', () => { + const buildMatrixHistogramQuery = jest.spyOn(dnsMatrixHistogramConfig, 'buildDsl'); + + afterEach(() => { + buildMatrixHistogramQuery.mockClear(); + }); + + describe('buildDsl', () => { + test('should build dsl query', () => { + matrixHistogram.buildDsl(mockDnsOptions); + expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockDnsOptions); + }); + + test('should throw error if histogramType is invalid', () => { + const invalidOptions = { + ...mockDnsOptions, + histogramType: 'xxx' as MatrixHistogramType, + } as MatrixHistogramRequestOptions; + + expect(() => { + matrixHistogram.buildDsl(invalidOptions); + }).toThrowError(`This histogram type xxx is unknown to the server side`); + }); + }); + + describe('parse', () => { + test('should parse data correctly', async () => { + const result = await matrixHistogram.parse(mockDnsOptions, mockDnsSearchStrategyResponse); + expect(result).toMatchObject(formattedDnsSearchStrategyResponse); + }); + }); +}); diff --git a/x-pack/plugins/snapshot_restore/kibana.json b/x-pack/plugins/snapshot_restore/kibana.json index e0a29581ea07..a8a3881929f4 100644 --- a/x-pack/plugins/snapshot_restore/kibana.json +++ b/x-pack/plugins/snapshot_restore/kibana.json @@ -5,7 +5,8 @@ "ui": true, "requiredPlugins": [ "licensing", - "management" + "management", + "features" ], "optionalPlugins": [ "usageCollection", diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/navigation.tsx b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/navigation.tsx index 64f5a8fa0871..d1e3c21399d5 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/navigation.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/navigation.tsx @@ -11,12 +11,14 @@ interface Props { currentStep: number; maxCompletedStep: number; updateCurrentStep: (step: number) => void; + isFormValid: boolean; } export const PolicyNavigation: React.FunctionComponent = ({ currentStep, maxCompletedStep, updateCurrentStep, + isFormValid, }) => { const { i18n } = useServices(); @@ -27,6 +29,7 @@ export const PolicyNavigation: React.FunctionComponent = ({ }), isComplete: maxCompletedStep >= 1, isSelected: currentStep === 1, + disabled: !isFormValid && currentStep !== 1, onClick: () => updateCurrentStep(1), }, { @@ -35,7 +38,7 @@ export const PolicyNavigation: React.FunctionComponent = ({ }), isComplete: maxCompletedStep >= 2, isSelected: currentStep === 2, - disabled: maxCompletedStep < 1, + disabled: maxCompletedStep < 1 || (!isFormValid && currentStep !== 2), onClick: () => updateCurrentStep(2), }, { @@ -44,7 +47,7 @@ export const PolicyNavigation: React.FunctionComponent = ({ }), isComplete: maxCompletedStep >= 3, isSelected: currentStep === 3, - disabled: maxCompletedStep < 2, + disabled: maxCompletedStep < 2 || (!isFormValid && currentStep !== 3), onClick: () => updateCurrentStep(3), }, { @@ -53,7 +56,7 @@ export const PolicyNavigation: React.FunctionComponent = ({ }), isComplete: maxCompletedStep >= 3, isSelected: currentStep === 4, - disabled: maxCompletedStep < 3, + disabled: maxCompletedStep < 3 || (!isFormValid && currentStep !== 4), onClick: () => updateCurrentStep(4), }, ]; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/policy_form.tsx b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/policy_form.tsx index 3e1fb9b6500b..c6b841c9ce7f 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/policy_form.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/policy_form.tsx @@ -130,6 +130,7 @@ export const PolicyForm: React.FunctionComponent = ({ currentStep={currentStep} maxCompletedStep={maxCompletedStep} updateCurrentStep={updateCurrentStep} + isFormValid={validation.isValid} /> diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx index f825c7b1f3d9..7d3ba92cf2ad 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx @@ -51,7 +51,7 @@ export const PolicyStepLogistics: React.FunctionComponent = ({ name: undefined, }, }, - sendRequest: reloadRepositories, + resendRequest: reloadRepositories, } = useLoadRepositories(); const { i18n, history } = useServices(); diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx index f67e8eb58623..b4612c9df42f 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx @@ -65,7 +65,7 @@ export const PolicyDetails: React.FunctionComponent = ({ onPolicyExecuted, }) => { const { i18n, uiMetricService, history } = useServices(); - const { error, data: policyDetails, sendRequest: reload } = useLoadPolicy(policyName); + const { error, data: policyDetails, resendRequest: reload } = useLoadPolicy(policyName); const [activeTab, setActiveTab] = useState(TAB_SUMMARY); const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx index 655bd0e9d8bb..57f18ccbf815 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx @@ -45,7 +45,7 @@ export const PolicyList: React.FunctionComponent { diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx index d55bbf0b324c..e7e4a9b54ada 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { SlmPolicy } from '../../../../../../common/types'; -import { Error } from '../../../../../shared_imports'; +import { UseRequestResponse } from '../../../../../shared_imports'; import { UIM_POLICY_SHOW_DETAILS_CLICK } from '../../../../constants'; import { useServices } from '../../../../app_context'; import { @@ -30,13 +30,12 @@ import { PolicyDeleteProvider, } from '../../../../components'; import { linkToAddPolicy, linkToEditPolicy } from '../../../../services/navigation'; -import { SendRequestResponse } from '../../../../../shared_imports'; import { reactRouterNavigate } from '../../../../../../../../../src/plugins/kibana_react/public'; interface Props { policies: SlmPolicy[]; - reload: () => Promise>; + reload: UseRequestResponse['resendRequest']; openPolicyDetailsUrl: (name: SlmPolicy['name']) => string; onPolicyDeleted: (policiesDeleted: Array) => void; onPolicyExecuted: () => void; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx index 9afdad3806de..a3f57ce4fbf5 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx @@ -40,7 +40,7 @@ export const RepositoryList: React.FunctionComponent Promise>; + reload: UseRequestResponse['resendRequest']; openRepositoryDetailsUrl: (name: Repository['name']) => string; onRepositoryDeleted: (repositoriesDeleted: Array) => void; } diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx index d7a82386926c..d9507a101bba 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx @@ -52,9 +52,13 @@ export const RestoreList: React.FunctionComponent = () => { const [currentInterval, setCurrentInterval] = useState(INTERVAL_OPTIONS[1]); // Load restores - const { error, isLoading, data: restores = [], isInitialRequest, sendRequest } = useLoadRestores( - currentInterval - ); + const { + error, + isLoading, + data: restores = [], + isInitialRequest, + resendRequest, + } = useLoadRestores(currentInterval); const { uiMetricService, history } = useServices(); @@ -174,7 +178,7 @@ export const RestoreList: React.FunctionComponent = () => { key={interval} icon="empty" onClick={() => { - sendRequest(); + resendRequest(); setCurrentInterval(interval); setIsIntervalMenuOpen(false); }} diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx index d13188fc4473..97def33ffe8f 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx @@ -44,7 +44,7 @@ export const SnapshotList: React.FunctionComponent Promise>; + reload: UseRequestResponse['resendRequest']; openSnapshotDetailsUrl: (repositoryName: string, snapshotId: string) => string; repositoryFilter?: string; policyFilter?: string; diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index cad8ce147bd2..bd1c0e0cd395 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -14,6 +14,7 @@ export { sendRequest, SendRequestConfig, SendRequestResponse, + UseRequestResponse, useAuthorizationContext, useRequest, UseRequestConfig, diff --git a/x-pack/plugins/snapshot_restore/server/plugin.ts b/x-pack/plugins/snapshot_restore/server/plugin.ts index cf7504363b82..411c0cb9a5eb 100644 --- a/x-pack/plugins/snapshot_restore/server/plugin.ts +++ b/x-pack/plugins/snapshot_restore/server/plugin.ts @@ -20,7 +20,7 @@ import { ILegacyScopedClusterClient, } from 'kibana/server'; -import { PLUGIN } from '../common'; +import { PLUGIN, APP_REQUIRED_CLUSTER_PRIVILEGES } from '../common'; import { License } from './services'; import { ApiRoutes } from './routes'; import { wrapEsError } from './lib'; @@ -54,7 +54,7 @@ export class SnapshotRestoreServerPlugin implements Plugin public async setup( { http, getStartServices }: CoreSetup, - { licensing, security, cloud }: Dependencies + { licensing, features, security, cloud }: Dependencies ): Promise { const pluginConfig = await this.context.config .create() @@ -81,6 +81,19 @@ export class SnapshotRestoreServerPlugin implements Plugin } ); + features.registerElasticsearchFeature({ + id: PLUGIN.id, + management: { + data: [PLUGIN.id], + }, + privileges: [ + { + requiredClusterPrivileges: [...APP_REQUIRED_CLUSTER_PRIVILEGES], + ui: [], + }, + ], + }); + http.registerRouteHandlerContext('snapshotRestore', async (ctx, request) => { this.snapshotRestoreESClient = this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices)); diff --git a/x-pack/plugins/snapshot_restore/server/types.ts b/x-pack/plugins/snapshot_restore/server/types.ts index 8cfcaec1a2cd..eb51f086deac 100644 --- a/x-pack/plugins/snapshot_restore/server/types.ts +++ b/x-pack/plugins/snapshot_restore/server/types.ts @@ -7,12 +7,14 @@ import { LegacyScopedClusterClient, IRouter } from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; import { CloudSetup } from '../../cloud/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { License } from './services'; import { wrapEsError } from './lib'; import { isEsError } from './shared_imports'; export interface Dependencies { licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; security?: SecurityPluginSetup; cloud?: CloudSetup; } diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx index ad5ebe157cfb..0eed6793ddbe 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.test.tsx @@ -10,9 +10,9 @@ import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { Space } from '../../../../common/model/space'; import { SectionPanel } from '../section_panel'; import { EnabledFeatures } from './enabled_features'; -import { FeatureConfig } from '../../../../../features/public'; +import { KibanaFeatureConfig } from '../../../../../features/public'; -const features: FeatureConfig[] = [ +const features: KibanaFeatureConfig[] = [ { id: 'feature-1', name: 'Feature 1', diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 373e0b42aebe..689bb610d5f3 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment, ReactNode } from 'react'; import { ApplicationStart } from 'kibana/public'; -import { FeatureConfig } from '../../../../../../plugins/features/public'; +import { KibanaFeatureConfig } from '../../../../../../plugins/features/public'; import { Space } from '../../../../common/model/space'; import { getEnabledFeatures } from '../../lib/feature_utils'; import { SectionPanel } from '../section_panel'; @@ -17,7 +17,7 @@ import { FeatureTable } from './feature_table'; interface Props { space: Partial; - features: FeatureConfig[]; + features: KibanaFeatureConfig[]; securityEnabled: boolean; onChange: (space: Partial) => void; getUrlForApp: ApplicationStart['getUrlForApp']; diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx index df07d128e497..9265ca46e3a3 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.tsx @@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { ChangeEvent, Component } from 'react'; -import { FeatureConfig } from '../../../../../../plugins/features/public'; +import { KibanaFeatureConfig } from '../../../../../../plugins/features/public'; import { Space } from '../../../../common/model/space'; import { ToggleAllFeatures } from './toggle_all_features'; interface Props { space: Partial; - features: FeatureConfig[]; + features: KibanaFeatureConfig[]; onChange: (space: Partial) => void; } @@ -70,8 +70,8 @@ export class FeatureTable extends Component { defaultMessage: 'Feature', }), render: ( - feature: FeatureConfig, - _item: { feature: FeatureConfig; space: Props['space'] } + feature: KibanaFeatureConfig, + _item: { feature: KibanaFeatureConfig; space: Props['space'] } ) => { return ( diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx index b573848f0c84..f58072084887 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.test.tsx @@ -16,7 +16,7 @@ import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; import { notificationServiceMock, scopedHistoryMock } from 'src/core/public/mocks'; import { featuresPluginMock } from '../../../../features/public/mocks'; -import { Feature } from '../../../../features/public'; +import { KibanaFeature } from '../../../../features/public'; // To be resolved by EUI team. // https://github.com/elastic/eui/issues/3712 @@ -34,7 +34,7 @@ const space = { const featuresStart = featuresPluginMock.createStart(); featuresStart.getFeatures.mockResolvedValue([ - new Feature({ + new KibanaFeature({ id: 'feature-1', name: 'feature 1', icon: 'spacesApp', diff --git a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx index e725310c4181..5338710b7c8a 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/manage_space_page.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import React, { Component, Fragment } from 'react'; import { ApplicationStart, Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public'; -import { Feature, FeaturesPluginStart } from '../../../../features/public'; +import { KibanaFeature, FeaturesPluginStart } from '../../../../features/public'; import { isReservedSpace } from '../../../common'; import { Space } from '../../../common/model/space'; import { SpacesManager } from '../../spaces_manager'; @@ -46,7 +46,7 @@ interface Props { interface State { space: Partial; - features: Feature[]; + features: KibanaFeature[]; originalSpace?: Partial; showAlteringActiveSpaceDialog: boolean; isLoading: boolean; @@ -312,7 +312,7 @@ export class ManageSpacePage extends Component { } }; - private loadSpace = async (spaceId: string, featuresPromise: Promise) => { + private loadSpace = async (spaceId: string, featuresPromise: Promise) => { const { spacesManager, onLoadSpace } = this.props; try { diff --git a/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts index 20d419e5c90e..212ffe96cdbf 100644 --- a/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.test.ts @@ -5,7 +5,7 @@ */ import { getEnabledFeatures } from './feature_utils'; -import { FeatureConfig } from '../../../../features/public'; +import { KibanaFeatureConfig } from '../../../../features/public'; const buildFeatures = () => [ @@ -25,7 +25,7 @@ const buildFeatures = () => id: 'feature4', name: 'feature 4', }, - ] as FeatureConfig[]; + ] as KibanaFeatureConfig[]; const buildSpace = (disabledFeatures = [] as string[]) => ({ id: 'space', diff --git a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts index 273ea7e60bc5..c6f7031976a9 100644 --- a/x-pack/plugins/spaces/public/management/lib/feature_utils.ts +++ b/x-pack/plugins/spaces/public/management/lib/feature_utils.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FeatureConfig } from '../../../../features/common'; +import { KibanaFeatureConfig } from '../../../../features/common'; import { Space } from '../..'; -export function getEnabledFeatures(features: FeatureConfig[], space: Partial) { +export function getEnabledFeatures(features: KibanaFeatureConfig[], space: Partial) { return features.filter((feature) => !(space.disabledFeatures || []).includes(feature.id)); } diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx index 36efc6874978..b40f34273d99 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_page.tsx @@ -21,7 +21,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ApplicationStart, Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public'; -import { Feature, FeaturesPluginStart } from '../../../../features/public'; +import { KibanaFeature, FeaturesPluginStart } from '../../../../features/public'; import { isReservedSpace } from '../../../common'; import { DEFAULT_SPACE_ID } from '../../../common/constants'; import { Space } from '../../../common/model/space'; @@ -46,7 +46,7 @@ interface Props { interface State { spaces: Space[]; - features: Feature[]; + features: KibanaFeature[]; loading: boolean; showConfirmDeleteModal: boolean; selectedSpace: Space | null; diff --git a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx index 607570eedc78..fe4bdc865094 100644 --- a/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_grid/spaces_grid_pages.test.tsx @@ -13,7 +13,7 @@ import { SpacesGridPage } from './spaces_grid_page'; import { httpServiceMock, scopedHistoryMock } from 'src/core/public/mocks'; import { notificationServiceMock } from 'src/core/public/mocks'; import { featuresPluginMock } from '../../../../features/public/mocks'; -import { Feature } from '../../../../features/public'; +import { KibanaFeature } from '../../../../features/public'; const spaces = [ { @@ -42,7 +42,7 @@ spacesManager.getSpaces = jest.fn().mockResolvedValue(spaces); const featuresStart = featuresPluginMock.createStart(); featuresStart.getFeatures.mockResolvedValue([ - new Feature({ + new KibanaFeature({ id: 'feature-1', name: 'feature 1', icon: 'spacesApp', diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_provider.test.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_provider.test.ts index 8678bdceb70f..b0b89afa79d5 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_provider.test.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_provider.test.ts @@ -10,6 +10,9 @@ describe('Capabilities provider', () => { it('provides the expected capabilities', () => { expect(capabilitiesProvider()).toMatchInlineSnapshot(` Object { + "catalogue": Object { + "spaces": true, + }, "management": Object { "kibana": Object { "spaces": true, diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_provider.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_provider.ts index 5976aabfa66e..1aaf2ad1df92 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_provider.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_provider.ts @@ -8,6 +8,9 @@ export const capabilitiesProvider = () => ({ spaces: { manage: true, }, + catalogue: { + spaces: true, + }, management: { kibana: { spaces: true, diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts index c9ea1b44e723..bf0b51b7e250 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Feature } from '../../../../plugins/features/server'; +import { KibanaFeature } from '../../../../plugins/features/server'; import { Space } from '../../common/model/space'; import { setupCapabilitiesSwitcher } from './capabilities_switcher'; import { Capabilities, CoreSetup } from 'src/core/server'; @@ -80,7 +80,7 @@ const features = ([ }, }, }, -] as unknown) as Feature[]; +] as unknown) as KibanaFeature[]; const buildCapabilities = () => Object.freeze({ @@ -121,7 +121,7 @@ const setup = (space: Space) => { const coreSetup = coreMock.createSetup(); const featuresStart = featuresPluginMock.createStart(); - featuresStart.getFeatures.mockReturnValue(features); + featuresStart.getKibanaFeatures.mockReturnValue(features); coreSetup.getStartServices.mockResolvedValue([ coreMock.createStart(), diff --git a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts index e8d964b22010..8b0b955c40d9 100644 --- a/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts +++ b/x-pack/plugins/spaces/server/capabilities/capabilities_switcher.ts @@ -5,7 +5,7 @@ */ import _ from 'lodash'; import { Capabilities, CapabilitiesSwitcher, CoreSetup, Logger } from 'src/core/server'; -import { Feature } from '../../../../plugins/features/server'; +import { KibanaFeature } from '../../../../plugins/features/server'; import { Space } from '../../common/model/space'; import { SpacesServiceSetup } from '../spaces_service'; import { PluginsStart } from '../plugin'; @@ -28,7 +28,7 @@ export function setupCapabilitiesSwitcher( core.getStartServices(), ]); - const registeredFeatures = features.getFeatures(); + const registeredFeatures = features.getKibanaFeatures(); // try to retrieve capabilities for authenticated or "maybe authenticated" users return toggleCapabilities(registeredFeatures, capabilities, activeSpace); @@ -39,7 +39,11 @@ export function setupCapabilitiesSwitcher( }; } -function toggleCapabilities(features: Feature[], capabilities: Capabilities, activeSpace: Space) { +function toggleCapabilities( + features: KibanaFeature[], + capabilities: Capabilities, + activeSpace: Space +) { const clonedCapabilities = _.cloneDeep(capabilities); toggleDisabledFeatures(features, clonedCapabilities, activeSpace); @@ -48,7 +52,7 @@ function toggleCapabilities(features: Feature[], capabilities: Capabilities, act } function toggleDisabledFeatures( - features: Feature[], + features: KibanaFeature[], capabilities: Capabilities, activeSpace: Space ) { @@ -61,7 +65,7 @@ function toggleDisabledFeatures( } return [[...acc[0], feature], acc[1]]; }, - [[], []] as [Feature[], Feature[]] + [[], []] as [KibanaFeature[], KibanaFeature[]] ); const navLinks = capabilities.navLinks; diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts index d49dfa2015dc..1cec7b769fa2 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts @@ -20,6 +20,7 @@ import { copySavedObjectsToSpacesFactory } from './copy_to_spaces'; jest.mock('../../../../../../src/core/server', () => { return { + ...(jest.requireActual('../../../../../../src/core/server') as Record), exportSavedObjectsToStream: jest.fn(), importSavedObjectsFromStream: jest.fn(), }; diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts index 6a77bf7397cb..37181c9d8164 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts @@ -20,6 +20,7 @@ import { resolveCopySavedObjectsToSpacesConflictsFactory } from './resolve_copy_ jest.mock('../../../../../../src/core/server', () => { return { + ...(jest.requireActual('../../../../../../src/core/server') as Record), exportSavedObjectsToStream: jest.fn(), resolveSavedObjectsImportErrors: jest.fn(), }; diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts index dabdcf553edb..fe1acd93570f 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.test.ts @@ -25,7 +25,7 @@ import { SpacesService } from '../../spaces_service'; import { SpacesAuditLogger } from '../audit_logger'; import { convertSavedObjectToSpace } from '../../routes/lib'; import { initSpacesOnPostAuthRequestInterceptor } from './on_post_auth_interceptor'; -import { Feature } from '../../../../features/server'; +import { KibanaFeature } from '../../../../features/server'; import { spacesConfig } from '../__fixtures__'; import { securityMock } from '../../../../security/server/mocks'; import { featuresPluginMock } from '../../../../features/server/mocks'; @@ -124,7 +124,7 @@ describe.skip('onPostAuthInterceptor', () => { const loggingMock = loggingSystemMock.create().asLoggerFactory().get('xpack', 'spaces'); const featuresPlugin = featuresPluginMock.createSetup(); - featuresPlugin.getFeatures.mockReturnValue(([ + featuresPlugin.getKibanaFeatures.mockReturnValue(([ { id: 'feature-1', name: 'feature 1', @@ -145,7 +145,7 @@ describe.skip('onPostAuthInterceptor', () => { name: 'feature 4', app: ['kibana'], }, - ] as unknown) as Feature[]); + ] as unknown) as KibanaFeature[]); const mockRepository = jest.fn().mockImplementation(() => { return { diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts index 3d6084d37a38..e4ca0f8072f9 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_post_auth_interceptor.ts @@ -108,7 +108,7 @@ export function initSpacesOnPostAuthRequestInterceptor({ if (appId !== 'kibana' && space && space.disabledFeatures.length > 0) { log.debug(`Verifying application is available: "${appId}"`); - const allFeatures = features.getFeatures(); + const allFeatures = features.getKibanaFeatures(); const isRegisteredApp = allFeatures.some((feature) => feature.app.includes(appId)); if (isRegisteredApp) { diff --git a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts index 90ce2b01bfd2..1090b029069d 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts @@ -260,10 +260,12 @@ describe('#getAll', () => { mockAuthorization.mode.useRbacForRequest.mockReturnValue(true); mockCheckPrivilegesAtSpaces.mockReturnValue({ username, - privileges: [ - { resource: savedObjects[0].id, privilege, authorized: false }, - { resource: savedObjects[1].id, privilege, authorized: false }, - ], + privileges: { + kibana: [ + { resource: savedObjects[0].id, privilege, authorized: false }, + { resource: savedObjects[1].id, privilege, authorized: false }, + ], + }, }); const maxSpaces = 1234; const mockConfig = createMockConfig({ @@ -298,7 +300,7 @@ describe('#getAll', () => { expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); expect(mockCheckPrivilegesAtSpaces).toHaveBeenCalledWith( savedObjects.map((savedObject) => savedObject.id), - [privilege] + { kibana: [privilege] } ); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledWith( username, @@ -318,10 +320,12 @@ describe('#getAll', () => { mockAuthorization.mode.useRbacForRequest.mockReturnValue(true); mockCheckPrivilegesAtSpaces.mockReturnValue({ username, - privileges: [ - { resource: savedObjects[0].id, privilege, authorized: true }, - { resource: savedObjects[1].id, privilege, authorized: false }, - ], + privileges: { + kibana: [ + { resource: savedObjects[0].id, privilege, authorized: true }, + { resource: savedObjects[1].id, privilege, authorized: false }, + ], + }, }); const mockInternalRepository = { find: jest.fn().mockReturnValue({ @@ -357,7 +361,7 @@ describe('#getAll', () => { expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); expect(mockCheckPrivilegesAtSpaces).toHaveBeenCalledWith( savedObjects.map((savedObject) => savedObject.id), - [privilege] + { kibana: [privilege] } ); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledWith( @@ -451,9 +455,9 @@ describe('#canEnumerateSpaces', () => { expect(canEnumerateSpaces).toEqual(false); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); @@ -486,9 +490,9 @@ describe('#canEnumerateSpaces', () => { expect(canEnumerateSpaces).toEqual(true); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); @@ -603,7 +607,9 @@ describe('#get', () => { await expect(client.get(id)).rejects.toThrowErrorMatchingSnapshot(); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesAtSpace).toHaveBeenCalledWith(id, mockAuthorization.actions.login); + expect(mockCheckPrivilegesAtSpace).toHaveBeenCalledWith(id, { + kibana: mockAuthorization.actions.login, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledWith(username, 'get', [ id, ]); @@ -641,7 +647,9 @@ describe('#get', () => { expect(space).toEqual(expectedSpace); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesAtSpace).toHaveBeenCalledWith(id, mockAuthorization.actions.login); + expect(mockCheckPrivilegesAtSpace).toHaveBeenCalledWith(id, { + kibana: mockAuthorization.actions.login, + }); expect(mockInternalRepository.get).toHaveBeenCalledWith('space', id); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledWith(username, 'get', [ @@ -886,9 +894,9 @@ describe('#create', () => { expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledWith(username, 'create'); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); }); @@ -939,9 +947,9 @@ describe('#create', () => { }); expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledWith(username, 'create'); }); @@ -989,9 +997,9 @@ describe('#create', () => { expect(mockInternalRepository.create).not.toHaveBeenCalled(); expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledWith(username, 'create'); }); @@ -1128,9 +1136,9 @@ describe('#update', () => { expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledWith(username, 'update'); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); }); @@ -1167,9 +1175,9 @@ describe('#update', () => { expect(actualSpace).toEqual(expectedReturnedSpace); expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockInternalRepository.update).toHaveBeenCalledWith('space', id, attributes); expect(mockInternalRepository.get).toHaveBeenCalledWith('space', id); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); @@ -1353,9 +1361,9 @@ describe('#delete', () => { expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledWith(username, 'delete'); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); }); @@ -1389,9 +1397,9 @@ describe('#delete', () => { expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockInternalRepository.get).toHaveBeenCalledWith('space', id); expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledWith(username, 'delete'); @@ -1429,9 +1437,9 @@ describe('#delete', () => { expect(mockAuthorization.mode.useRbacForRequest).toHaveBeenCalledWith(request); expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith( - mockAuthorization.actions.space.manage - ); + expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ + kibana: mockAuthorization.actions.space.manage, + }); expect(mockInternalRepository.get).toHaveBeenCalledWith('space', id); expect(mockInternalRepository.delete).toHaveBeenCalledWith('space', id); expect(mockInternalRepository.deleteByNamespace).toHaveBeenCalledWith(id); diff --git a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts index b1d6e3200ab3..acb00a87bf7d 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts @@ -50,9 +50,9 @@ export class SpacesClient { public async canEnumerateSpaces(): Promise { if (this.useRbac()) { const checkPrivileges = this.authorization!.checkPrivilegesWithRequest(this.request); - const { hasAllRequested } = await checkPrivileges.globally( - this.authorization!.actions.space.manage - ); + const { hasAllRequested } = await checkPrivileges.globally({ + kibana: this.authorization!.actions.space.manage, + }); this.debugLogger(`SpacesClient.canEnumerateSpaces, using RBAC. Result: ${hasAllRequested}`); return hasAllRequested; } @@ -87,9 +87,11 @@ export class SpacesClient { const privilege = privilegeFactory(this.authorization!); - const { username, privileges } = await checkPrivileges.atSpaces(spaceIds, privilege); + const { username, privileges } = await checkPrivileges.atSpaces(spaceIds, { + kibana: privilege, + }); - const authorized = privileges.filter((x) => x.authorized).map((x) => x.resource); + const authorized = privileges.kibana.filter((x) => x.authorized).map((x) => x.resource); this.debugLogger( `SpacesClient.getAll(), authorized for ${ @@ -234,7 +236,7 @@ export class SpacesClient { private async ensureAuthorizedGlobally(action: string, method: string, forbiddenMessage: string) { const checkPrivileges = this.authorization!.checkPrivilegesWithRequest(this.request); - const { username, hasAllRequested } = await checkPrivileges.globally(action); + const { username, hasAllRequested } = await checkPrivileges.globally({ kibana: action }); if (hasAllRequested) { this.auditLogger.spacesAuthorizationSuccess(username, method); @@ -252,7 +254,9 @@ export class SpacesClient { forbiddenMessage: string ) { const checkPrivileges = this.authorization!.checkPrivilegesWithRequest(this.request); - const { username, hasAllRequested } = await checkPrivileges.atSpace(spaceId, action); + const { username, hasAllRequested } = await checkPrivileges.atSpace(spaceId, { + kibana: action, + }); if (hasAllRequested) { this.auditLogger.spacesAuthorizationSuccess(username, method, [spaceId]); diff --git a/x-pack/plugins/spaces/server/lib/utils/__mocks__/index.ts b/x-pack/plugins/spaces/server/lib/utils/__mocks__/index.ts new file mode 100644 index 000000000000..2b93e6d87a7a --- /dev/null +++ b/x-pack/plugins/spaces/server/lib/utils/__mocks__/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +const mockNamespaceIdToString = jest.fn(); +const mockNamespaceStringToId = jest.fn(); +jest.mock('../../../../../../../src/core/server', () => ({ + SavedObjectsUtils: { + namespaceIdToString: mockNamespaceIdToString, + namespaceStringToId: mockNamespaceStringToId, + }, +})); + +export { mockNamespaceIdToString, mockNamespaceStringToId }; diff --git a/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts b/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts index a81a5f3cee18..79d3dda30104 100644 --- a/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts +++ b/x-pack/plugins/spaces/server/lib/utils/namespace.test.ts @@ -4,45 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DEFAULT_SPACE_ID } from '../../../common/constants'; +import { mockNamespaceIdToString, mockNamespaceStringToId } from './__mocks__'; import { spaceIdToNamespace, namespaceToSpaceId } from './namespace'; -describe('#spaceIdToNamespace', () => { - it('converts the default space to undefined', () => { - expect(spaceIdToNamespace(DEFAULT_SPACE_ID)).toBeUndefined(); - }); - - it('returns non-default spaces as-is', () => { - expect(spaceIdToNamespace('foo')).toEqual('foo'); - }); - - it('throws an error when a spaceId is not provided', () => { - // @ts-ignore ts knows this isn't right - expect(() => spaceIdToNamespace()).toThrowErrorMatchingInlineSnapshot(`"spaceId is required"`); +beforeEach(() => { + jest.clearAllMocks(); +}); - // @ts-ignore ts knows this isn't right - expect(() => spaceIdToNamespace(null)).toThrowErrorMatchingInlineSnapshot( - `"spaceId is required"` - ); +describe('#spaceIdToNamespace', () => { + it('returns result of namespaceStringToId', () => { + mockNamespaceStringToId.mockReturnValue('bar'); - expect(() => spaceIdToNamespace('')).toThrowErrorMatchingInlineSnapshot( - `"spaceId is required"` - ); + const result = spaceIdToNamespace('foo'); + expect(mockNamespaceStringToId).toHaveBeenCalledWith('foo'); + expect(result).toEqual('bar'); }); }); describe('#namespaceToSpaceId', () => { - it('returns the default space id for undefined namespaces', () => { - expect(namespaceToSpaceId(undefined)).toEqual(DEFAULT_SPACE_ID); - }); - - it('returns all other namespaces as-is', () => { - expect(namespaceToSpaceId('foo')).toEqual('foo'); - }); + it('returns result of namespaceIdToString', () => { + mockNamespaceIdToString.mockReturnValue('bar'); - it('throws an error when an empty string is provided', () => { - expect(() => namespaceToSpaceId('')).toThrowErrorMatchingInlineSnapshot( - `"namespace cannot be an empty string"` - ); + const result = namespaceToSpaceId('foo'); + expect(mockNamespaceIdToString).toHaveBeenCalledWith('foo'); + expect(result).toEqual('bar'); }); }); diff --git a/x-pack/plugins/spaces/server/lib/utils/namespace.ts b/x-pack/plugins/spaces/server/lib/utils/namespace.ts index 8c7ed2ea1797..344da18846f3 100644 --- a/x-pack/plugins/spaces/server/lib/utils/namespace.ts +++ b/x-pack/plugins/spaces/server/lib/utils/namespace.ts @@ -4,28 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DEFAULT_SPACE_ID } from '../../../common/constants'; +import { SavedObjectsUtils } from '../../../../../../src/core/server'; -export function spaceIdToNamespace(spaceId: string): string | undefined { - if (!spaceId) { - throw new TypeError('spaceId is required'); - } - - if (spaceId === DEFAULT_SPACE_ID) { - return undefined; - } - - return spaceId; +/** + * Converts a Space ID string to its namespace ID representation. Note that a Space ID string is equivalent to a namespace string. + * + * See also: {@link namespaceStringToId}. + */ +export function spaceIdToNamespace(spaceId: string) { + return SavedObjectsUtils.namespaceStringToId(spaceId); } -export function namespaceToSpaceId(namespace: string | undefined): string { - if (namespace === '') { - throw new TypeError('namespace cannot be an empty string'); - } - - if (!namespace) { - return DEFAULT_SPACE_ID; - } - - return namespace; +/** + * Converts a namespace ID to its Space ID string representation. Note that a Space ID string is equivalent to a namespace string. + * + * See also: {@link namespaceIdToString}. + */ +export function namespaceToSpaceId(namespace?: string) { + return SavedObjectsUtils.namespaceIdToString(namespace); } diff --git a/x-pack/plugins/spaces/server/plugin.test.ts b/x-pack/plugins/spaces/server/plugin.test.ts index a82f2370cc12..b650a114ed97 100644 --- a/x-pack/plugins/spaces/server/plugin.test.ts +++ b/x-pack/plugins/spaces/server/plugin.test.ts @@ -8,14 +8,14 @@ import { CoreSetup } from 'src/core/server'; import { coreMock } from 'src/core/server/mocks'; import { featuresPluginMock } from '../../features/server/mocks'; import { licensingMock } from '../../licensing/server/mocks'; -import { Plugin, PluginsSetup } from './plugin'; +import { Plugin, PluginsStart } from './plugin'; import { usageCollectionPluginMock } from '../../../../src/plugins/usage_collection/server/mocks'; describe('Spaces Plugin', () => { describe('#setup', () => { it('can setup with all optional plugins disabled, exposing the expected contract', async () => { const initializerContext = coreMock.createPluginInitializerContext({}); - const core = coreMock.createSetup() as CoreSetup; + const core = coreMock.createSetup() as CoreSetup; const features = featuresPluginMock.createSetup(); const licensing = licensingMock.createSetup(); @@ -38,7 +38,7 @@ describe('Spaces Plugin', () => { it('registers the capabilities provider and switcher', async () => { const initializerContext = coreMock.createPluginInitializerContext({}); - const core = coreMock.createSetup() as CoreSetup; + const core = coreMock.createSetup() as CoreSetup; const features = featuresPluginMock.createSetup(); const licensing = licensingMock.createSetup(); @@ -52,7 +52,7 @@ describe('Spaces Plugin', () => { it('registers the usage collector', async () => { const initializerContext = coreMock.createPluginInitializerContext({}); - const core = coreMock.createSetup() as CoreSetup; + const core = coreMock.createSetup() as CoreSetup; const features = featuresPluginMock.createSetup(); const licensing = licensingMock.createSetup(); @@ -67,7 +67,7 @@ describe('Spaces Plugin', () => { it('registers the "space" saved object type and client wrapper', async () => { const initializerContext = coreMock.createPluginInitializerContext({}); - const core = coreMock.createSetup() as CoreSetup; + const core = coreMock.createSetup() as CoreSetup; const features = featuresPluginMock.createSetup(); const licensing = licensingMock.createSetup(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts index bec3a5dcb0b7..dce6de908cfc 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts @@ -30,10 +30,10 @@ import { securityMock } from '../../../../../security/server/mocks'; import { ObjectType } from '@kbn/config-schema'; jest.mock('../../../../../../../src/core/server', () => { return { + ...(jest.requireActual('../../../../../../../src/core/server') as Record), exportSavedObjectsToStream: jest.fn(), importSavedObjectsFromStream: jest.fn(), resolveSavedObjectsImportErrors: jest.fn(), - kibanaResponseFactory: jest.requireActual('src/core/server').kibanaResponseFactory, }; }); import { diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts index 57ec688ab70e..fddd7f92b7f2 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts @@ -7,18 +7,18 @@ import { getSpacesUsageCollector, UsageStats } from './spaces_usage_collector'; import * as Rx from 'rxjs'; import { PluginsSetup } from '../plugin'; -import { Feature } from '../../../features/server'; +import { KibanaFeature } from '../../../features/server'; import { ILicense, LicensingPluginSetup } from '../../../licensing/server'; import { pluginInitializerContextConfigMock } from 'src/core/server/mocks'; interface SetupOpts { license?: Partial; - features?: Feature[]; + features?: KibanaFeature[]; } function setup({ license = { isAvailable: true }, - features = [{ id: 'feature1' } as Feature, { id: 'feature2' } as Feature], + features = [{ id: 'feature1' } as KibanaFeature, { id: 'feature2' } as KibanaFeature], }: SetupOpts = {}) { class MockUsageCollector { private fetch: any; @@ -37,7 +37,7 @@ function setup({ } as LicensingPluginSetup; const featuresSetup = ({ - getFeatures: jest.fn().mockReturnValue(features), + getKibanaFeatures: jest.fn().mockReturnValue(features), } as unknown) as PluginsSetup['features']; return { diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 3ea4693d9e9d..36d46c3d01ba 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -46,7 +46,7 @@ async function getSpacesUsage( return null; } - const knownFeatureIds = features.getFeatures().map((feature) => feature.id); + const knownFeatureIds = features.getKibanaFeatures().map((feature) => feature.id); let resp: SpacesAggregationResponse | undefined; try { diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index a02123c4a3f8..45c41b4d1d69 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -633,7 +633,7 @@ if (doc['task.runAt'].size()!=0) { const runAt = new Date(); const tasks = [ { - _id: 'aaa', + _id: 'task:aaa', _source: { type: 'task', task: { @@ -654,7 +654,104 @@ if (doc['task.runAt'].size()!=0) { sort: ['a', 1], }, { + // this is invalid as it doesn't have the `type` prefix _id: 'bbb', + _source: { + type: 'task', + task: { + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: 'claiming', + params: '{ "shazm": 1 }', + state: '{ "henry": "The 8th" }', + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + }, + }, + _seq_no: 3, + _primary_term: 4, + sort: ['b', 2], + }, + ]; + const { + result: { docs }, + args: { + search: { + body: { query }, + }, + }, + } = await testClaimAvailableTasks({ + opts: { + taskManagerId, + }, + claimingOpts: { + claimOwnershipUntil, + size: 10, + }, + hits: tasks, + }); + + expect(query.bool.must).toContainEqual({ + bool: { + must: [ + { + term: { + 'task.ownerId': taskManagerId, + }, + }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }); + + expect(docs).toMatchObject([ + { + attempts: 0, + id: 'aaa', + schedule: undefined, + params: { hello: 'world' }, + runAt, + scope: ['reporting'], + state: { baby: 'Henhen' }, + status: 'claiming', + taskType: 'foo', + user: 'jimbo', + ownerId: taskManagerId, + }, + ]); + }); + + test('it filters out invalid tasks that arent SavedObjects', async () => { + const taskManagerId = uuid.v1(); + const claimOwnershipUntil = new Date(Date.now()); + const runAt = new Date(); + const tasks = [ + { + _id: 'task:aaa', + _source: { + type: 'task', + task: { + runAt, + taskType: 'foo', + schedule: undefined, + attempts: 0, + status: 'claiming', + params: '{ "hello": "world" }', + state: '{ "baby": "Henhen" }', + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + }, + }, + _seq_no: 1, + _primary_term: 2, + sort: ['a', 1], + }, + { + _id: 'task:bbb', _source: { type: 'task', task: { @@ -729,7 +826,7 @@ if (doc['task.runAt'].size()!=0) { const runAt = new Date(); const tasks = [ { - _id: 'aaa', + _id: 'task:aaa', _source: { type: 'task', task: { @@ -750,7 +847,7 @@ if (doc['task.runAt'].size()!=0) { sort: ['a', 1], }, { - _id: 'bbb', + _id: 'task:bbb', _source: { type: 'task', task: { @@ -1069,7 +1166,7 @@ if (doc['task.runAt'].size()!=0) { const runAt = new Date(); const tasks = [ { - _id: 'claimed-by-id', + _id: 'task:claimed-by-id', _source: { type: 'task', task: { @@ -1093,7 +1190,7 @@ if (doc['task.runAt'].size()!=0) { sort: ['a', 1], }, { - _id: 'claimed-by-schedule', + _id: 'task:claimed-by-schedule', _source: { type: 'task', task: { @@ -1117,7 +1214,7 @@ if (doc['task.runAt'].size()!=0) { sort: ['b', 2], }, { - _id: 'already-running', + _id: 'task:already-running', _source: { type: 'task', task: { @@ -1378,8 +1475,8 @@ if (doc['task.runAt'].size()!=0) { }); function generateFakeTasks(count: number = 1) { - return _.times(count, () => ({ - _id: 'aaa', + return _.times(count, (index) => ({ + _id: `task:id-${index}`, _source: { type: 'task', task: {}, diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index f2da41053e6a..acd19bd75f7a 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -451,6 +451,7 @@ export class TaskStore { return { docs: (rawDocs as SavedObjectsRawDoc[]) + .filter((doc) => this.serializer.isRawSavedObject(doc)) .map((doc) => this.serializer.rawToSavedObject(doc)) .map((doc) => omit(doc, 'namespace') as SavedObject) .map(savedObjectToConcreteTaskInstance), diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index a7330d3ebd55..904b14a7459a 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -297,6 +297,28 @@ } } }, + "security": { + "properties": { + "auditLoggingEnabled": { + "type": "boolean" + }, + "loginSelectorEnabled": { + "type": "boolean" + }, + "accessAgreementEnabled": { + "type": "boolean" + }, + "authProviderCount": { + "type": "number" + }, + "enabledAuthProviders": { + "type": "keyword" + }, + "httpAuthSchemes": { + "type": "keyword" + } + } + }, "spaces": { "properties": { "usesFeatureControls": { diff --git a/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.d.ts b/x-pack/plugins/transform/common/api_schemas/audit_messages.ts similarity index 61% rename from x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.d.ts rename to x-pack/plugins/transform/common/api_schemas/audit_messages.ts index 29a537a7ca8d..76e63af26267 100644 --- a/x-pack/plugins/ml/public/application/components/messagebar/messagebar_service.d.ts +++ b/x-pack/plugins/transform/common/api_schemas/audit_messages.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -declare interface MlMessageBarService { - notify: { - error(text: any, resp?: any): void; - }; -} +import { TransformMessage } from '../types/messages'; -export const mlMessageBarService: MlMessageBarService; +export type GetTransformsAuditMessagesResponseSchema = TransformMessage[]; diff --git a/x-pack/plugins/transform/common/api_schemas/common.ts b/x-pack/plugins/transform/common/api_schemas/common.ts new file mode 100644 index 000000000000..80b14ce6adee --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/common.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +import { TRANSFORM_STATE } from '../constants'; + +export const transformIdsSchema = schema.arrayOf( + schema.object({ + id: schema.string(), + }) +); + +export type TransformIdsSchema = TypeOf; + +export const transformStateSchema = schema.oneOf([ + schema.literal(TRANSFORM_STATE.ABORTING), + schema.literal(TRANSFORM_STATE.FAILED), + schema.literal(TRANSFORM_STATE.INDEXING), + schema.literal(TRANSFORM_STATE.STARTED), + schema.literal(TRANSFORM_STATE.STOPPED), + schema.literal(TRANSFORM_STATE.STOPPING), +]); + +export const indexPatternTitleSchema = schema.object({ + /** Title of the index pattern for which to return stats. */ + indexPatternTitle: schema.string(), +}); + +export type IndexPatternTitleSchema = TypeOf; + +export const transformIdParamSchema = schema.object({ + transformId: schema.string(), +}); + +export type TransformIdParamSchema = TypeOf; + +export interface ResponseStatus { + success: boolean; + error?: any; +} + +export interface CommonResponseStatusSchema { + [key: string]: ResponseStatus; +} diff --git a/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts b/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts new file mode 100644 index 000000000000..c4d1a1f5f758 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/delete_transforms.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +import { transformStateSchema, ResponseStatus } from './common'; + +export const deleteTransformsRequestSchema = schema.object({ + /** + * Delete Transform & Destination Index + */ + transformsInfo: schema.arrayOf( + schema.object({ + id: schema.string(), + state: transformStateSchema, + }) + ), + deleteDestIndex: schema.maybe(schema.boolean()), + deleteDestIndexPattern: schema.maybe(schema.boolean()), + forceDelete: schema.maybe(schema.boolean()), +}); + +export type DeleteTransformsRequestSchema = TypeOf; + +export interface DeleteTransformStatus { + transformDeleted: ResponseStatus; + destIndexDeleted?: ResponseStatus; + destIndexPatternDeleted?: ResponseStatus; + destinationIndex?: string | undefined; +} + +export interface DeleteTransformsResponseSchema { + [key: string]: DeleteTransformStatus; +} diff --git a/x-pack/plugins/transform/common/api_schemas/field_histograms.ts b/x-pack/plugins/transform/common/api_schemas/field_histograms.ts new file mode 100644 index 000000000000..3bdbb5f1ff70 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/field_histograms.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +export const fieldHistogramsRequestSchema = schema.object({ + /** Query to match documents in the index. */ + query: schema.any(), + /** The fields to return histogram data. */ + fields: schema.arrayOf(schema.any()), + /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */ + samplerShardSize: schema.number(), +}); + +export type FieldHistogramsRequestSchema = TypeOf; +export type FieldHistogramsResponseSchema = any[]; diff --git a/x-pack/plugins/transform/common/api_schemas/start_transforms.ts b/x-pack/plugins/transform/common/api_schemas/start_transforms.ts new file mode 100644 index 000000000000..b9611636e61a --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/start_transforms.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 { TypeOf } from '@kbn/config-schema'; + +import { transformIdsSchema, CommonResponseStatusSchema } from './common'; + +export const startTransformsRequestSchema = transformIdsSchema; +export type StartTransformsRequestSchema = TypeOf; +export type StartTransformsResponseSchema = CommonResponseStatusSchema; diff --git a/x-pack/plugins/transform/common/api_schemas/stop_transforms.ts b/x-pack/plugins/transform/common/api_schemas/stop_transforms.ts new file mode 100644 index 000000000000..56956de20b49 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/stop_transforms.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +import { transformStateSchema, CommonResponseStatusSchema } from './common'; + +export const stopTransformsRequestSchema = schema.arrayOf( + schema.object({ + id: schema.string(), + state: transformStateSchema, + }) +); + +export type StopTransformsRequestSchema = TypeOf; +export type StopTransformsResponseSchema = CommonResponseStatusSchema; diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts new file mode 100644 index 000000000000..155807a5c445 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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'; + +import type { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common'; + +import type { Dictionary } from '../types/common'; +import type { PivotAggDict } from '../types/pivot_aggs'; +import type { PivotGroupByDict } from '../types/pivot_group_by'; +import type { TransformId, TransformPivotConfig } from '../types/transform'; + +import { transformStateSchema } from './common'; + +// GET transforms +export const getTransformsRequestSchema = schema.arrayOf( + schema.object({ + id: schema.string(), + state: transformStateSchema, + }) +); + +export type GetTransformsRequestSchema = TypeOf; + +export interface GetTransformsResponseSchema { + count: number; + transforms: TransformPivotConfig[]; +} + +// schemas shared by parts of the preview, create and update endpoint +export const destSchema = schema.object({ + index: schema.string(), + pipeline: schema.maybe(schema.string()), +}); +export const pivotSchema = schema.object({ + group_by: schema.any(), + aggregations: schema.any(), +}); +export const settingsSchema = schema.object({ + max_page_search_size: schema.maybe(schema.number()), + // The default value is null, which disables throttling. + docs_per_second: schema.maybe(schema.nullable(schema.number())), +}); +export const sourceSchema = schema.object({ + index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), +}); +export const syncSchema = schema.object({ + time: schema.object({ + delay: schema.maybe(schema.string()), + field: schema.string(), + }), +}); + +// PUT transforms/{transformId} +export const putTransformsRequestSchema = schema.object({ + description: schema.maybe(schema.string()), + dest: destSchema, + frequency: schema.maybe(schema.string()), + pivot: pivotSchema, + settings: schema.maybe(settingsSchema), + source: sourceSchema, + sync: schema.maybe(syncSchema), +}); + +export interface PutTransformsRequestSchema extends TypeOf { + pivot: { + group_by: PivotGroupByDict; + aggregations: PivotAggDict; + }; +} + +interface TransformCreated { + transform: TransformId; +} +interface TransformCreatedError { + id: TransformId; + error: any; +} +export interface PutTransformsResponseSchema { + transformsCreated: TransformCreated[]; + errors: TransformCreatedError[]; +} + +// POST transforms/_preview +export const postTransformsPreviewRequestSchema = schema.object({ + pivot: pivotSchema, + source: sourceSchema, +}); + +export interface PostTransformsPreviewRequestSchema + extends TypeOf { + pivot: { + group_by: PivotGroupByDict; + aggregations: PivotAggDict; + }; +} + +interface EsMappingType { + type: ES_FIELD_TYPES; +} + +export type PreviewItem = Dictionary; +export type PreviewData = PreviewItem[]; +export type PreviewMappingsProperties = Dictionary; + +export interface PostTransformsPreviewResponseSchema { + generated_dest_index: { + mappings: { + _meta: { + _transform: { + transform: string; + version: { create: string }; + creation_date_in_millis: number; + }; + created_by: string; + }; + properties: PreviewMappingsProperties; + }; + settings: { index: { number_of_shards: string; auto_expand_replicas: string } }; + aliases: Record; + }; + preview: PreviewData; +} diff --git a/x-pack/plugins/transform/common/api_schemas/transforms_stats.ts b/x-pack/plugins/transform/common/api_schemas/transforms_stats.ts new file mode 100644 index 000000000000..30661a8a407d --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/transforms_stats.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TypeOf } from '@kbn/config-schema'; + +import { TransformStats } from '../types/transform_stats'; + +import { getTransformsRequestSchema } from './transforms'; + +export const getTransformsStatsRequestSchema = getTransformsRequestSchema; + +export type GetTransformsRequestSchema = TypeOf; + +export interface GetTransformsStatsResponseSchema { + node_failures?: object; + count: number; + transforms: TransformStats[]; +} diff --git a/x-pack/plugins/transform/common/api_schemas/type_guards.ts b/x-pack/plugins/transform/common/api_schemas/type_guards.ts new file mode 100644 index 000000000000..f9753a412527 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/type_guards.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import type { SearchResponse7 } from '../../../ml/common'; + +import type { EsIndex } from '../types/es_index'; + +// To be able to use the type guards on the client side, we need to make sure we don't import +// the code of '@kbn/config-schema' but just its types, otherwise the client side code will +// fail to build. +import type { FieldHistogramsResponseSchema } from './field_histograms'; +import type { GetTransformsAuditMessagesResponseSchema } from './audit_messages'; +import type { DeleteTransformsResponseSchema } from './delete_transforms'; +import type { StartTransformsResponseSchema } from './start_transforms'; +import type { StopTransformsResponseSchema } from './stop_transforms'; +import type { + GetTransformsResponseSchema, + PostTransformsPreviewResponseSchema, + PutTransformsResponseSchema, +} from './transforms'; +import type { GetTransformsStatsResponseSchema } from './transforms_stats'; +import type { PostTransformsUpdateResponseSchema } from './update_transforms'; + +const isBasicObject = (arg: any) => { + return typeof arg === 'object' && arg !== null; +}; + +const isGenericResponseSchema = (arg: any): arg is T => { + return ( + isBasicObject(arg) && + {}.hasOwnProperty.call(arg, 'count') && + {}.hasOwnProperty.call(arg, 'transforms') && + Array.isArray(arg.transforms) + ); +}; + +export const isGetTransformsResponseSchema = (arg: any): arg is GetTransformsResponseSchema => { + return isGenericResponseSchema(arg); +}; + +export const isGetTransformsStatsResponseSchema = ( + arg: any +): arg is GetTransformsStatsResponseSchema => { + return isGenericResponseSchema(arg); +}; + +export const isDeleteTransformsResponseSchema = ( + arg: any +): arg is DeleteTransformsResponseSchema => { + return ( + isBasicObject(arg) && + Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'transformDeleted'))) + ); +}; + +export const isEsIndices = (arg: any): arg is EsIndex[] => { + return Array.isArray(arg); +}; + +export const isEsSearchResponse = (arg: any): arg is SearchResponse7 => { + return isBasicObject(arg) && {}.hasOwnProperty.call(arg, 'hits'); +}; + +export const isFieldHistogramsResponseSchema = (arg: any): arg is FieldHistogramsResponseSchema => { + return Array.isArray(arg); +}; + +export const isGetTransformsAuditMessagesResponseSchema = ( + arg: any +): arg is GetTransformsAuditMessagesResponseSchema => { + return Array.isArray(arg); +}; + +export const isPostTransformsPreviewResponseSchema = ( + arg: any +): arg is PostTransformsPreviewResponseSchema => { + return ( + isBasicObject(arg) && + {}.hasOwnProperty.call(arg, 'generated_dest_index') && + {}.hasOwnProperty.call(arg, 'preview') && + typeof arg.generated_dest_index !== undefined && + Array.isArray(arg.preview) + ); +}; + +export const isPostTransformsUpdateResponseSchema = ( + arg: any +): arg is PostTransformsUpdateResponseSchema => { + return isBasicObject(arg) && {}.hasOwnProperty.call(arg, 'id') && typeof arg.id === 'string'; +}; + +export const isPutTransformsResponseSchema = (arg: any): arg is PutTransformsResponseSchema => { + return ( + isBasicObject(arg) && + {}.hasOwnProperty.call(arg, 'transformsCreated') && + {}.hasOwnProperty.call(arg, 'errors') && + Array.isArray(arg.transformsCreated) && + Array.isArray(arg.errors) + ); +}; + +const isGenericSuccessResponseSchema = (arg: any) => + isBasicObject(arg) && Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'success'))); + +export const isStartTransformsResponseSchema = (arg: any): arg is StartTransformsResponseSchema => { + return isGenericSuccessResponseSchema(arg); +}; + +export const isStopTransformsResponseSchema = (arg: any): arg is StopTransformsResponseSchema => { + return isGenericSuccessResponseSchema(arg); +}; diff --git a/x-pack/plugins/transform/common/api_schemas/update_transforms.ts b/x-pack/plugins/transform/common/api_schemas/update_transforms.ts new file mode 100644 index 000000000000..e303d94ef053 --- /dev/null +++ b/x-pack/plugins/transform/common/api_schemas/update_transforms.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +import { TransformPivotConfig } from '../types/transform'; + +import { destSchema, settingsSchema, sourceSchema, syncSchema } from './transforms'; + +// POST _transform/{transform_id}/_update +export const postTransformsUpdateRequestSchema = schema.object({ + description: schema.maybe(schema.string()), + dest: schema.maybe(destSchema), + frequency: schema.maybe(schema.string()), + settings: schema.maybe(settingsSchema), + source: schema.maybe(sourceSchema), + sync: schema.maybe(syncSchema), +}); + +export type PostTransformsUpdateRequestSchema = TypeOf; +export type PostTransformsUpdateResponseSchema = TransformPivotConfig; diff --git a/x-pack/plugins/transform/common/constants.ts b/x-pack/plugins/transform/common/constants.ts index b01a82dffa04..5efb6f31c1e3 100644 --- a/x-pack/plugins/transform/common/constants.ts +++ b/x-pack/plugins/transform/common/constants.ts @@ -75,3 +75,24 @@ export const APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES = [ ]; export const APP_INDEX_PRIVILEGES = ['monitor']; + +// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243 +export const TRANSFORM_STATE = { + ABORTING: 'aborting', + FAILED: 'failed', + INDEXING: 'indexing', + STARTED: 'started', + STOPPED: 'stopped', + STOPPING: 'stopping', +} as const; + +const transformStates = Object.values(TRANSFORM_STATE); +export type TransformState = typeof transformStates[number]; + +export const TRANSFORM_MODE = { + BATCH: 'batch', + CONTINUOUS: 'continuous', +} as const; + +const transformModes = Object.values(TRANSFORM_MODE); +export type TransformMode = typeof transformModes[number]; diff --git a/x-pack/plugins/transform/common/index.ts b/x-pack/plugins/transform/common/index.ts deleted file mode 100644 index 08bb4022c701..000000000000 --- a/x-pack/plugins/transform/common/index.ts +++ /dev/null @@ -1,58 +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 interface MissingPrivileges { - [key: string]: string[] | undefined; -} - -export interface Privileges { - hasAllPrivileges: boolean; - missingPrivileges: MissingPrivileges; -} - -export type TransformId = string; - -// reflects https://github.com/elastic/elasticsearch/blob/master/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/transforms/DataFrameTransformStats.java#L243 -export enum TRANSFORM_STATE { - ABORTING = 'aborting', - FAILED = 'failed', - INDEXING = 'indexing', - STARTED = 'started', - STOPPED = 'stopped', - STOPPING = 'stopping', -} - -export interface TransformEndpointRequest { - id: TransformId; - state?: TRANSFORM_STATE; -} - -export interface ResultData { - success: boolean; - error?: any; -} - -export interface TransformEndpointResult { - [key: string]: ResultData; -} - -export interface DeleteTransformEndpointRequest { - transformsInfo: TransformEndpointRequest[]; - deleteDestIndex?: boolean; - deleteDestIndexPattern?: boolean; - forceDelete?: boolean; -} - -export interface DeleteTransformStatus { - transformDeleted: ResultData; - destIndexDeleted?: ResultData; - destIndexPatternDeleted?: ResultData; - destinationIndex?: string | undefined; -} - -export interface DeleteTransformEndpointResult { - [key: string]: DeleteTransformStatus; -} diff --git a/x-pack/plugins/transform/common/shared_imports.ts b/x-pack/plugins/transform/common/shared_imports.ts new file mode 100644 index 000000000000..8681204755c3 --- /dev/null +++ b/x-pack/plugins/transform/common/shared_imports.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type { SearchResponse7 } from '../../ml/common'; diff --git a/x-pack/plugins/transform/common/types/aggregations.ts b/x-pack/plugins/transform/common/types/aggregations.ts new file mode 100644 index 000000000000..77b7e55e3ba9 --- /dev/null +++ b/x-pack/plugins/transform/common/types/aggregations.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type AggName = string; diff --git a/x-pack/plugins/transform/public/app/hooks/use_api_types.ts b/x-pack/plugins/transform/common/types/es_index.ts similarity index 100% rename from x-pack/plugins/transform/public/app/hooks/use_api_types.ts rename to x-pack/plugins/transform/common/types/es_index.ts diff --git a/x-pack/plugins/transform/common/types/fields.ts b/x-pack/plugins/transform/common/types/fields.ts new file mode 100644 index 000000000000..2c274f3bd9b4 --- /dev/null +++ b/x-pack/plugins/transform/common/types/fields.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type EsFieldName = string; diff --git a/x-pack/plugins/transform/common/types/pivot_aggs.ts b/x-pack/plugins/transform/common/types/pivot_aggs.ts new file mode 100644 index 000000000000..d50609da6a5d --- /dev/null +++ b/x-pack/plugins/transform/common/types/pivot_aggs.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 { AggName } from './aggregations'; +import { EsFieldName } from './fields'; + +export const PIVOT_SUPPORTED_AGGS = { + AVG: 'avg', + CARDINALITY: 'cardinality', + MAX: 'max', + MIN: 'min', + PERCENTILES: 'percentiles', + SUM: 'sum', + VALUE_COUNT: 'value_count', + FILTER: 'filter', +} as const; + +export type PivotSupportedAggs = typeof PIVOT_SUPPORTED_AGGS[keyof typeof PIVOT_SUPPORTED_AGGS]; + +export type PivotAgg = { + [key in PivotSupportedAggs]?: { + field: EsFieldName; + }; +}; + +export type PivotAggDict = { + [key in AggName]: PivotAgg; +}; diff --git a/x-pack/plugins/transform/common/types/pivot_group_by.ts b/x-pack/plugins/transform/common/types/pivot_group_by.ts new file mode 100644 index 000000000000..bfaf17a32b58 --- /dev/null +++ b/x-pack/plugins/transform/common/types/pivot_group_by.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 { Dictionary } from './common'; +import { EsFieldName } from './fields'; + +export type GenericAgg = object; + +export interface TermsAgg { + terms: { + field: EsFieldName; + }; +} + +export interface HistogramAgg { + histogram: { + field: EsFieldName; + interval: string; + }; +} + +export interface DateHistogramAgg { + date_histogram: { + field: EsFieldName; + calendar_interval: string; + }; +} + +export type PivotGroupBy = GenericAgg | TermsAgg | HistogramAgg | DateHistogramAgg; +export type PivotGroupByDict = Dictionary; diff --git a/x-pack/plugins/transform/common/types/privileges.ts b/x-pack/plugins/transform/common/types/privileges.ts new file mode 100644 index 000000000000..bf710b822559 --- /dev/null +++ b/x-pack/plugins/transform/common/types/privileges.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface MissingPrivileges { + [key: string]: string[] | undefined; +} + +export interface Privileges { + hasAllPrivileges: boolean; + missingPrivileges: MissingPrivileges; +} diff --git a/x-pack/plugins/transform/common/types/transform.ts b/x-pack/plugins/transform/common/types/transform.ts new file mode 100644 index 000000000000..6b3170544270 --- /dev/null +++ b/x-pack/plugins/transform/common/types/transform.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 type { PutTransformsRequestSchema } from '../api_schemas/transforms'; + +export type IndexName = string; +export type IndexPattern = string; +export type TransformId = string; + +export interface TransformPivotConfig extends PutTransformsRequestSchema { + id: TransformId; + create_time?: number; + version?: string; +} diff --git a/x-pack/plugins/transform/common/types/transform_stats.ts b/x-pack/plugins/transform/common/types/transform_stats.ts new file mode 100644 index 000000000000..5bd2fd955845 --- /dev/null +++ b/x-pack/plugins/transform/common/types/transform_stats.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 { TransformState, TRANSFORM_STATE } from '../constants'; +import { TransformId } from './transform'; + +export interface TransformStats { + id: TransformId; + checkpointing: { + last: { + checkpoint: number; + timestamp_millis?: number; + }; + next?: { + checkpoint: number; + checkpoint_progress?: { + total_docs: number; + docs_remaining: number; + percent_complete: number; + }; + }; + operations_behind: number; + }; + node?: { + id: string; + name: string; + ephemeral_id: string; + transport_address: string; + attributes: Record; + }; + stats: { + documents_indexed: number; + documents_processed: number; + index_failures: number; + index_time_in_ms: number; + index_total: number; + pages_processed: number; + search_failures: number; + search_time_in_ms: number; + search_total: number; + trigger_count: number; + processing_time_in_ms: number; + processing_total: number; + exponential_avg_checkpoint_duration_ms: number; + exponential_avg_documents_indexed: number; + exponential_avg_documents_processed: number; + }; + reason?: string; + state: TransformState; +} + +export function isTransformStats(arg: any): arg is TransformStats { + return ( + typeof arg === 'object' && + arg !== null && + {}.hasOwnProperty.call(arg, 'state') && + Object.values(TRANSFORM_STATE).includes(arg.state) + ); +} diff --git a/x-pack/plugins/transform/common/utils/errors.ts b/x-pack/plugins/transform/common/utils/errors.ts new file mode 100644 index 000000000000..0c31d7e1584f --- /dev/null +++ b/x-pack/plugins/transform/common/utils/errors.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ErrorResponse { + body: { + statusCode: number; + error: string; + message: string; + attributes?: any; + }; + name: string; +} + +export function isErrorResponse(arg: any): arg is ErrorResponse { + return arg?.body?.error !== undefined && arg?.body?.message !== undefined; +} + +export function getErrorMessage(error: any) { + if (isErrorResponse(error)) { + return `${error.body.error}: ${error.body.message}`; + } + + if (typeof error === 'object' && typeof error.message === 'string') { + return error.message; + } + + return JSON.stringify(error); +} diff --git a/x-pack/plugins/transform/kibana.json b/x-pack/plugins/transform/kibana.json index d7e7a7fabba4..2efe0bb25bc6 100644 --- a/x-pack/plugins/transform/kibana.json +++ b/x-pack/plugins/transform/kibana.json @@ -7,7 +7,8 @@ "data", "home", "licensing", - "management" + "management", + "features" ], "optionalPlugins": [ "security", diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index e115e086f45b..470c42d5de7f 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -15,7 +15,6 @@ export const useRequest = jest.fn(() => ({ // just passing through the reimports export { - getErrorMessage, getDataGridSchemaFromKibanaFieldType, getFieldsFromKibanaIndexPattern, multiColumnSortFactory, @@ -24,7 +23,6 @@ export { DataGrid, EsSorting, RenderCellValue, - SearchResponse7, UseDataGridReturnType, UseIndexDataReturnType, INDEX_STATUS, diff --git a/x-pack/plugins/transform/public/app/common/aggregations.ts b/x-pack/plugins/transform/public/app/common/aggregations.ts index 397a58006f1d..507579d37435 100644 --- a/x-pack/plugins/transform/public/app/common/aggregations.ts +++ b/x-pack/plugins/transform/public/app/common/aggregations.ts @@ -6,7 +6,7 @@ import { composeValidators, patternValidator } from '../../../../ml/public'; -export type AggName = string; +import { AggName } from '../../../common/types/aggregations'; export function isAggName(arg: any): arg is AggName { // allow all characters except `[]>` and must not start or end with a space. diff --git a/x-pack/plugins/transform/public/app/common/data_grid.test.ts b/x-pack/plugins/transform/public/app/common/data_grid.test.ts index 0e5ecb5d3b21..6d96f614b28a 100644 --- a/x-pack/plugins/transform/public/app/common/data_grid.test.ts +++ b/x-pack/plugins/transform/public/app/common/data_grid.test.ts @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs'; + import { - getPreviewRequestBody, + getPreviewTransformRequestBody, PivotAggsConfig, PivotGroupByConfig, - PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, SimpleQuery, } from '../common'; @@ -35,7 +36,12 @@ describe('Transform: Data Grid', () => { aggName: 'the-agg-agg-name', dropDownName: 'the-agg-drop-down-name', }; - const request = getPreviewRequestBody('the-index-pattern-title', query, [groupBy], [agg]); + const request = getPreviewTransformRequestBody( + 'the-index-pattern-title', + query, + [groupBy], + [agg] + ); const pivotPreviewDevConsoleStatement = getPivotPreviewDevConsoleStatement(request); expect(pivotPreviewDevConsoleStatement).toBe(`POST _transform/_preview diff --git a/x-pack/plugins/transform/public/app/common/data_grid.ts b/x-pack/plugins/transform/public/app/common/data_grid.ts index cf9ba5d6f585..08f834431fa8 100644 --- a/x-pack/plugins/transform/public/app/common/data_grid.ts +++ b/x-pack/plugins/transform/public/app/common/data_grid.ts @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import type { PostTransformsPreviewRequestSchema } from '../../../common/api_schemas/transforms'; + import { PivotQuery } from './request'; -import { PreviewRequestBody } from './transform'; export const INIT_MAX_COLUMNS = 20; -export const getPivotPreviewDevConsoleStatement = (request: PreviewRequestBody) => { +export const getPivotPreviewDevConsoleStatement = (request: PostTransformsPreviewRequestSchema) => { return `POST _transform/_preview\n${JSON.stringify(request, null, 2)}\n`; }; diff --git a/x-pack/plugins/transform/public/app/common/fields.ts b/x-pack/plugins/transform/public/app/common/fields.ts index b22aae255b9f..778750e1f97e 100644 --- a/x-pack/plugins/transform/public/app/common/fields.ts +++ b/x-pack/plugins/transform/public/app/common/fields.ts @@ -5,10 +5,10 @@ */ import { Dictionary } from '../../../common/types/common'; +import { EsFieldName } from '../../../common/types/fields'; export type EsId = string; export type EsDocSource = Dictionary; -export type EsFieldName = string; export interface EsDoc extends Dictionary { _id: EsId; diff --git a/x-pack/plugins/transform/public/app/common/index.ts b/x-pack/plugins/transform/public/app/common/index.ts index 45ddc440057b..0fc947eaf33b 100644 --- a/x-pack/plugins/transform/public/app/common/index.ts +++ b/x-pack/plugins/transform/public/app/common/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { AggName, isAggName } from './aggregations'; +export { isAggName } from './aggregations'; export { getIndexDevConsoleStatement, getPivotPreviewDevConsoleStatement, @@ -17,44 +17,28 @@ export { toggleSelectedField, EsDoc, EsDocSource, - EsFieldName, } from './fields'; export { DropDownLabel, DropDownOption, Label } from './dropdown'; export { isTransformIdValid, refreshTransformList$, useRefreshTransformList, - CreateRequestBody, - PreviewRequestBody, - TransformPivotConfig, - IndexName, - IndexPattern, REFRESH_TRANSFORM_LIST_STATE, } from './transform'; export { TRANSFORM_LIST_COLUMN, TransformListAction, TransformListRow } from './transform_list'; -export { - getTransformProgress, - isCompletedBatchTransform, - isTransformStats, - TransformStats, - TRANSFORM_MODE, -} from './transform_stats'; +export { getTransformProgress, isCompletedBatchTransform } from './transform_stats'; export { getDiscoverUrl } from './navigation'; -export { GetTransformsResponse, PreviewData, PreviewMappings } from './pivot_preview'; export { getEsAggFromAggConfig, isPivotAggsConfigWithUiSupport, isPivotAggsConfigPercentiles, PERCENTILES_AGG_DEFAULT_PERCENTS, - PivotAgg, - PivotAggDict, PivotAggsConfig, PivotAggsConfigDict, PivotAggsConfigBase, PivotAggsConfigWithUiSupport, PivotAggsConfigWithUiSupportDict, pivotAggsFieldSupport, - PIVOT_SUPPORTED_AGGS, } from './pivot_aggs'; export { dateHistogramIntervalFormatRegex, @@ -65,25 +49,19 @@ export { isGroupByHistogram, isGroupByTerms, pivotGroupByFieldSupport, - DateHistogramAgg, - GenericAgg, GroupByConfigWithInterval, GroupByConfigWithUiSupport, - HistogramAgg, - PivotGroupBy, PivotGroupByConfig, - PivotGroupByDict, PivotGroupByConfigDict, PivotGroupByConfigWithUiSupportDict, PivotSupportedGroupByAggs, PivotSupportedGroupByAggsWithInterval, PIVOT_SUPPORTED_GROUP_BY_AGGS, - TermsAgg, } from './pivot_group_by'; export { defaultQuery, - getPreviewRequestBody, - getCreateRequestBody, + getPreviewTransformRequestBody, + getCreateTransformRequestBody, getPivotQuery, isDefaultQuery, isMatchAllQuery, diff --git a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts index ec52de4b9da9..7a7bb4c65b30 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts @@ -5,31 +5,22 @@ */ import { FC } from 'react'; -import { Dictionary } from '../../../common/types/common'; + import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common'; -import { AggName } from './aggregations'; -import { EsFieldName } from './fields'; +import type { AggName } from '../../../common/types/aggregations'; +import type { Dictionary } from '../../../common/types/common'; +import type { EsFieldName } from '../../../common/types/fields'; +import type { PivotAgg, PivotSupportedAggs } from '../../../common/types/pivot_aggs'; +import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs'; + import { getAggFormConfig } from '../sections/create_transform/components/step_define/common/get_agg_form_config'; import { PivotAggsConfigFilter } from '../sections/create_transform/components/step_define/common/filter_agg/types'; -export type PivotSupportedAggs = typeof PIVOT_SUPPORTED_AGGS[keyof typeof PIVOT_SUPPORTED_AGGS]; - export function isPivotSupportedAggs(arg: any): arg is PivotSupportedAggs { return Object.values(PIVOT_SUPPORTED_AGGS).includes(arg); } -export const PIVOT_SUPPORTED_AGGS = { - AVG: 'avg', - CARDINALITY: 'cardinality', - MAX: 'max', - MIN: 'min', - PERCENTILES: 'percentiles', - SUM: 'sum', - VALUE_COUNT: 'value_count', - FILTER: 'filter', -} as const; - export const PERCENTILES_AGG_DEFAULT_PERCENTS = [1, 5, 25, 50, 75, 95, 99]; export const pivotAggsFieldSupport = { @@ -69,16 +60,6 @@ export const pivotAggsFieldSupport = { [KBN_FIELD_TYPES.CONFLICT]: [PIVOT_SUPPORTED_AGGS.VALUE_COUNT, PIVOT_SUPPORTED_AGGS.FILTER], }; -export type PivotAgg = { - [key in PivotSupportedAggs]?: { - field: EsFieldName; - }; -}; - -export type PivotAggDict = { - [key in AggName]: PivotAgg; -}; - /** * The maximum level of sub-aggregations */ diff --git a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts index 7da52fc01833..2c2bac369c72 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AggName } from '../../../common/types/aggregations'; import { Dictionary } from '../../../common/types/common'; +import { EsFieldName } from '../../../common/types/fields'; +import { GenericAgg } from '../../../common/types/pivot_group_by'; import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common'; -import { AggName } from './aggregations'; -import { EsFieldName } from './fields'; - export enum PIVOT_SUPPORTED_GROUP_BY_AGGS { DATE_HISTOGRAM = 'date_histogram', HISTOGRAM = 'histogram', @@ -106,31 +106,6 @@ export function isPivotGroupByConfigWithUiSupport(arg: any): arg is GroupByConfi return isGroupByDateHistogram(arg) || isGroupByHistogram(arg) || isGroupByTerms(arg); } -export type GenericAgg = object; - -export interface TermsAgg { - terms: { - field: EsFieldName; - }; -} - -export interface HistogramAgg { - histogram: { - field: EsFieldName; - interval: string; - }; -} - -export interface DateHistogramAgg { - date_histogram: { - field: EsFieldName; - calendar_interval: string; - }; -} - -export type PivotGroupBy = GenericAgg | TermsAgg | HistogramAgg | DateHistogramAgg; -export type PivotGroupByDict = Dictionary; - export function getEsAggFromGroupByConfig(groupByConfig: GroupByConfigBase): GenericAgg { const { agg, aggName, dropDownName, ...esAgg } = groupByConfig; diff --git a/x-pack/plugins/transform/public/app/common/pivot_preview.ts b/x-pack/plugins/transform/public/app/common/pivot_preview.ts deleted file mode 100644 index 14368a80b013..000000000000 --- a/x-pack/plugins/transform/public/app/common/pivot_preview.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 { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; - -import { Dictionary } from '../../../common/types/common'; - -interface EsMappingType { - type: ES_FIELD_TYPES; -} - -export type PreviewItem = Dictionary; -export type PreviewData = PreviewItem[]; -export interface PreviewMappings { - properties: Dictionary; -} - -export interface GetTransformsResponse { - preview: PreviewData; - generated_dest_index: { - mappings: PreviewMappings; - // Not in use yet - aliases: any; - settings: any; - }; -} diff --git a/x-pack/plugins/transform/public/app/common/request.test.ts b/x-pack/plugins/transform/public/app/common/request.test.ts index 63f1f8b10ad4..416927c46084 100644 --- a/x-pack/plugins/transform/public/app/common/request.test.ts +++ b/x-pack/plugins/transform/public/app/common/request.test.ts @@ -4,17 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs'; + import { PivotGroupByConfig } from '../common'; import { StepDefineExposedState } from '../sections/create_transform/components/step_define'; import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form'; import { PIVOT_SUPPORTED_GROUP_BY_AGGS } from './pivot_group_by'; -import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from './pivot_aggs'; +import { PivotAggsConfig } from './pivot_aggs'; import { defaultQuery, - getPreviewRequestBody, - getCreateRequestBody, + getPreviewTransformRequestBody, + getCreateTransformRequestBody, getPivotQuery, isDefaultQuery, isMatchAllQuery, @@ -55,7 +57,7 @@ describe('Transform: Common', () => { }); }); - test('getPreviewRequestBody()', () => { + test('getPreviewTransformRequestBody()', () => { const query = getPivotQuery('the-query'); const groupBy: PivotGroupByConfig[] = [ { @@ -73,7 +75,7 @@ describe('Transform: Common', () => { dropDownName: 'the-agg-drop-down-name', }, ]; - const request = getPreviewRequestBody('the-index-pattern-title', query, groupBy, aggs); + const request = getPreviewTransformRequestBody('the-index-pattern-title', query, groupBy, aggs); expect(request).toEqual({ pivot: { @@ -87,7 +89,7 @@ describe('Transform: Common', () => { }); }); - test('getPreviewRequestBody() with comma-separated index pattern', () => { + test('getPreviewTransformRequestBody() with comma-separated index pattern', () => { const query = getPivotQuery('the-query'); const groupBy: PivotGroupByConfig[] = [ { @@ -105,7 +107,7 @@ describe('Transform: Common', () => { dropDownName: 'the-agg-drop-down-name', }, ]; - const request = getPreviewRequestBody( + const request = getPreviewTransformRequestBody( 'the-index-pattern-title,the-other-title', query, groupBy, @@ -124,7 +126,7 @@ describe('Transform: Common', () => { }); }); - test('getCreateRequestBody()', () => { + test('getCreateTransformRequestBody()', () => { const groupBy: PivotGroupByConfig = { agg: PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS, field: 'the-group-by-field', @@ -160,7 +162,7 @@ describe('Transform: Common', () => { valid: true, }; - const request = getCreateRequestBody( + const request = getCreateTransformRequestBody( 'the-index-pattern-title', pivotState, transformDetailsState diff --git a/x-pack/plugins/transform/public/app/common/request.ts b/x-pack/plugins/transform/public/app/common/request.ts index 9a0084c2ebff..10f3a6347702 100644 --- a/x-pack/plugins/transform/public/app/common/request.ts +++ b/x-pack/plugins/transform/public/app/common/request.ts @@ -4,15 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DefaultOperator } from 'elasticsearch'; - +import type { DefaultOperator } from 'elasticsearch'; + +import { HttpFetchError } from '../../../../../../src/core/public'; +import type { IndexPattern } from '../../../../../../src/plugins/data/public'; + +import type { + PostTransformsPreviewRequestSchema, + PutTransformsRequestSchema, +} from '../../../common/api_schemas/transforms'; +import type { + DateHistogramAgg, + HistogramAgg, + TermsAgg, +} from '../../../common/types/pivot_group_by'; import { dictionaryToArray } from '../../../common/types/common'; -import { SavedSearchQuery } from '../hooks/use_search_items'; - -import { StepDefineExposedState } from '../sections/create_transform/components/step_define'; -import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form'; -import { IndexPattern } from '../../../../../../src/plugins/data/public'; +import type { SavedSearchQuery } from '../hooks/use_search_items'; +import type { StepDefineExposedState } from '../sections/create_transform/components/step_define'; +import type { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form'; import { getEsAggFromAggConfig, @@ -24,8 +34,6 @@ import { } from '../common'; import { PivotAggsConfig } from './pivot_aggs'; -import { DateHistogramAgg, HistogramAgg, TermsAgg } from './pivot_group_by'; -import { PreviewRequestBody, CreateRequestBody } from './transform'; export interface SimpleQuery { query_string: { @@ -63,17 +71,18 @@ export function isDefaultQuery(query: PivotQuery): boolean { return isSimpleQuery(query) && query.query_string.query === '*'; } -export function getPreviewRequestBody( +export function getPreviewTransformRequestBody( indexPatternTitle: IndexPattern['title'], query: PivotQuery, groupBy: PivotGroupByConfig[], aggs: PivotAggsConfig[] -): PreviewRequestBody { +): PostTransformsPreviewRequestSchema { const index = indexPatternTitle.split(',').map((name: string) => name.trim()); - const request: PreviewRequestBody = { + const request: PostTransformsPreviewRequestSchema = { source: { index, + ...(!isDefaultQuery(query) && !isMatchAllQuery(query) ? { query } : {}), }, pivot: { group_by: {}, @@ -81,10 +90,6 @@ export function getPreviewRequestBody( }, }; - if (!isDefaultQuery(query) && !isMatchAllQuery(query)) { - request.source.query = query; - } - groupBy.forEach((g) => { if (isGroupByTerms(g)) { const termsAgg: TermsAgg = { @@ -125,37 +130,41 @@ export function getPreviewRequestBody( return request; } -export function getCreateRequestBody( +export const getCreateTransformRequestBody = ( indexPatternTitle: IndexPattern['title'], pivotState: StepDefineExposedState, transformDetailsState: StepDetailsExposedState -): CreateRequestBody { - const request: CreateRequestBody = { - ...getPreviewRequestBody( - indexPatternTitle, - getPivotQuery(pivotState.searchQuery), - dictionaryToArray(pivotState.groupByList), - dictionaryToArray(pivotState.aggList) - ), - // conditionally add optional description - ...(transformDetailsState.transformDescription !== '' - ? { description: transformDetailsState.transformDescription } - : {}), - dest: { - index: transformDetailsState.destinationIndex, - }, - // conditionally add continuous mode config - ...(transformDetailsState.isContinuousModeEnabled - ? { - sync: { - time: { - field: transformDetailsState.continuousModeDateField, - delay: transformDetailsState.continuousModeDelay, - }, +): PutTransformsRequestSchema => ({ + ...getPreviewTransformRequestBody( + indexPatternTitle, + getPivotQuery(pivotState.searchQuery), + dictionaryToArray(pivotState.groupByList), + dictionaryToArray(pivotState.aggList) + ), + // conditionally add optional description + ...(transformDetailsState.transformDescription !== '' + ? { description: transformDetailsState.transformDescription } + : {}), + dest: { + index: transformDetailsState.destinationIndex, + }, + // conditionally add continuous mode config + ...(transformDetailsState.isContinuousModeEnabled + ? { + sync: { + time: { + field: transformDetailsState.continuousModeDateField, + delay: transformDetailsState.continuousModeDelay, }, - } - : {}), - }; - - return request; + }, + } + : {}), +}); + +export function isHttpFetchError(error: any): error is HttpFetchError { + return ( + error instanceof HttpFetchError && + typeof error.name === 'string' && + typeof error.message !== 'undefined' + ); } diff --git a/x-pack/plugins/transform/public/app/common/transform.ts b/x-pack/plugins/transform/public/app/common/transform.ts index a02bed2fa65e..b71bab62096b 100644 --- a/x-pack/plugins/transform/public/app/common/transform.ts +++ b/x-pack/plugins/transform/public/app/common/transform.ts @@ -9,13 +9,7 @@ import { BehaviorSubject } from 'rxjs'; import { filter, distinctUntilChanged } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -import { TransformId } from '../../../common'; - -import { PivotAggDict } from './pivot_aggs'; -import { PivotGroupByDict } from './pivot_group_by'; - -export type IndexName = string; -export type IndexPattern = string; +import { TransformId } from '../../../common/types/transform'; // Transform name must contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores; // It must also start and end with an alphanumeric character. @@ -23,41 +17,6 @@ export function isTransformIdValid(transformId: TransformId) { return /^[a-z0-9\-\_]+$/g.test(transformId) && !/^([_-].*)?(.*[_-])?$/g.test(transformId); } -export interface PreviewRequestBody { - pivot: { - group_by: PivotGroupByDict; - aggregations: PivotAggDict; - }; - source: { - index: IndexPattern | IndexPattern[]; - query?: any; - }; -} - -export interface CreateRequestBody extends PreviewRequestBody { - description?: string; - dest: { - index: IndexName; - }; - frequency?: string; - settings?: { - max_page_search_size?: number; - docs_per_second?: number; - }; - sync?: { - time: { - field: string; - delay: string; - }; - }; -} - -export interface TransformPivotConfig extends CreateRequestBody { - id: TransformId; - create_time?: number; - version?: string; -} - export enum REFRESH_TRANSFORM_LIST_STATE { ERROR = 'error', IDLE = 'idle', diff --git a/x-pack/plugins/transform/public/app/common/transform_list.ts b/x-pack/plugins/transform/public/app/common/transform_list.ts index a2a762a7e2df..b32803fea150 100644 --- a/x-pack/plugins/transform/public/app/common/transform_list.ts +++ b/x-pack/plugins/transform/public/app/common/transform_list.ts @@ -6,9 +6,8 @@ import { EuiTableActionsColumnType } from '@elastic/eui'; -import { TransformId } from '../../../common'; -import { TransformPivotConfig } from './transform'; -import { TransformStats } from './transform_stats'; +import { TransformId, TransformPivotConfig } from '../../../common/types/transform'; +import { TransformStats } from '../../../common/types/transform_stats'; // Used to pass on attribute names to table columns export enum TRANSFORM_LIST_COLUMN { diff --git a/x-pack/plugins/transform/public/app/common/transform_stats.ts b/x-pack/plugins/transform/public/app/common/transform_stats.ts index 72df6d3985e2..aaf7f97399d4 100644 --- a/x-pack/plugins/transform/public/app/common/transform_stats.ts +++ b/x-pack/plugins/transform/public/app/common/transform_stats.ts @@ -4,64 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TransformId, TRANSFORM_STATE } from '../../../common'; +import { TRANSFORM_STATE } from '../../../common/constants'; import { TransformListRow } from './transform_list'; -export enum TRANSFORM_MODE { - BATCH = 'batch', - CONTINUOUS = 'continuous', -} - -export interface TransformStats { - id: TransformId; - checkpointing: { - last: { - checkpoint: number; - timestamp_millis?: number; - }; - next?: { - checkpoint: number; - checkpoint_progress?: { - total_docs: number; - docs_remaining: number; - percent_complete: number; - }; - }; - operations_behind: number; - }; - node?: { - id: string; - name: string; - ephemeral_id: string; - transport_address: string; - attributes: Record; - }; - stats: { - documents_indexed: number; - documents_processed: number; - index_failures: number; - index_time_in_ms: number; - index_total: number; - pages_processed: number; - search_failures: number; - search_time_in_ms: number; - search_total: number; - trigger_count: number; - }; - reason?: string; - state: TRANSFORM_STATE; -} - -export function isTransformStats(arg: any): arg is TransformStats { - return ( - typeof arg === 'object' && - arg !== null && - {}.hasOwnProperty.call(arg, 'state') && - Object.values(TRANSFORM_STATE).includes(arg.state) - ); -} - export function getTransformProgress(item: TransformListRow) { if (isCompletedBatchTransform(item)) { return 100; diff --git a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts index a5cccd58211c..40a6ab2b6586 100644 --- a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts @@ -4,67 +4,162 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TransformId, TransformEndpointRequest } from '../../../../common'; +import { HttpFetchError } from 'kibana/public'; -import { PreviewRequestBody } from '../../common'; +import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; + +import { TransformId } from '../../../../common/types/transform'; +import type { FieldHistogramsResponseSchema } from '../../../../common/api_schemas/field_histograms'; +import type { GetTransformsAuditMessagesResponseSchema } from '../../../../common/api_schemas/audit_messages'; +import type { + DeleteTransformsRequestSchema, + DeleteTransformsResponseSchema, +} from '../../../../common/api_schemas/delete_transforms'; +import type { + StartTransformsRequestSchema, + StartTransformsResponseSchema, +} from '../../../../common/api_schemas/start_transforms'; +import type { + StopTransformsRequestSchema, + StopTransformsResponseSchema, +} from '../../../../common/api_schemas/stop_transforms'; +import type { + GetTransformsResponseSchema, + PostTransformsPreviewRequestSchema, + PostTransformsPreviewResponseSchema, + PutTransformsRequestSchema, + PutTransformsResponseSchema, +} from '../../../../common/api_schemas/transforms'; +import type { GetTransformsStatsResponseSchema } from '../../../../common/api_schemas/transforms_stats'; +import type { + PostTransformsUpdateRequestSchema, + PostTransformsUpdateResponseSchema, +} from '../../../../common/api_schemas/update_transforms'; + +import type { SearchResponse7 } from '../../../../common/shared_imports'; +import { EsIndex } from '../../../../common/types/es_index'; + +import type { SavedSearchQuery } from '../use_search_items'; + +// Default sampler shard size used for field histograms +export const DEFAULT_SAMPLER_SHARD_SIZE = 5000; + +export interface FieldHistogramRequestConfig { + fieldName: string; + type?: KBN_FIELD_TYPES; +} const apiFactory = () => ({ - getTransforms(transformId?: TransformId): Promise { - return new Promise((resolve, reject) => { - resolve([]); - }); + async getTransform( + transformId: TransformId + ): Promise { + return Promise.resolve({ count: 0, transforms: [] }); }, - getTransformsStats(transformId?: TransformId): Promise { - if (transformId !== undefined) { - return new Promise((resolve, reject) => { - resolve([]); - }); - } - - return new Promise((resolve, reject) => { - resolve([]); - }); + async getTransforms(): Promise { + return Promise.resolve({ count: 0, transforms: [] }); }, - createTransform(transformId: TransformId, transformConfig: any): Promise { - return new Promise((resolve, reject) => { - resolve([]); - }); + async getTransformStats( + transformId: TransformId + ): Promise { + return Promise.resolve({ count: 0, transforms: [] }); }, - deleteTransforms(transformsInfo: TransformEndpointRequest[]) { - return new Promise((resolve, reject) => { - resolve([]); - }); + async getTransformsStats(): Promise { + return Promise.resolve({ count: 0, transforms: [] }); }, - getTransformsPreview(obj: PreviewRequestBody): Promise { - return new Promise((resolve, reject) => { - resolve([]); - }); + async createTransform( + transformId: TransformId, + transformConfig: PutTransformsRequestSchema + ): Promise { + return Promise.resolve({ transformsCreated: [], errors: [] }); }, - startTransforms(transformsInfo: TransformEndpointRequest[]) { - return new Promise((resolve, reject) => { - resolve([]); + async updateTransform( + transformId: TransformId, + transformConfig: PostTransformsUpdateRequestSchema + ): Promise { + return Promise.resolve({ + id: 'the-test-id', + source: { index: ['the-index-name'], query: { match_all: {} } }, + dest: { index: 'user-the-destination-index-name' }, + frequency: '10m', + pivot: { + group_by: { the_group: { terms: { field: 'the-group-by-field' } } }, + aggregations: { the_agg: { value_count: { field: 'the-agg-field' } } }, + }, + description: 'the-description', + settings: { docs_per_second: null }, + version: '8.0.0', + create_time: 1598860879097, }); }, - stopTransforms(transformsInfo: TransformEndpointRequest[]) { - return new Promise((resolve, reject) => { - resolve([]); - }); + async deleteTransforms( + reqBody: DeleteTransformsRequestSchema + ): Promise { + return Promise.resolve({}); }, - getTransformAuditMessages(transformId: TransformId): Promise { - return new Promise((resolve, reject) => { - resolve([]); + async getTransformsPreview( + obj: PostTransformsPreviewRequestSchema + ): Promise { + return Promise.resolve({ + generated_dest_index: { + mappings: { + _meta: { + _transform: { + transform: 'the-transform', + version: { create: 'the-version' }, + creation_date_in_millis: 0, + }, + created_by: 'mock', + }, + properties: {}, + }, + settings: { index: { number_of_shards: '1', auto_expand_replicas: '0-1' } }, + aliases: {}, + }, + preview: [], }); }, - esSearch(payload: any) { - return new Promise((resolve, reject) => { - resolve([]); - }); + async startTransforms( + reqBody: StartTransformsRequestSchema + ): Promise { + return Promise.resolve({}); + }, + async stopTransforms( + transformsInfo: StopTransformsRequestSchema + ): Promise { + return Promise.resolve({}); }, - getIndices() { - return new Promise((resolve, reject) => { - resolve([]); + async getTransformAuditMessages( + transformId: TransformId + ): Promise { + return Promise.resolve([]); + }, + async esSearch(payload: any): Promise { + return Promise.resolve({ + hits: { + hits: [], + total: { + value: 0, + relation: 'the-relation', + }, + max_score: 0, + }, + timed_out: false, + took: 10, + _shards: { total: 1, successful: 1, failed: 0, skipped: 0 }, }); }, + + async getEsIndices(): Promise { + return Promise.resolve([]); + }, + async getHistogramsForFields( + indexPatternTitle: string, + fields: FieldHistogramRequestConfig[], + query: string | SavedSearchQuery, + samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE + ): Promise { + return Promise.resolve([]); + }, }); export const useApi = () => { diff --git a/x-pack/plugins/transform/public/app/hooks/use_api.ts b/x-pack/plugins/transform/public/app/hooks/use_api.ts index 1d2752b9e939..4cff5dd9b648 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_api.ts @@ -6,20 +6,43 @@ import { useMemo } from 'react'; +import { HttpFetchError } from 'kibana/public'; + import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; -import { - TransformId, - TransformEndpointRequest, - TransformEndpointResult, - DeleteTransformEndpointResult, -} from '../../../common'; +import type { GetTransformsAuditMessagesResponseSchema } from '../../../common/api_schemas/audit_messages'; +import type { + DeleteTransformsRequestSchema, + DeleteTransformsResponseSchema, +} from '../../../common/api_schemas/delete_transforms'; +import type { FieldHistogramsResponseSchema } from '../../../common/api_schemas/field_histograms'; +import type { + StartTransformsRequestSchema, + StartTransformsResponseSchema, +} from '../../../common/api_schemas/start_transforms'; +import type { + StopTransformsRequestSchema, + StopTransformsResponseSchema, +} from '../../../common/api_schemas/stop_transforms'; +import type { + GetTransformsResponseSchema, + PostTransformsPreviewRequestSchema, + PostTransformsPreviewResponseSchema, + PutTransformsRequestSchema, + PutTransformsResponseSchema, +} from '../../../common/api_schemas/transforms'; +import type { + PostTransformsUpdateRequestSchema, + PostTransformsUpdateResponseSchema, +} from '../../../common/api_schemas/update_transforms'; +import type { GetTransformsStatsResponseSchema } from '../../../common/api_schemas/transforms_stats'; +import { TransformId } from '../../../common/types/transform'; import { API_BASE_PATH } from '../../../common/constants'; +import { EsIndex } from '../../../common/types/es_index'; +import type { SearchResponse7 } from '../../../common/shared_imports'; import { useAppDependencies } from '../app_dependencies'; -import { GetTransformsResponse, PreviewRequestBody } from '../common'; -import { EsIndex } from './use_api_types'; import { SavedSearchQuery } from './use_search_items'; // Default sampler shard size used for field histograms @@ -35,81 +58,146 @@ export const useApi = () => { return useMemo( () => ({ - getTransforms(transformId?: TransformId): Promise { - const transformIdString = transformId !== undefined ? `/${transformId}` : ''; - return http.get(`${API_BASE_PATH}transforms${transformIdString}`); + async getTransform( + transformId: TransformId + ): Promise { + try { + return await http.get(`${API_BASE_PATH}transforms/${transformId}`); + } catch (e) { + return e; + } }, - getTransformsStats(transformId?: TransformId): Promise { - if (transformId !== undefined) { - return http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`); + async getTransforms(): Promise { + try { + return await http.get(`${API_BASE_PATH}transforms`); + } catch (e) { + return e; } - - return http.get(`${API_BASE_PATH}transforms/_stats`); }, - createTransform(transformId: TransformId, transformConfig: any): Promise { - return http.put(`${API_BASE_PATH}transforms/${transformId}`, { - body: JSON.stringify(transformConfig), - }); + async getTransformStats( + transformId: TransformId + ): Promise { + try { + return await http.get(`${API_BASE_PATH}transforms/${transformId}/_stats`); + } catch (e) { + return e; + } }, - updateTransform(transformId: TransformId, transformConfig: any): Promise { - return http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, { - body: JSON.stringify(transformConfig), - }); + async getTransformsStats(): Promise { + try { + return await http.get(`${API_BASE_PATH}transforms/_stats`); + } catch (e) { + return e; + } }, - deleteTransforms( - transformsInfo: TransformEndpointRequest[], - deleteDestIndex: boolean | undefined, - deleteDestIndexPattern: boolean | undefined, - forceDelete: boolean - ): Promise { - return http.post(`${API_BASE_PATH}delete_transforms`, { - body: JSON.stringify({ - transformsInfo, - deleteDestIndex, - deleteDestIndexPattern, - forceDelete, - }), - }); + async createTransform( + transformId: TransformId, + transformConfig: PutTransformsRequestSchema + ): Promise { + try { + return await http.put(`${API_BASE_PATH}transforms/${transformId}`, { + body: JSON.stringify(transformConfig), + }); + } catch (e) { + return e; + } }, - getTransformsPreview(obj: PreviewRequestBody): Promise { - return http.post(`${API_BASE_PATH}transforms/_preview`, { - body: JSON.stringify(obj), - }); + async updateTransform( + transformId: TransformId, + transformConfig: PostTransformsUpdateRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}transforms/${transformId}/_update`, { + body: JSON.stringify(transformConfig), + }); + } catch (e) { + return e; + } }, - startTransforms( - transformsInfo: TransformEndpointRequest[] - ): Promise { - return http.post(`${API_BASE_PATH}start_transforms`, { - body: JSON.stringify(transformsInfo), - }); + async deleteTransforms( + reqBody: DeleteTransformsRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}delete_transforms`, { + body: JSON.stringify(reqBody), + }); + } catch (e) { + return e; + } }, - stopTransforms(transformsInfo: TransformEndpointRequest[]): Promise { - return http.post(`${API_BASE_PATH}stop_transforms`, { - body: JSON.stringify(transformsInfo), - }); + async getTransformsPreview( + obj: PostTransformsPreviewRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}transforms/_preview`, { + body: JSON.stringify(obj), + }); + } catch (e) { + return e; + } }, - getTransformAuditMessages(transformId: TransformId): Promise { - return http.get(`${API_BASE_PATH}transforms/${transformId}/messages`); + async startTransforms( + reqBody: StartTransformsRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}start_transforms`, { + body: JSON.stringify(reqBody), + }); + } catch (e) { + return e; + } + }, + async stopTransforms( + transformsInfo: StopTransformsRequestSchema + ): Promise { + try { + return await http.post(`${API_BASE_PATH}stop_transforms`, { + body: JSON.stringify(transformsInfo), + }); + } catch (e) { + return e; + } + }, + async getTransformAuditMessages( + transformId: TransformId + ): Promise { + try { + return await http.get(`${API_BASE_PATH}transforms/${transformId}/messages`); + } catch (e) { + return e; + } }, - esSearch(payload: any): Promise { - return http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) }); + async esSearch(payload: any): Promise { + try { + return await http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) }); + } catch (e) { + return e; + } }, - getIndices(): Promise { - return http.get(`/api/index_management/indices`); + async getEsIndices(): Promise { + try { + return await http.get(`/api/index_management/indices`); + } catch (e) { + return e; + } }, - getHistogramsForFields( + async getHistogramsForFields( indexPatternTitle: string, fields: FieldHistogramRequestConfig[], query: string | SavedSearchQuery, samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE - ) { - return http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, { - body: JSON.stringify({ - query, - fields, - samplerShardSize, - }), - }); + ): Promise { + try { + return await http.post(`${API_BASE_PATH}field_histograms/${indexPatternTitle}`, { + body: JSON.stringify({ + query, + fields, + samplerShardSize, + }), + }); + } catch (e) { + return e; + } }, }), [http] diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index 43c5ae6fad1b..1a97ba7806fe 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -7,12 +7,13 @@ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; -import { - DeleteTransformEndpointResult, +import type { DeleteTransformStatus, - TransformEndpointRequest, -} from '../../../common'; -import { extractErrorMessage, getErrorMessage } from '../../shared_imports'; + DeleteTransformsRequestSchema, +} from '../../../common/api_schemas/delete_transforms'; +import { isDeleteTransformsResponseSchema } from '../../../common/api_schemas/type_guards'; +import { extractErrorMessage } from '../../shared_imports'; +import { getErrorMessage } from '../../../common/utils/errors'; import { useAppDependencies, useToastNotifications } from '../app_dependencies'; import { REFRESH_TRANSFORM_LIST_STATE, refreshTransformList$, TransformListRow } from '../common'; import { ToastNotificationText } from '../components'; @@ -108,173 +109,157 @@ export const useDeleteTransforms = () => { const toastNotifications = useToastNotifications(); const api = useApi(); - return async ( - transforms: TransformListRow[], - shouldDeleteDestIndex: boolean, - shouldDeleteDestIndexPattern: boolean, - shouldForceDelete = false - ) => { - const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({ - id: tf.config.id, - state: tf.stats.state, - })); + return async (reqBody: DeleteTransformsRequestSchema) => { + const results = await api.deleteTransforms(reqBody); - try { - const results: DeleteTransformEndpointResult = await api.deleteTransforms( - transformsInfo, - shouldDeleteDestIndex, - shouldDeleteDestIndexPattern, - shouldForceDelete - ); - const isBulk = Object.keys(results).length > 1; - const successCount: Record = { - transformDeleted: 0, - destIndexDeleted: 0, - destIndexPatternDeleted: 0, - }; - for (const transformId in results) { - // hasOwnProperty check to ensure only properties on object itself, and not its prototypes - if (results.hasOwnProperty(transformId)) { - const status = results[transformId]; - const destinationIndex = status.destinationIndex; + if (!isDeleteTransformsResponseSchema(results)) { + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', { + defaultMessage: 'An error occurred calling the API endpoint to delete transforms.', + }), + text: toMountPoint( + + ), + }); + return; + } - // if we are only deleting one transform, show the success toast messages - if (!isBulk && status.transformDeleted) { - if (status.transformDeleted?.success) { - toastNotifications.addSuccess( - i18n.translate('xpack.transform.transformList.deleteTransformSuccessMessage', { - defaultMessage: 'Request to delete transform {transformId} acknowledged.', - values: { transformId }, - }) - ); - } - if (status.destIndexDeleted?.success) { - toastNotifications.addSuccess( - i18n.translate( - 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexSuccessMessage', - { - defaultMessage: - 'Request to delete destination index {destinationIndex} acknowledged.', - values: { destinationIndex }, - } - ) - ); - } - if (status.destIndexPatternDeleted?.success) { - toastNotifications.addSuccess( - i18n.translate( - 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternSuccessMessage', - { - defaultMessage: - 'Request to delete index pattern {destinationIndex} acknowledged.', - values: { destinationIndex }, - } - ) - ); - } - } else { - (Object.keys(successCount) as SuccessCountField[]).forEach((key) => { - if (status[key]?.success) { - successCount[key] = successCount[key] + 1; - } - }); - } - if (status.transformDeleted?.error) { - const error = extractErrorMessage(status.transformDeleted.error); - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.transformList.deleteTransformErrorMessage', { - defaultMessage: 'An error occurred deleting the transform {transformId}', + const isBulk = Object.keys(results).length > 1; + const successCount: Record = { + transformDeleted: 0, + destIndexDeleted: 0, + destIndexPatternDeleted: 0, + }; + for (const transformId in results) { + // hasOwnProperty check to ensure only properties on object itself, and not its prototypes + if (results.hasOwnProperty(transformId)) { + const status = results[transformId]; + const destinationIndex = status.destinationIndex; + + // if we are only deleting one transform, show the success toast messages + if (!isBulk && status.transformDeleted) { + if (status.transformDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.deleteTransformSuccessMessage', { + defaultMessage: 'Request to delete transform {transformId} acknowledged.', values: { transformId }, - }), - text: toMountPoint( - - ), - }); + }) + ); } - - if (status.destIndexDeleted?.error) { - const error = extractErrorMessage(status.destIndexDeleted.error); - toastNotifications.addDanger({ - title: i18n.translate( - 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage', + if (status.destIndexDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate( + 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexSuccessMessage', { - defaultMessage: 'An error occurred deleting destination index {destinationIndex}', + defaultMessage: + 'Request to delete destination index {destinationIndex} acknowledged.', values: { destinationIndex }, } - ), - text: toMountPoint( - - ), - }); + ) + ); } - - if (status.destIndexPatternDeleted?.error) { - const error = extractErrorMessage(status.destIndexPatternDeleted.error); - toastNotifications.addDanger({ - title: i18n.translate( - 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternErrorMessage', + if (status.destIndexPatternDeleted?.success) { + toastNotifications.addSuccess( + i18n.translate( + 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternSuccessMessage', { - defaultMessage: 'An error occurred deleting index pattern {destinationIndex}', + defaultMessage: + 'Request to delete index pattern {destinationIndex} acknowledged.', values: { destinationIndex }, } - ), - text: toMountPoint( - - ), - }); + ) + ); } + } else { + (Object.keys(successCount) as SuccessCountField[]).forEach((key) => { + if (status[key]?.success) { + successCount[key] = successCount[key] + 1; + } + }); } - } - - // if we are deleting multiple transforms, combine the success messages - if (isBulk) { - if (successCount.transformDeleted > 0) { - toastNotifications.addSuccess( - i18n.translate('xpack.transform.transformList.bulkDeleteTransformSuccessMessage', { - defaultMessage: - 'Successfully deleted {count} {count, plural, one {transform} other {transforms}}.', - values: { count: successCount.transformDeleted }, - }) - ); + if (status.transformDeleted?.error) { + const error = extractErrorMessage(status.transformDeleted.error); + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.transformList.deleteTransformErrorMessage', { + defaultMessage: 'An error occurred deleting the transform {transformId}', + values: { transformId }, + }), + text: toMountPoint( + + ), + }); } - if (successCount.destIndexDeleted > 0) { - toastNotifications.addSuccess( - i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexSuccessMessage', { - defaultMessage: - 'Successfully deleted {count} destination {count, plural, one {index} other {indices}}.', - values: { count: successCount.destIndexDeleted }, - }) - ); + if (status.destIndexDeleted?.error) { + const error = extractErrorMessage(status.destIndexDeleted.error); + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage', + { + defaultMessage: 'An error occurred deleting destination index {destinationIndex}', + values: { destinationIndex }, + } + ), + text: toMountPoint( + + ), + }); } - if (successCount.destIndexPatternDeleted > 0) { - toastNotifications.addSuccess( - i18n.translate( - 'xpack.transform.transformList.bulkDeleteDestIndexPatternSuccessMessage', + + if (status.destIndexPatternDeleted?.error) { + const error = extractErrorMessage(status.destIndexPatternDeleted.error); + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.deleteTransform.deleteAnalyticsWithIndexPatternErrorMessage', { - defaultMessage: - 'Successfully deleted {count} destination index {count, plural, one {pattern} other {patterns}}.', - values: { count: successCount.destIndexPatternDeleted }, + defaultMessage: 'An error occurred deleting index pattern {destinationIndex}', + values: { destinationIndex }, } - ) - ); + ), + text: toMountPoint( + + ), + }); } } + } - refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH); - } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', { - defaultMessage: 'An error occurred calling the API endpoint to delete transforms.', - }), - text: toMountPoint( - - ), - }); + // if we are deleting multiple transforms, combine the success messages + if (isBulk) { + if (successCount.transformDeleted > 0) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.bulkDeleteTransformSuccessMessage', { + defaultMessage: + 'Successfully deleted {count} {count, plural, one {transform} other {transforms}}.', + values: { count: successCount.transformDeleted }, + }) + ); + } + + if (successCount.destIndexDeleted > 0) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexSuccessMessage', { + defaultMessage: + 'Successfully deleted {count} destination {count, plural, one {index} other {indices}}.', + values: { count: successCount.destIndexDeleted }, + }) + ); + } + if (successCount.destIndexPatternDeleted > 0) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.bulkDeleteDestIndexPatternSuccessMessage', { + defaultMessage: + 'Successfully deleted {count} destination index {count, plural, one {pattern} other {patterns}}.', + values: { count: successCount.destIndexPatternDeleted }, + }) + ); + } } + + refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH); }; }; diff --git a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts index bd19a7f8bf4d..5f3a9a6abfdb 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts @@ -4,52 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - TransformListRow, - TransformStats, - TRANSFORM_MODE, - isTransformStats, - TransformPivotConfig, - refreshTransformList$, - REFRESH_TRANSFORM_LIST_STATE, -} from '../common'; - -import { useApi } from './use_api'; +import { HttpFetchError } from 'src/core/public'; -interface GetTransformsResponse { - count: number; - transforms: TransformPivotConfig[]; -} - -interface GetTransformsStatsResponseOk { - node_failures?: object; - count: number; - transforms: TransformStats[]; -} - -const isGetTransformsStatsResponseOk = (arg: any): arg is GetTransformsStatsResponseOk => { - return ( - {}.hasOwnProperty.call(arg, 'count') && - {}.hasOwnProperty.call(arg, 'transforms') && - Array.isArray(arg.transforms) - ); -}; +import { + isGetTransformsResponseSchema, + isGetTransformsStatsResponseSchema, +} from '../../../common/api_schemas/type_guards'; +import { TRANSFORM_MODE } from '../../../common/constants'; +import { isTransformStats } from '../../../common/types/transform_stats'; -interface GetTransformsStatsResponseError { - statusCode: number; - error: string; - message: string; -} +import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; -type GetTransformsStatsResponse = GetTransformsStatsResponseOk | GetTransformsStatsResponseError; +import { useApi } from './use_api'; export type GetTransforms = (forceRefresh?: boolean) => void; export const useGetTransforms = ( setTransforms: React.Dispatch>, - setErrorMessage: React.Dispatch< - React.SetStateAction - >, + setErrorMessage: React.Dispatch>, setIsInitialized: React.Dispatch>, blockRefresh: boolean ): GetTransforms => { @@ -66,45 +38,57 @@ export const useGetTransforms = ( return; } - try { - const transformConfigs: GetTransformsResponse = await api.getTransforms(); - const transformStats: GetTransformsStatsResponse = await api.getTransformsStats(); - - const tableRows = transformConfigs.transforms.reduce((reducedtableRows, config) => { - const stats = isGetTransformsStatsResponseOk(transformStats) - ? transformStats.transforms.find((d) => config.id === d.id) - : undefined; - - // A newly created transform might not have corresponding stats yet. - // If that's the case we just skip the transform and don't add it to the transform list yet. - if (!isTransformStats(stats)) { - return reducedtableRows; - } - - // Table with expandable rows requires `id` on the outer most level - reducedtableRows.push({ - id: config.id, - config, - mode: - typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH, - stats, - }); - return reducedtableRows; - }, [] as TransformListRow[]); + const transformConfigs = await api.getTransforms(); + const transformStats = await api.getTransformsStats(); - setTransforms(tableRows); - setErrorMessage(undefined); - setIsInitialized(true); - refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE); - } catch (e) { + if ( + !isGetTransformsResponseSchema(transformConfigs) || + !isGetTransformsStatsResponseSchema(transformStats) + ) { // An error is followed immediately by setting the state to idle. // This way we're able to treat ERROR as a one-time-event like REFRESH. refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.ERROR); refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE); setTransforms([]); - setErrorMessage(e); + setIsInitialized(true); + + if (!isGetTransformsResponseSchema(transformConfigs)) { + setErrorMessage(transformConfigs); + } else if (!isGetTransformsStatsResponseSchema(transformStats)) { + setErrorMessage(transformStats); + } + + return; } + + const tableRows = transformConfigs.transforms.reduce((reducedtableRows, config) => { + const stats = isGetTransformsStatsResponseSchema(transformStats) + ? transformStats.transforms.find((d) => config.id === d.id) + : undefined; + + // A newly created transform might not have corresponding stats yet. + // If that's the case we just skip the transform and don't add it to the transform list yet. + if (!isTransformStats(stats)) { + return reducedtableRows; + } + + // Table with expandable rows requires `id` on the outer most level + reducedtableRows.push({ + id: config.id, + config, + mode: + typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH, + stats, + }); + return reducedtableRows; + }, [] as TransformListRow[]); + + setTransforms(tableRows); + setErrorMessage(undefined); + setIsInitialized(true); + refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.IDLE); + concurrentLoads--; if (concurrentLoads > 0) { diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index ad5850f26be2..ce233d0cf7ca 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -8,19 +8,23 @@ import { useEffect } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; +import { + isEsSearchResponse, + isFieldHistogramsResponseSchema, +} from '../../../common/api_schemas/type_guards'; + import { getFieldType, getDataGridSchemaFromKibanaFieldType, getFieldsFromKibanaIndexPattern, - getErrorMessage, showDataGridColumnChartErrorMessageToast, useDataGrid, useRenderCellValue, EsSorting, - SearchResponse7, UseIndexDataReturnType, INDEX_STATUS, } from '../../shared_imports'; +import { getErrorMessage } from '../../../common/utils/errors'; import { isDefaultQuery, matchAllQuery, PivotQuery } from '../common'; @@ -29,8 +33,6 @@ import { useApi } from './use_api'; import { useToastNotifications } from '../app_dependencies'; -type IndexSearchResponse = SearchResponse7; - export const useIndexData = ( indexPattern: SearchItems['indexPattern'], query: PivotQuery @@ -90,37 +92,39 @@ export const useIndexData = ( }, }; - try { - const resp: IndexSearchResponse = await api.esSearch(esSearchRequest); - - const docs = resp.hits.hits.map((d) => d._source); + const resp = await api.esSearch(esSearchRequest); - setRowCount(resp.hits.total.value); - setTableItems(docs); - setStatus(INDEX_STATUS.LOADED); - } catch (e) { - setErrorMessage(getErrorMessage(e)); + if (!isEsSearchResponse(resp)) { + setErrorMessage(getErrorMessage(resp)); setStatus(INDEX_STATUS.ERROR); return; } + + const docs = resp.hits.hits.map((d) => d._source); + + setRowCount(resp.hits.total.value); + setTableItems(docs); + setStatus(INDEX_STATUS.LOADED); }; const fetchColumnChartsData = async function () { - try { - const columnChartsData = await api.getHistogramsForFields( - indexPattern.title, - columns - .filter((cT) => dataGrid.visibleColumns.includes(cT.id)) - .map((cT) => ({ - fieldName: cT.id, - type: getFieldType(cT.schema), - })), - isDefaultQuery(query) ? matchAllQuery : query - ); - setColumnCharts(columnChartsData); - } catch (e) { - showDataGridColumnChartErrorMessageToast(e, toastNotifications); + const columnChartsData = await api.getHistogramsForFields( + indexPattern.title, + columns + .filter((cT) => dataGrid.visibleColumns.includes(cT.id)) + .map((cT) => ({ + fieldName: cT.id, + type: getFieldType(cT.schema), + })), + isDefaultQuery(query) ? matchAllQuery : query + ); + + if (!isFieldHistogramsResponseSchema(columnChartsData)) { + showDataGridColumnChartErrorMessageToast(columnChartsData, toastNotifications); + return; } + + setColumnCharts(columnChartsData); }; useEffect(() => { diff --git a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts index a9f34996b9b5..c51bf7d7e674 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts @@ -13,26 +13,27 @@ import { i18n } from '@kbn/i18n'; import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/common'; +import type { PreviewMappingsProperties } from '../../../common/api_schemas/transforms'; +import { isPostTransformsPreviewResponseSchema } from '../../../common/api_schemas/type_guards'; import { dictionaryToArray } from '../../../common/types/common'; -import { formatHumanReadableDateTimeSeconds } from '../../shared_imports'; import { getNestedProperty } from '../../../common/utils/object_utils'; import { - getErrorMessage, + formatHumanReadableDateTimeSeconds, multiColumnSortFactory, useDataGrid, RenderCellValue, UseIndexDataReturnType, INDEX_STATUS, } from '../../shared_imports'; +import { getErrorMessage } from '../../../common/utils/errors'; import { - getPreviewRequestBody, + getPreviewTransformRequestBody, PivotAggsConfigDict, PivotGroupByConfigDict, PivotGroupByConfig, PivotQuery, - PreviewMappings, PivotAggsConfig, } from '../common'; @@ -74,21 +75,23 @@ export const usePivotData = ( aggs: PivotAggsConfigDict, groupBy: PivotGroupByConfigDict ): UseIndexDataReturnType => { - const [previewMappings, setPreviewMappings] = useState({ properties: {} }); + const [previewMappingsProperties, setPreviewMappingsProperties] = useState< + PreviewMappingsProperties + >({}); const api = useApi(); const aggsArr = useMemo(() => dictionaryToArray(aggs), [aggs]); const groupByArr = useMemo(() => dictionaryToArray(groupBy), [groupBy]); // Filters mapping properties of type `object`, which get returned for nested field parents. - const columnKeys = Object.keys(previewMappings.properties).filter( - (key) => previewMappings.properties[key].type !== 'object' + const columnKeys = Object.keys(previewMappingsProperties).filter( + (key) => previewMappingsProperties[key].type !== 'object' ); columnKeys.sort(sortColumns(groupByArr)); // EuiDataGrid State const columns: EuiDataGridColumn[] = columnKeys.map((id) => { - const field = previewMappings.properties[id]; + const field = previewMappingsProperties[id]; // Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json'] // To fall back to the default string schema it needs to be undefined. @@ -159,28 +162,35 @@ export const usePivotData = ( setNoDataMessage(''); setStatus(INDEX_STATUS.LOADING); - try { - const previewRequest = getPreviewRequestBody(indexPatternTitle, query, groupByArr, aggsArr); - const resp = await api.getTransformsPreview(previewRequest); - setTableItems(resp.preview); - setRowCount(resp.preview.length); - setPreviewMappings(resp.generated_dest_index.mappings); - setStatus(INDEX_STATUS.LOADED); - - if (resp.preview.length === 0) { - setNoDataMessage( - i18n.translate('xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', { - defaultMessage: - 'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.', - }) - ); - } - } catch (e) { - setErrorMessage(getErrorMessage(e)); + const previewRequest = getPreviewTransformRequestBody( + indexPatternTitle, + query, + groupByArr, + aggsArr + ); + const resp = await api.getTransformsPreview(previewRequest); + + if (!isPostTransformsPreviewResponseSchema(resp)) { + setErrorMessage(getErrorMessage(resp)); setTableItems([]); setRowCount(0); - setPreviewMappings({ properties: {} }); + setPreviewMappingsProperties({}); setStatus(INDEX_STATUS.ERROR); + return; + } + + setTableItems(resp.preview); + setRowCount(resp.preview.length); + setPreviewMappingsProperties(resp.generated_dest_index.mappings.properties); + setStatus(INDEX_STATUS.LOADED); + + if (resp.preview.length === 0) { + setNoDataMessage( + i18n.translate('xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', { + defaultMessage: + 'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.', + }) + ); } }; @@ -236,19 +246,19 @@ export const usePivotData = ( if ( [ES_FIELD_TYPES.DATE, ES_FIELD_TYPES.DATE_NANOS].includes( - previewMappings.properties[columnId].type + previewMappingsProperties[columnId].type ) ) { return formatHumanReadableDateTimeSeconds(moment(cellValue).unix() * 1000); } - if (previewMappings.properties[columnId].type === ES_FIELD_TYPES.BOOLEAN) { + if (previewMappingsProperties[columnId].type === ES_FIELD_TYPES.BOOLEAN) { return cellValue ? 'true' : 'false'; } return cellValue; }; - }, [pageData, pagination.pageIndex, pagination.pageSize, previewMappings.properties]); + }, [pageData, pagination.pageIndex, pagination.pageSize, previewMappingsProperties]); return { ...dataGrid, diff --git a/x-pack/plugins/transform/public/app/hooks/use_start_transform.ts b/x-pack/plugins/transform/public/app/hooks/use_start_transform.tsx similarity index 52% rename from x-pack/plugins/transform/public/app/hooks/use_start_transform.ts rename to x-pack/plugins/transform/public/app/hooks/use_start_transform.tsx index a0ffe1fdfa33..71ed220b6b4d 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_start_transform.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_start_transform.tsx @@ -4,25 +4,45 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; + import { i18n } from '@kbn/i18n'; -import { TransformEndpointRequest, TransformEndpointResult } from '../../../common'; +import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; + +import type { StartTransformsRequestSchema } from '../../../common/api_schemas/start_transforms'; +import { isStartTransformsResponseSchema } from '../../../common/api_schemas/type_guards'; + +import { getErrorMessage } from '../../../common/utils/errors'; -import { useToastNotifications } from '../app_dependencies'; -import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; +import { useAppDependencies, useToastNotifications } from '../app_dependencies'; +import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; +import { ToastNotificationText } from '../components'; import { useApi } from './use_api'; export const useStartTransforms = () => { + const deps = useAppDependencies(); const toastNotifications = useToastNotifications(); const api = useApi(); - return async (transforms: TransformListRow[]) => { - const transformsInfo: TransformEndpointRequest[] = transforms.map((tf) => ({ - id: tf.config.id, - state: tf.stats.state, - })); - const results: TransformEndpointResult = await api.startTransforms(transformsInfo); + return async (transformsInfo: StartTransformsRequestSchema) => { + const results = await api.startTransforms(transformsInfo); + + if (!isStartTransformsResponseSchema(results)) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.stepCreateForm.startTransformResponseSchemaErrorMessage', + { + defaultMessage: 'An error occurred calling the start transforms request.', + } + ), + text: toMountPoint( + + ), + }); + return; + } for (const transformId in results) { // hasOwnProperty check to ensure only properties on object itself, and not its prototypes diff --git a/x-pack/plugins/transform/public/app/hooks/use_stop_transform.ts b/x-pack/plugins/transform/public/app/hooks/use_stop_transform.tsx similarity index 53% rename from x-pack/plugins/transform/public/app/hooks/use_stop_transform.ts rename to x-pack/plugins/transform/public/app/hooks/use_stop_transform.tsx index 0df983464770..be223c5eddfd 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_stop_transform.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_stop_transform.tsx @@ -4,25 +4,45 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; + import { i18n } from '@kbn/i18n'; -import { TransformEndpointRequest, TransformEndpointResult } from '../../../common'; +import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; + +import type { StopTransformsRequestSchema } from '../../../common/api_schemas/stop_transforms'; +import { isStopTransformsResponseSchema } from '../../../common/api_schemas/type_guards'; + +import { getErrorMessage } from '../../../common/utils/errors'; -import { useToastNotifications } from '../app_dependencies'; -import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; +import { useAppDependencies, useToastNotifications } from '../app_dependencies'; +import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common'; +import { ToastNotificationText } from '../components'; import { useApi } from './use_api'; export const useStopTransforms = () => { + const deps = useAppDependencies(); const toastNotifications = useToastNotifications(); const api = useApi(); - return async (transforms: TransformListRow[]) => { - const transformsInfo: TransformEndpointRequest[] = transforms.map((df) => ({ - id: df.config.id, - state: df.stats.state, - })); - const results: TransformEndpointResult = await api.stopTransforms(transformsInfo); + return async (transformsInfo: StopTransformsRequestSchema) => { + const results = await api.stopTransforms(transformsInfo); + + if (!isStopTransformsResponseSchema(results)) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.transformList.stopTransformResponseSchemaErrorMessage', + { + defaultMessage: 'An error occurred called the stop transforms request.', + } + ), + text: toMountPoint( + + ), + }); + return; + } for (const transformId in results) { // hasOwnProperty check to ensure only properties on object itself, and not its prototypes diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx index 6553d4474d39..790fcaf5fa83 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx @@ -6,7 +6,7 @@ import React, { createContext } from 'react'; -import { Privileges } from '../../../../../common'; +import { Privileges } from '../../../../../common/types/privileges'; import { useRequest } from '../../../hooks'; diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts index 282a737d0bf1..841c6ed01766 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; -import { Privileges } from '../../../../../common'; +import { Privileges } from '../../../../../common/types/privileges'; export interface Capabilities { canGetTransform: boolean; diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx index 89c6ac3a054f..beeacc76bdc9 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/with_privileges.tsx @@ -10,7 +10,7 @@ import { EuiPageContent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { MissingPrivileges } from '../../../../../common'; +import { MissingPrivileges } from '../../../../../common/types/privileges'; import { SectionLoading } from '../../../components'; diff --git a/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx b/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx index 19ba31d36e6e..9a97c66bfb10 100644 --- a/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx @@ -21,40 +21,20 @@ import { EuiTitle, } from '@elastic/eui'; +import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; +import { TransformPivotConfig } from '../../../../common/types/transform'; + +import { isHttpFetchError } from '../../common/request'; import { useApi } from '../../hooks/use_api'; import { useDocumentationLinks } from '../../hooks/use_documentation_links'; import { useSearchItems } from '../../hooks/use_search_items'; -import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; - import { useAppDependencies } from '../../app_dependencies'; -import { TransformPivotConfig } from '../../common'; import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation'; import { PrivilegesWrapper } from '../../lib/authorization'; import { Wizard } from '../create_transform/components/wizard'; -interface GetTransformsResponseOk { - count: number; - transforms: TransformPivotConfig[]; -} - -interface GetTransformsResponseError { - error: { - msg: string; - path: string; - query: any; - statusCode: number; - response: string; - }; -} - -function isGetTransformsResponseError(arg: any): arg is GetTransformsResponseError { - return arg.error !== undefined; -} - -type GetTransformsResponse = GetTransformsResponseOk | GetTransformsResponseError; - type Props = RouteComponentProps<{ transformId: string }>; export const CloneTransformSection: FC = ({ match }) => { // Set breadcrumb and page title @@ -84,15 +64,15 @@ export const CloneTransformSection: FC = ({ match }) => { } = useSearchItems(undefined); const fetchTransformConfig = async () => { - try { - const transformConfigs: GetTransformsResponse = await api.getTransforms(transformId); - if (isGetTransformsResponseError(transformConfigs)) { - setTransformConfig(undefined); - setErrorMessage(transformConfigs.error.msg); - setIsInitialized(true); - return; - } + const transformConfigs = await api.getTransform(transformId); + if (isHttpFetchError(transformConfigs)) { + setTransformConfig(undefined); + setErrorMessage(transformConfigs.message); + setIsInitialized(true); + return; + } + try { await loadIndexPatterns(savedObjectsClient, indexPatterns); const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index) ? transformConfigs.transforms[0].source.index.join(',') diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.test.tsx index fa0fe7bdf612..49d59706befb 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.test.tsx @@ -7,7 +7,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { AggName, PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common'; +import { AggName } from '../../../../../../common/types/aggregations'; +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + +import { PivotAggsConfig } from '../../../../common'; import { AggLabelForm } from './agg_label_form'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.tsx index e50ba9e13733..4e5e3f71cd6e 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/agg_label_form.tsx @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui'; +import { AggName } from '../../../../../../common/types/aggregations'; + import { - AggName, isPivotAggsConfigWithUiSupport, PivotAggsConfig, PivotAggsConfigWithUiSupportDict, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.test.tsx index 32c7ca5972e0..93de3d4fcfc9 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.test.tsx @@ -7,7 +7,9 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common'; +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + +import { PivotAggsConfig } from '../../../../common'; import { AggListForm, AggListProps } from './list_form'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx index a02f4455250d..f6ae1f292b0e 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx @@ -8,8 +8,9 @@ import React, { Fragment } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { AggName } from '../../../../../../common/types/aggregations'; + import { - AggName, PivotAggsConfig, PivotAggsConfigDict, PivotAggsConfigWithUiSupportDict, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.test.tsx index 923d52ba5cec..8c644c358e65 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.test.tsx @@ -7,7 +7,9 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { PivotAggsConfig, PIVOT_SUPPORTED_AGGS } from '../../../../common'; +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + +import { PivotAggsConfig } from '../../../../common'; import { AggListSummary, AggListSummaryProps } from './list_summary'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.tsx index 7d07d79e7d28..fb6e141a54b0 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_summary.tsx @@ -8,7 +8,9 @@ import React, { Fragment } from 'react'; import { EuiForm, EuiPanel, EuiSpacer } from '@elastic/eui'; -import { AggName, PivotAggsConfigDict } from '../../../../common'; +import { AggName } from '../../../../../../common/types/aggregations'; + +import { PivotAggsConfigDict } from '../../../../common'; export interface AggListSummaryProps { list: PivotAggsConfigDict; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.test.tsx index b3e770a26968..8f2fbfb7084e 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.test.tsx @@ -7,7 +7,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { AggName, PIVOT_SUPPORTED_AGGS, PivotAggsConfig } from '../../../../common'; +import { AggName } from '../../../../../../common/types/aggregations'; +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + +import { PivotAggsConfig } from '../../../../common'; import { PopoverForm } from './popover_form'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx index 50064274cf98..30e8c2b594db 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/aggregation_list/popover_form.tsx @@ -20,10 +20,14 @@ import { import { cloneDeep } from 'lodash'; import { useUpdateEffect } from 'react-use'; +import { AggName } from '../../../../../../common/types/aggregations'; import { dictionaryToArray } from '../../../../../../common/types/common'; +import { + PivotSupportedAggs, + PIVOT_SUPPORTED_AGGS, +} from '../../../../../../common/types/pivot_aggs'; import { - AggName, isAggName, isPivotAggsConfigPercentiles, isPivotAggsConfigWithUiSupport, @@ -31,9 +35,8 @@ import { PERCENTILES_AGG_DEFAULT_PERCENTS, PivotAggsConfig, PivotAggsConfigWithUiSupportDict, - PIVOT_SUPPORTED_AGGS, } from '../../../../common'; -import { isPivotAggsWithExtendedForm, PivotSupportedAggs } from '../../../../common/pivot_aggs'; +import { isPivotAggsWithExtendedForm } from '../../../../common/pivot_aggs'; import { getAggFormConfig } from '../step_define/common/get_agg_form_config'; interface Props { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/group_by_label_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/group_by_label_form.tsx index c79da06ac808..ff66ed6779e1 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/group_by_label_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/group_by_label_form.tsx @@ -10,8 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPopover, EuiTextColor } from '@elastic/eui'; +import { AggName } from '../../../../../../common/types/aggregations'; + import { - AggName, isGroupByDateHistogram, isGroupByHistogram, PivotGroupByConfig, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx index 2dc1a4332f6a..a60989c76ab1 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx @@ -8,8 +8,9 @@ import React, { Fragment } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { AggName } from '../../../../../../common/types/aggregations'; + import { - AggName, PivotGroupByConfig, PivotGroupByConfigDict, PivotGroupByConfigWithUiSupportDict, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.test.tsx index 090f3b19f47f..13829222f11f 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.test.tsx @@ -7,7 +7,9 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { AggName, PIVOT_SUPPORTED_GROUP_BY_AGGS, PivotGroupByConfig } from '../../../../common'; +import { AggName } from '../../../../../../common/types/aggregations'; + +import { PIVOT_SUPPORTED_GROUP_BY_AGGS, PivotGroupByConfig } from '../../../../common'; import { isIntervalValid, PopoverForm } from './popover_form'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.tsx index 0452638e90df..f0a96fa6ab87 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/group_by_list/popover_form.tsx @@ -18,10 +18,10 @@ import { EuiSpacer, } from '@elastic/eui'; +import { AggName } from '../../../../../../common/types/aggregations'; import { dictionaryToArray } from '../../../../../../common/types/common'; import { - AggName, dateHistogramIntervalFormatRegex, getEsAggFromGroupByConfig, isGroupByDateHistogram, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index 255a245081d5..675bd0f9f88e 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -30,9 +30,15 @@ import { import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; +import type { PutTransformsResponseSchema } from '../../../../../../common/api_schemas/transforms'; +import { + isGetTransformsStatsResponseSchema, + isPutTransformsResponseSchema, + isStartTransformsResponseSchema, +} from '../../../../../../common/api_schemas/type_guards'; import { PROGRESS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants'; -import { getErrorMessage } from '../../../../../shared_imports'; +import { getErrorMessage } from '../../../../../../common/utils/errors'; import { getTransformProgress, getDiscoverUrl } from '../../../../common'; import { useApi } from '../../../../hooks/use_api'; @@ -93,34 +99,28 @@ export const StepCreateForm: FC = React.memo( async function createTransform() { setLoading(true); - try { - const resp = await api.createTransform(transformId, transformConfig); - if (resp.errors !== undefined && Array.isArray(resp.errors)) { - if (resp.errors.length === 1) { - throw resp.errors[0]; - } + const resp = await api.createTransform(transformId, transformConfig); - if (resp.errors.length > 1) { - throw resp.errors; - } + if (!isPutTransformsResponseSchema(resp) || resp.errors.length > 0) { + let respErrors: + | PutTransformsResponseSchema['errors'] + | PutTransformsResponseSchema['errors'][number] + | undefined; + + if (isPutTransformsResponseSchema(resp) && resp.errors.length > 0) { + respErrors = resp.errors.length === 1 ? resp.errors[0] : resp.errors; } - toastNotifications.addSuccess( - i18n.translate('xpack.transform.stepCreateForm.createTransformSuccessMessage', { - defaultMessage: 'Request to create transform {transformId} acknowledged.', - values: { transformId }, - }) - ); - setCreated(true); - setLoading(false); - } catch (e) { toastNotifications.addDanger({ title: i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', { defaultMessage: 'An error occurred creating the transform {transformId}:', values: { transformId }, }), text: toMountPoint( - + ), }); setCreated(false); @@ -128,6 +128,15 @@ export const StepCreateForm: FC = React.memo( return false; } + toastNotifications.addSuccess( + i18n.translate('xpack.transform.stepCreateForm.createTransformSuccessMessage', { + defaultMessage: 'Request to create transform {transformId} acknowledged.', + values: { transformId }, + }) + ); + setCreated(true); + setLoading(false); + if (createIndexPattern) { createKibanaIndexPattern(); } @@ -138,37 +147,36 @@ export const StepCreateForm: FC = React.memo( async function startTransform() { setLoading(true); - try { - const resp = await api.startTransforms([{ id: transformId }]); - if (typeof resp === 'object' && resp !== null && resp[transformId]?.success === true) { - toastNotifications.addSuccess( - i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', { - defaultMessage: 'Request to start transform {transformId} acknowledged.', - values: { transformId }, - }) - ); - setStarted(true); - setLoading(false); - } else { - const errorMessage = - typeof resp === 'object' && resp !== null && resp[transformId]?.success === false - ? resp[transformId].error - : resp; - throw new Error(errorMessage); - } - } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', { - defaultMessage: 'An error occurred starting the transform {transformId}:', + const resp = await api.startTransforms([{ id: transformId }]); + + if (isStartTransformsResponseSchema(resp) && resp[transformId]?.success === true) { + toastNotifications.addSuccess( + i18n.translate('xpack.transform.stepCreateForm.startTransformSuccessMessage', { + defaultMessage: 'Request to start transform {transformId} acknowledged.', values: { transformId }, - }), - text: toMountPoint( - - ), - }); - setStarted(false); + }) + ); + setStarted(true); setLoading(false); + return; } + + const errorMessage = + isStartTransformsResponseSchema(resp) && resp[transformId]?.success === false + ? resp[transformId].error + : resp; + + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', { + defaultMessage: 'An error occurred starting the transform {transformId}:', + values: { transformId }, + }), + text: toMountPoint( + + ), + }); + setStarted(false); + setLoading(false); } async function createAndStartTransform() { @@ -250,27 +258,30 @@ export const StepCreateForm: FC = React.memo( // wrapping in function so we can keep the interval id in local scope function startProgressBar() { const interval = setInterval(async () => { - try { - const stats = await api.getTransformsStats(transformId); - if (stats && Array.isArray(stats.transforms) && stats.transforms.length > 0) { - const percent = - getTransformProgress({ - id: transformConfig.id, - config: transformConfig, - stats: stats.transforms[0], - }) || 0; - setProgressPercentComplete(percent); - if (percent >= 100) { - clearInterval(interval); - } + const stats = await api.getTransformStats(transformId); + + if ( + isGetTransformsStatsResponseSchema(stats) && + Array.isArray(stats.transforms) && + stats.transforms.length > 0 + ) { + const percent = + getTransformProgress({ + id: transformConfig.id, + config: transformConfig, + stats: stats.transforms[0], + }) || 0; + setProgressPercentComplete(percent); + if (percent >= 100) { + clearInterval(interval); } - } catch (e) { + } else { toastNotifications.addDanger({ title: i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', { defaultMessage: 'An error occurred getting the progress percentage:', }), text: toMountPoint( - + ), }); clearInterval(interval); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/apply_transform_config_to_define_state.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/apply_transform_config_to_define_state.ts index fba703b1540f..1523a0d9a89f 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/apply_transform_config_to_define_state.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/apply_transform_config_to_define_state.ts @@ -6,19 +6,21 @@ import { isEqual } from 'lodash'; +import { Dictionary } from '../../../../../../../common/types/common'; +import { PivotSupportedAggs } from '../../../../../../../common/types/pivot_aggs'; +import { TransformPivotConfig } from '../../../../../../../common/types/transform'; + import { matchAllQuery, PivotAggsConfig, PivotAggsConfigDict, PivotGroupByConfig, PivotGroupByConfigDict, - TransformPivotConfig, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../../common'; -import { Dictionary } from '../../../../../../../common/types/common'; import { StepDefineExposedState } from './types'; -import { getAggConfigFromEsAgg, PivotSupportedAggs } from '../../../../../common/pivot_aggs'; +import { getAggConfigFromEsAgg } from '../../../../../common/pivot_aggs'; export function applyTransformConfigToDefineState( state: StepDefineExposedState, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx index 9d3ab44aa570..d59f99192621 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx @@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { debounce } from 'lodash'; import { useUpdateEffect } from 'react-use'; import { i18n } from '@kbn/i18n'; +import { isEsSearchResponse } from '../../../../../../../../../common/api_schemas/type_guards'; import { useApi } from '../../../../../../../hooks'; import { CreateTransformWizardContext } from '../../../../wizard/wizard'; import { FilterAggConfigTerm } from '../types'; @@ -55,22 +56,24 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm }, }; - try { - const response = await api.esSearch(esSearchRequest); - setOptions( - response.aggregations.field_values.buckets.map( - (value: { key: string; doc_count: number }) => ({ label: value.key }) - ) - ); - } catch (e) { + const response = await api.esSearch(esSearchRequest); + + setIsLoading(false); + + if (!isEsSearchResponse(response)) { toastNotifications.addWarning( i18n.translate('xpack.transform.agg.popoverForm.filerAgg.term.errorFetchSuggestions', { defaultMessage: 'Unable to fetch suggestions', }) ); + return; } - setIsLoading(false); + setOptions( + response.aggregations.field_values.buckets.map( + (value: { key: string; doc_count: number }) => ({ label: value.key }) + ) + ); }, 600), [selectedField] ); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_form_config.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_form_config.ts index 2839c1181c33..5575e6d814da 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_form_config.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_form_config.ts @@ -5,11 +5,11 @@ */ import { - PIVOT_SUPPORTED_AGGS, - PivotAggsConfigBase, - PivotAggsConfigWithUiBase, PivotSupportedAggs, -} from '../../../../../common/pivot_aggs'; + PIVOT_SUPPORTED_AGGS, +} from '../../../../../../../common/types/pivot_aggs'; + +import { PivotAggsConfigBase, PivotAggsConfigWithUiBase } from '../../../../../common/pivot_aggs'; import { getFilterAggConfig } from './filter_agg/config'; /** diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_name_conflict_toast_messages.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_name_conflict_toast_messages.ts index 57f9397089f1..03cbf2e35873 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_name_conflict_toast_messages.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_agg_name_conflict_toast_messages.ts @@ -6,7 +6,8 @@ import { i18n } from '@kbn/i18n'; -import { AggName, PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common'; +import { AggName } from '../../../../../../../common/types/aggregations'; +import { PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common'; export function getAggNameConflictToastMessages( aggName: AggName, @@ -36,7 +37,7 @@ export function getAggNameConflictToastMessages( // check the new aggName against existing aggs and groupbys const aggNameSplit = aggName.split('.'); let aggNameCheck: string; - aggNameSplit.forEach((aggNamePart) => { + aggNameSplit.forEach((aggNamePart: string) => { aggNameCheck = aggNameCheck === undefined ? aggNamePart : `${aggNameCheck}.${aggNamePart}`; if (aggList[aggNameCheck] !== undefined || groupByList[aggNameCheck] !== undefined) { conflicts.push( diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_aggregation_config.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_aggregation_config.ts index 460164c9afe7..14c03aebe892 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_aggregation_config.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_aggregation_config.ts @@ -4,13 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EsFieldName } from '../../../../../../../common/types/fields'; import { - EsFieldName, - PERCENTILES_AGG_DEFAULT_PERCENTS, + PivotSupportedAggs, PIVOT_SUPPORTED_AGGS, +} from '../../../../../../../common/types/pivot_aggs'; +import { + PERCENTILES_AGG_DEFAULT_PERCENTS, PivotAggsConfigWithUiSupport, } from '../../../../../common'; -import { PivotSupportedAggs } from '../../../../../common/pivot_aggs'; import { getFilterAggConfig } from './filter_agg/config'; /** diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_group_by_config.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_group_by_config.ts index 712a745ff6e7..657e8c935b87 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_group_by_config.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_default_group_by_config.ts @@ -4,11 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - EsFieldName, - GroupByConfigWithUiSupport, - PIVOT_SUPPORTED_GROUP_BY_AGGS, -} from '../../../../../common'; +import { EsFieldName } from '../../../../../../../common/types/fields'; + +import { GroupByConfigWithUiSupport, PIVOT_SUPPORTED_GROUP_BY_AGGS } from '../../../../../common'; export function getDefaultGroupByConfig( aggName: string, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts index 56fde98cd4c7..955982aae600 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts @@ -6,7 +6,9 @@ import { KBN_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; -import { EsFieldName, PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common'; +import { EsFieldName } from '../../../../../../../common/types/fields'; + +import { PivotAggsConfigDict, PivotGroupByConfigDict } from '../../../../../common'; import { SavedSearchQuery } from '../../../../../hooks/use_search_items'; import { QUERY_LANGUAGE } from './constants'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_pivot_editor.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_pivot_editor.ts index 2e9211428659..41b84f04db85 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_pivot_editor.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_pivot_editor.ts @@ -8,13 +8,13 @@ import { useEffect, useState } from 'react'; import { useXJsonMode } from '../../../../../../../../../../src/plugins/es_ui_shared/static/ace_x_json/hooks'; -import { PreviewRequestBody } from '../../../../../common'; +import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms'; import { StepDefineExposedState } from '../common'; export const useAdvancedPivotEditor = ( defaults: StepDefineExposedState, - previewRequest: PreviewRequestBody + previewRequest: PostTransformsPreviewRequestSchema ) => { const stringifiedPivotConfig = JSON.stringify(previewRequest.pivot, null, 2); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_source_editor.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_source_editor.ts index 1ea8a45248fb..3f930711b970 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_source_editor.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_advanced_source_editor.ts @@ -6,13 +6,13 @@ import { useState } from 'react'; -import { PreviewRequestBody } from '../../../../../common'; +import { PostTransformsPreviewRequestSchema } from '../../../../../../../common/api_schemas/transforms'; import { StepDefineExposedState } from '../common'; export const useAdvancedSourceEditor = ( defaults: StepDefineExposedState, - previewRequest: PreviewRequestBody + previewRequest: PostTransformsPreviewRequestSchema ) => { const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_pivot_config.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_pivot_config.ts index d35d567fc846..90b28f0e305a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_pivot_config.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_pivot_config.ts @@ -6,11 +6,11 @@ import { useCallback, useMemo, useState } from 'react'; +import { AggName } from '../../../../../../../common/types/aggregations'; import { dictionaryToArray } from '../../../../../../../common/types/common'; import { useToastNotifications } from '../../../../../app_dependencies'; import { - AggName, DropDownLabel, PivotAggsConfig, PivotAggsConfigDict, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_step_define_form.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_step_define_form.ts index f5980ae2243d..7c10201fc3a6 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_step_define_form.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_step_define_form.ts @@ -6,7 +6,7 @@ import { useEffect } from 'react'; -import { getPreviewRequestBody } from '../../../../../common'; +import { getPreviewTransformRequestBody } from '../../../../../common'; import { getDefaultStepDefineState } from '../common'; @@ -26,7 +26,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi const searchBar = useSearchBar(defaults, indexPattern); const pivotConfig = usePivotConfig(defaults, indexPattern); - const previewRequest = getPreviewRequestBody( + const previewRequest = getPreviewTransformRequestBody( indexPattern.title, searchBar.state.pivotQuery, pivotConfig.state.pivotGroupByArr, @@ -41,7 +41,7 @@ export const useStepDefineForm = ({ overrides, onChange, searchItems }: StepDefi useEffect(() => { if (!advancedSourceEditor.state.isAdvancedSourceEditorEnabled) { - const previewRequestUpdate = getPreviewRequestBody( + const previewRequestUpdate = getPreviewTransformRequestBody( indexPattern.title, searchBar.state.pivotQuery, pivotConfig.state.pivotGroupByArr, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx index 8c919a5185d7..986ac0a212e8 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx @@ -15,10 +15,11 @@ import { coreMock } from '../../../../../../../../../src/core/public/mocks'; import { dataPluginMock } from '../../../../../../../../../src/plugins/data/public/mocks'; const startMock = coreMock.createStart(); +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + import { PivotAggsConfigDict, PivotGroupByConfigDict, - PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; import { SearchItems } from '../../../../hooks/use_search_items'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index e0b350542a8f..10f473074b4d 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -22,6 +22,9 @@ import { EuiText, } from '@elastic/eui'; +import { PivotAggDict } from '../../../../../../common/types/pivot_aggs'; +import { PivotGroupByDict } from '../../../../../../common/types/pivot_group_by'; + import { DataGrid } from '../../../../../shared_imports'; import { @@ -30,10 +33,8 @@ import { } from '../../../../common/data_grid'; import { - getPreviewRequestBody, - PivotAggDict, + getPreviewTransformRequestBody, PivotAggsConfigDict, - PivotGroupByDict, PivotGroupByConfigDict, PivotSupportedGroupByAggs, PivotAggsConfig, @@ -87,7 +88,7 @@ export const StepDefineForm: FC = React.memo((props) => { toastNotifications, }; - const previewRequest = getPreviewRequestBody( + const previewRequest = getPreviewTransformRequestBody( indexPattern.title, pivotQuery, pivotGroupByArr, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx index dc3d950938c9..f8a060e0007b 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx @@ -7,10 +7,11 @@ import React from 'react'; import { render, wait } from '@testing-library/react'; +import { PIVOT_SUPPORTED_AGGS } from '../../../../../../common/types/pivot_aggs'; + import { PivotAggsConfig, PivotGroupByConfig, - PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; import { SearchItems } from '../../../../hooks/use_search_items'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx index 414f6e37504d..fa4f8a7e0969 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx @@ -18,7 +18,7 @@ import { useToastNotifications } from '../../../../app_dependencies'; import { getPivotQuery, getPivotPreviewDevConsoleStatement, - getPreviewRequestBody, + getPreviewTransformRequestBody, isDefaultQuery, isMatchAllQuery, } from '../../../../common'; @@ -44,7 +44,7 @@ export const StepDefineSummary: FC = ({ const pivotGroupByArr = dictionaryToArray(groupByList); const pivotQuery = getPivotQuery(searchQuery); - const previewRequest = getPreviewRequestBody( + const previewRequest = getPreviewTransformRequestBody( searchItems.indexPattern.title, pivotQuery, pivotGroupByArr, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index 271fde27f519..43d4f11cffc9 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -11,24 +11,28 @@ import { i18n } from '@kbn/i18n'; import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from '@elastic/eui'; import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common'; - import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; -import { TransformId } from '../../../../../../common'; + +import { + isEsIndices, + isPostTransformsPreviewResponseSchema, +} from '../../../../../../common/api_schemas/type_guards'; +import { TransformId, TransformPivotConfig } from '../../../../../../common/types/transform'; import { isValidIndexName } from '../../../../../../common/utils/es_utils'; -import { getErrorMessage } from '../../../../../shared_imports'; +import { getErrorMessage } from '../../../../../../common/utils/errors'; import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies'; import { ToastNotificationText } from '../../../../components'; +import { isHttpFetchError } from '../../../../common/request'; import { useDocumentationLinks } from '../../../../hooks/use_documentation_links'; import { SearchItems } from '../../../../hooks/use_search_items'; import { useApi } from '../../../../hooks/use_api'; import { StepDetailsTimeField } from './step_details_time_field'; import { getPivotQuery, - getPreviewRequestBody, + getPreviewTransformRequestBody, isTransformIdValid, - TransformPivotConfig, } from '../../../../common'; import { EsIndexName, IndexPatternTitle } from './common'; import { delayValidator } from '../../../../common/validators'; @@ -48,10 +52,12 @@ export interface StepDetailsExposedState { indexPatternDateField?: string | undefined; } +const defaultContinuousModeDelay = '60s'; + export function getDefaultStepDetailsState(): StepDetailsExposedState { return { continuousModeDateField: '', - continuousModeDelay: '60s', + continuousModeDelay: defaultContinuousModeDelay, createIndexPattern: true, isContinuousModeEnabled: false, transformId: '', @@ -72,7 +78,7 @@ export function applyTransformConfigToDetailsState( const time = transformConfig.sync?.time; if (time !== undefined) { state.continuousModeDateField = time.field; - state.continuousModeDelay = time.delay; + state.continuousModeDelay = time?.delay ?? defaultContinuousModeDelay; state.isContinuousModeEnabled = true; } } @@ -137,19 +143,20 @@ export const StepDetailsForm: FC = React.memo( useEffect(() => { // use an IIFE to avoid returning a Promise to useEffect. (async function () { - try { - const { searchQuery, groupByList, aggList } = stepDefineState; - const pivotAggsArr = dictionaryToArray(aggList); - const pivotGroupByArr = dictionaryToArray(groupByList); - const pivotQuery = getPivotQuery(searchQuery); - const previewRequest = getPreviewRequestBody( - searchItems.indexPattern.title, - pivotQuery, - pivotGroupByArr, - pivotAggsArr - ); - - const transformPreview = await api.getTransformsPreview(previewRequest); + const { searchQuery, groupByList, aggList } = stepDefineState; + const pivotAggsArr = dictionaryToArray(aggList); + const pivotGroupByArr = dictionaryToArray(groupByList); + const pivotQuery = getPivotQuery(searchQuery); + const previewRequest = getPreviewTransformRequestBody( + searchItems.indexPattern.title, + pivotQuery, + pivotGroupByArr, + pivotAggsArr + ); + + const transformPreview = await api.getTransformsPreview(previewRequest); + + if (isPostTransformsPreviewResponseSchema(transformPreview)) { const properties = transformPreview.generated_dest_index.mappings.properties; const datetimeColumns: string[] = Object.keys(properties).filter( (col) => properties[col].type === 'date' @@ -157,43 +164,46 @@ export const StepDetailsForm: FC = React.memo( setPreviewDateColumns(datetimeColumns); setIndexPatternDateField(datetimeColumns[0]); - } catch (e) { + } else { toastNotifications.addDanger({ title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', { - defaultMessage: 'An error occurred getting transform preview', + defaultMessage: 'An error occurred fetching the transform preview', }), text: toMountPoint( - + ), }); } - try { - setTransformIds( - (await api.getTransforms()).transforms.map( - (transform: TransformPivotConfig) => transform.id - ) - ); - } catch (e) { + const resp = await api.getTransforms(); + + if (isHttpFetchError(resp)) { toastNotifications.addDanger({ title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', { defaultMessage: 'An error occurred getting the existing transform IDs:', }), text: toMountPoint( - + ), }); + } else { + setTransformIds(resp.transforms.map((transform: TransformPivotConfig) => transform.id)); } - try { - setIndexNames((await api.getIndices()).map((index) => index.name)); - } catch (e) { + const indices = await api.getEsIndices(); + + if (isEsIndices(indices)) { + setIndexNames(indices.map((index) => index.name)); + } else { toastNotifications.addDanger({ title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', { defaultMessage: 'An error occurred getting the existing index names:', }), text: toMountPoint( - + ), }); } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx index 806dcbfa7560..0ca018972cac 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx @@ -10,7 +10,9 @@ import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus } from '@elastic/eui'; -import { getCreateRequestBody, TransformPivotConfig } from '../../../../common'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; + +import { getCreateTransformRequestBody } from '../../../../common'; import { SearchItems } from '../../../../hooks/use_search_items'; import { @@ -149,7 +151,7 @@ export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) } }, []); - const transformConfig = getCreateRequestBody( + const transformConfig = getCreateTransformRequestBody( indexPattern.title, stepDefineState, stepDetailsState diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_name.tsx index d8ab72f15c59..75868fb8fcab 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_name.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/delete_action_name.tsx @@ -7,7 +7,7 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiToolTip } from '@elastic/eui'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TransformState, TRANSFORM_STATE } from '../../../../../../common/constants'; import { createCapabilityFailureMessage } from '../../../../lib/authorization'; import { TransformListRow } from '../../../../common'; @@ -18,8 +18,8 @@ export const deleteActionNameText = i18n.translate( } ); -const transformCanNotBeDeleted = (item: TransformListRow) => - ![TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED].includes(item.stats.state); +const transformCanNotBeDeleted = (i: TransformListRow) => + !([TRANSFORM_STATE.STOPPED, TRANSFORM_STATE.FAILED] as TransformState[]).includes(i.stats.state); export const isDeleteActionDisabled = (items: TransformListRow[], forceDisable: boolean) => { const disabled = items.some(transformCanNotBeDeleted); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx index e573709fa6e6..7e8e099b69f8 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_delete/use_delete_action.tsx @@ -6,7 +6,7 @@ import React, { useContext, useMemo, useState } from 'react'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import { TransformListAction, TransformListRow } from '../../../../common'; import { useDeleteIndexAndTargetIndex, useDeleteTransforms } from '../../../../hooks'; @@ -55,7 +55,16 @@ export const useDeleteAction = (forceDisable: boolean) => { const forceDelete = isBulkAction ? shouldForceDelete : items[0] && items[0] && items[0].stats.state === TRANSFORM_STATE.FAILED; - deleteTransforms(items, shouldDeleteDestIndex, shouldDeleteDestIndexPattern, forceDelete); + + deleteTransforms({ + transformsInfo: items.map((i) => ({ + id: i.config.id, + state: i.stats.state, + })), + deleteDestIndex: shouldDeleteDestIndex, + deleteDestIndexPattern: shouldDeleteDestIndexPattern, + forceDelete, + }); }; const openModal = (newItems: TransformListRow[]) => { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx index 1fe20f1acae5..192ff7ac74c5 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_edit/use_edit_action.tsx @@ -6,7 +6,9 @@ import React, { useContext, useMemo, useState } from 'react'; -import { TransformListAction, TransformListRow, TransformPivotConfig } from '../../../../common'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; + +import { TransformListAction, TransformListRow } from '../../../../common'; import { AuthorizationContext } from '../../../../lib/authorization'; import { editActionNameText, EditActionName } from './edit_action_name'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx index 191df0c16cba..ca1c90b9b8fa 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/start_action_name.tsx @@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiToolTip } from '@elastic/eui'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import { createCapabilityFailureMessage, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx index 8d6a4376c55b..96af60778d6a 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_start/use_start_action.tsx @@ -6,7 +6,7 @@ import React, { useContext, useMemo, useState } from 'react'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import { AuthorizationContext } from '../../../../lib/authorization'; import { TransformListAction, TransformListRow } from '../../../../common'; @@ -27,7 +27,7 @@ export const useStartAction = (forceDisable: boolean) => { const startAndCloseModal = () => { setModalVisible(false); - startTransforms(items); + startTransforms(items.map((i) => ({ id: i.id }))); }; const openModal = (newItems: TransformListRow[]) => { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.tsx index e1ea82cb371e..4ec30faa4d76 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/stop_action_name.tsx @@ -8,7 +8,7 @@ import React, { FC, useContext } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiToolTip } from '@elastic/eui'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import { TransformListRow } from '../../../../common'; import { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/use_stop_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/use_stop_action.tsx index e0a7e0b489ab..4c872114a82a 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/use_stop_action.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_stop/use_stop_action.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useContext, useMemo } from 'react'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import { AuthorizationContext } from '../../../../lib/authorization'; import { TransformListAction, TransformListRow } from '../../../../common'; @@ -20,9 +20,10 @@ export const useStopAction = (forceDisable: boolean) => { const stopTransforms = useStopTransforms(); - const clickHandler = useCallback((item: TransformListRow) => stopTransforms([item]), [ - stopTransforms, - ]); + const clickHandler = useCallback( + (i: TransformListRow) => stopTransforms([{ id: i.id, state: i.stats.state }]), + [stopTransforms] + ); const action: TransformListAction = useMemo( () => ({ diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/edit_transform_flyout.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/edit_transform_flyout.tsx index 77a7ae25ce88..f9cdac51b658 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/edit_transform_flyout.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/edit_transform_flyout.tsx @@ -23,13 +23,12 @@ import { EuiTitle, } from '@elastic/eui'; -import { getErrorMessage } from '../../../../../shared_imports'; +import { isPostTransformsUpdateResponseSchema } from '../../../../../../common/api_schemas/type_guards'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; -import { - refreshTransformList$, - TransformPivotConfig, - REFRESH_TRANSFORM_LIST_STATE, -} from '../../../../common'; +import { getErrorMessage } from '../../../../../../common/utils/errors'; + +import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../../../../common'; import { useToastNotifications } from '../../../../app_dependencies'; import { useApi } from '../../../../hooks/use_api'; @@ -58,19 +57,21 @@ export const EditTransformFlyout: FC = ({ closeFlyout, const requestConfig = applyFormFieldsToTransformConfig(config, state.formFields); const transformId = config.id; - try { - await api.updateTransform(transformId, requestConfig); - toastNotifications.addSuccess( - i18n.translate('xpack.transform.transformList.editTransformSuccessMessage', { - defaultMessage: 'Transform {transformId} updated.', - values: { transformId }, - }) - ); - closeFlyout(); - refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH); - } catch (e) { - setErrorMessage(getErrorMessage(e)); + const resp = await api.updateTransform(transformId, requestConfig); + + if (!isPostTransformsUpdateResponseSchema(resp)) { + setErrorMessage(getErrorMessage(resp)); + return; } + + toastNotifications.addSuccess( + i18n.translate('xpack.transform.transformList.editTransformSuccessMessage', { + defaultMessage: 'Transform {transformId} updated.', + values: { transformId }, + }) + ); + closeFlyout(); + refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH); } const isUpdateButtonDisabled = !state.isFormValid || !state.isFormTouched; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.test.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.test.ts index 4a8b26b601ae..12e60c2af555 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.test.ts +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TransformPivotConfig } from '../../../../common'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; import { applyFormFieldsToTransformConfig, @@ -86,9 +86,7 @@ describe('Transform: applyFormFieldsToTransformConfig()', () => { }); test('should include previously nonexisting attributes', () => { - const transformConfigMock = getTransformConfigMock(); - delete transformConfigMock.description; - delete transformConfigMock.frequency; + const { description, frequency, ...transformConfigMock } = getTransformConfigMock(); const updateConfig = applyFormFieldsToTransformConfig(transformConfigMock, { description: getDescriptionFieldMock('the-new-description'), diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.ts index 649db51e6ea7..d622a7e9cc04 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.ts +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/edit_transform_flyout/use_edit_transform_flyout.ts @@ -9,7 +9,8 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; -import { TransformPivotConfig } from '../../../../common'; +import { PostTransformsUpdateRequestSchema } from '../../../../../../common/api_schemas/update_transforms'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; // A Validator function takes in a value to check and returns an array of error messages. // If no messages (empty array) get returned, the value is valid. @@ -118,53 +119,35 @@ interface Action { value: string; } -// Some attributes can have a value of `null` to trigger -// a reset to the default value, or in the case of `docs_per_second` -// `null` is used to disable throttling. -interface UpdateTransformPivotConfig { - description: string; - frequency: string; - settings: { - docs_per_second: number | null; - }; -} - // Takes in the form configuration and returns a // request object suitable to be sent to the // transform update API endpoint. export const applyFormFieldsToTransformConfig = ( config: TransformPivotConfig, { description, docsPerSecond, frequency }: EditTransformFlyoutFieldsState -): Partial => { - const updateConfig: Partial = {}; - - // set the values only if they changed from the default - // and actually differ from the previous value. - if ( - !(config.frequency === undefined && frequency.value === '') && - config.frequency !== frequency.value - ) { - updateConfig.frequency = frequency.value; - } - - if ( - !(config.description === undefined && description.value === '') && - config.description !== description.value - ) { - updateConfig.description = description.value; - } - +): PostTransformsUpdateRequestSchema => { // if the input field was left empty, // fall back to the default value of `null` // which will disable throttling. const docsPerSecondFormValue = docsPerSecond.value !== '' ? parseInt(docsPerSecond.value, 10) : null; const docsPerSecondConfigValue = config.settings?.docs_per_second ?? null; - if (docsPerSecondFormValue !== docsPerSecondConfigValue) { - updateConfig.settings = { docs_per_second: docsPerSecondFormValue }; - } - return updateConfig; + return { + // set the values only if they changed from the default + // and actually differ from the previous value. + ...(!(config.frequency === undefined && frequency.value === '') && + config.frequency !== frequency.value + ? { frequency: frequency.value } + : {}), + ...(!(config.description === undefined && description.value === '') && + config.description !== description.value + ? { description: description.value } + : {}), + ...(docsPerSecondFormValue !== docsPerSecondConfigValue + ? { settings: { docs_per_second: docsPerSecondFormValue } } + : {}), + }; }; // Takes in a transform configuration and returns diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts index 11e4dc3dfa2b..f6708f7c36f2 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/common.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; import mockTransformListRow from '../../../../common/__mocks__/transform_list_row.json'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx index 08545c288ba9..02bad50dc0df 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_messages_pane.tsx @@ -9,11 +9,15 @@ import React, { useState } from 'react'; import { EuiSpacer, EuiBasicTable } from '@elastic/eui'; // @ts-ignore import { formatDate } from '@elastic/eui/lib/services/format'; -import { i18n } from '@kbn/i18n'; import theme from '@elastic/eui/dist/eui_theme_light.json'; + +import { i18n } from '@kbn/i18n'; + +import { isGetTransformsAuditMessagesResponseSchema } from '../../../../../../common/api_schemas/type_guards'; +import { TransformMessage } from '../../../../../../common/types/messages'; + import { useApi } from '../../../../hooks/use_api'; import { JobIcon } from '../../../../components/job_icon'; -import { TransformMessage } from '../../../../../../common/types/messages'; import { useRefreshTransformList } from '../../../../common'; const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; @@ -36,25 +40,16 @@ export const ExpandedRowMessagesPane: React.FC = ({ transformId }) => { let concurrentLoads = 0; return async function getMessages() { - try { - concurrentLoads++; - - if (concurrentLoads > 1) { - return; - } + concurrentLoads++; - setIsLoading(true); - const messagesResp = await api.getTransformAuditMessages(transformId); - setIsLoading(false); - setMessages(messagesResp as any[]); + if (concurrentLoads > 1) { + return; + } - concurrentLoads--; + setIsLoading(true); + const messagesResp = await api.getTransformAuditMessages(transformId); - if (concurrentLoads > 0) { - concurrentLoads = 0; - getMessages(); - } - } catch (error) { + if (!isGetTransformsAuditMessagesResponseSchema(messagesResp)) { setIsLoading(false); setErrorMessage( i18n.translate( @@ -64,6 +59,17 @@ export const ExpandedRowMessagesPane: React.FC = ({ transformId }) => { } ) ); + return; + } + + setIsLoading(false); + setMessages(messagesResp as any[]); + + concurrentLoads--; + + if (concurrentLoads > 0) { + concurrentLoads = 0; + getMessages(); } }; }; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_preview_pane.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_preview_pane.tsx index a917fc73ad8f..87d9a25dabab 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_preview_pane.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_preview_pane.tsx @@ -6,10 +6,11 @@ import React, { useMemo, FC } from 'react'; +import { TransformPivotConfig } from '../../../../../../common/types/transform'; import { DataGrid } from '../../../../../shared_imports'; import { useToastNotifications } from '../../../../app_dependencies'; -import { getPivotQuery, TransformPivotConfig } from '../../../../common'; +import { getPivotQuery } from '../../../../common'; import { usePivotData } from '../../../../hooks/use_pivot_data'; import { SearchItems } from '../../../../hooks/use_search_items'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx index dad0f0e5ee28..12836c0a18ce 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx @@ -23,7 +23,7 @@ import { EuiTitle, } from '@elastic/eui'; -import { TransformId } from '../../../../../../common'; +import { TransformId } from '../../../../../../common/types/transform'; import { useRefreshTransformList, @@ -189,7 +189,11 @@ export const TransformList: FC = ({
,
- stopTransforms(transformSelection)}> + + stopTransforms(transformSelection.map((t) => ({ id: t.id, state: t.stats.state }))) + } + >
, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar.tsx index fab591f88131..fdcb9ba5f0af 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar.tsx @@ -16,8 +16,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TermClause, FieldClause, Value } from './common'; -import { TRANSFORM_STATE } from '../../../../../../common'; -import { TRANSFORM_MODE, TransformListRow } from '../../../../common'; +import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants'; +import { TransformListRow } from '../../../../common'; import { getTaskStateBadge } from './use_columns'; const filters: SearchFilterConfig[] = [ diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx index bce01b954c83..313668d4c518 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transforms_stats_bar.tsx @@ -7,9 +7,9 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; -import { TRANSFORM_STATE } from '../../../../../../common'; +import { TRANSFORM_MODE, TRANSFORM_STATE } from '../../../../../../common/constants'; -import { TRANSFORM_MODE, TransformListRow } from '../../../../common'; +import { TransformListRow } from '../../../../common'; import { StatsBar, TransformStatsBarStats } from '../stats_bar'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx index d2d8c7084941..040e502ce488 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx @@ -22,24 +22,21 @@ import { RIGHT_ALIGNMENT, } from '@elastic/eui'; -import { TransformId, TRANSFORM_STATE } from '../../../../../../common'; +import { TransformId } from '../../../../../../common/types/transform'; +import { TransformStats } from '../../../../../../common/types/transform_stats'; +import { TRANSFORM_STATE } from '../../../../../../common/constants'; -import { - getTransformProgress, - TransformListRow, - TransformStats, - TRANSFORM_LIST_COLUMN, -} from '../../../../common'; +import { getTransformProgress, TransformListRow, TRANSFORM_LIST_COLUMN } from '../../../../common'; import { useActions } from './use_actions'; -enum STATE_COLOR { - aborting = 'warning', - failed = 'danger', - indexing = 'primary', - started = 'primary', - stopped = 'hollow', - stopping = 'hollow', -} +const STATE_COLOR = { + aborting: 'warning', + failed: 'danger', + indexing: 'primary', + started: 'primary', + stopped: 'hollow', + stopping: 'hollow', +} as const; export const getTaskStateBadge = ( state: TransformStats['state'], diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index abbc39dd6c72..4737787dbd9e 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -15,7 +15,6 @@ export { export { getFieldType, - getErrorMessage, extractErrorMessage, formatHumanReadableDateTimeSeconds, getDataGridSchemaFromKibanaFieldType, @@ -28,7 +27,6 @@ export { DataGrid, EsSorting, RenderCellValue, - SearchResponse7, UseDataGridReturnType, UseIndexDataReturnType, INDEX_STATUS, diff --git a/x-pack/plugins/transform/server/README.md b/x-pack/plugins/transform/server/README.md new file mode 100644 index 000000000000..1142c1fea094 --- /dev/null +++ b/x-pack/plugins/transform/server/README.md @@ -0,0 +1,19 @@ +# Transform Kibana API routes + +This folder contains Transform API routes in Kibana. + +Each route handler requires [apiDoc](https://github.com/apidoc/apidoc) annotations in order +to generate documentation. +The [apidoc-markdown](https://github.com/rigwild/apidoc-markdown) package is also required in order to generate the markdown. + +There are custom parser and worker (`x-pack/plugins/transform/server/routes/apidoc_scripts`) to process api schemas for each documentation entry. It's written with typescript so make sure all the scripts in the folder are compiled before executing `apidoc` command. + +Make sure you have run `yarn kbn bootstrap` to get all requires dev dependencies. Then execute the following command from the transform plugin folder: +``` +yarn run apiDocs +``` +It compiles all the required scripts and generates the documentation both in HTML and Markdown formats. + + +It will create a new directory `routes_doc` (next to the `routes` folder) which contains the documentation in HTML format +as well as `Transform_API.md` file. diff --git a/x-pack/plugins/transform/server/plugin.ts b/x-pack/plugins/transform/server/plugin.ts index 79e9be239c79..988750f70efe 100644 --- a/x-pack/plugins/transform/server/plugin.ts +++ b/x-pack/plugins/transform/server/plugin.ts @@ -58,7 +58,7 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { this.license = new License(); } - setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies): {} { + setup({ http, getStartServices }: CoreSetup, { licensing, features }: Dependencies): {} { const router = http.createRouter(); this.license.setup( @@ -75,6 +75,20 @@ export class TransformServerPlugin implements Plugin<{}, void, any, any> { } ); + features.registerElasticsearchFeature({ + id: PLUGIN.id, + management: { + data: [PLUGIN.id], + }, + catalogue: [PLUGIN.id], + privileges: [ + { + requiredClusterPrivileges: ['monitor_transform'], + ui: [], + }, + ], + }); + this.apiRoutes.setup({ router, license: this.license, diff --git a/x-pack/plugins/transform/server/routes/api/error_utils.ts b/x-pack/plugins/transform/server/routes/api/error_utils.ts index 5a479e4f429f..269cd28c4bda 100644 --- a/x-pack/plugins/transform/server/routes/api/error_utils.ts +++ b/x-pack/plugins/transform/server/routes/api/error_utils.ts @@ -10,11 +10,8 @@ import { i18n } from '@kbn/i18n'; import { ResponseError, CustomHttpResponseOptions } from 'src/core/server'; -import { - TransformEndpointRequest, - TransformEndpointResult, - DeleteTransformEndpointResult, -} from '../../../common'; +import { CommonResponseStatusSchema, TransformIdsSchema } from '../../../common/api_schemas/common'; +import { DeleteTransformsResponseSchema } from '../../../common/api_schemas/delete_transforms'; const REQUEST_TIMEOUT = 'RequestTimeout'; @@ -23,9 +20,9 @@ export function isRequestTimeout(error: any) { } interface Params { - results: TransformEndpointResult | DeleteTransformEndpointResult; + results: CommonResponseStatusSchema | DeleteTransformsResponseSchema; id: string; - items: TransformEndpointRequest[]; + items: TransformIdsSchema; action: string; } @@ -63,7 +60,7 @@ export function fillResultsWithTimeouts({ results, id, items, action }: Params) }, }; - const newResults: TransformEndpointResult | DeleteTransformEndpointResult = {}; + const newResults: CommonResponseStatusSchema | DeleteTransformsResponseSchema = {}; return items.reduce((accumResults, currentVal) => { if (results[currentVal.id] === undefined) { diff --git a/x-pack/plugins/transform/server/routes/api/field_histograms.ts b/x-pack/plugins/transform/server/routes/api/field_histograms.ts index 2642040c4cd0..88352ec4af12 100644 --- a/x-pack/plugins/transform/server/routes/api/field_histograms.ts +++ b/x-pack/plugins/transform/server/routes/api/field_histograms.ts @@ -11,40 +11,49 @@ import { wrapEsError } from '../../../../../legacy/server/lib/create_router/error_wrappers'; +import { + indexPatternTitleSchema, + IndexPatternTitleSchema, +} from '../../../common/api_schemas/common'; +import { + fieldHistogramsRequestSchema, + FieldHistogramsRequestSchema, +} from '../../../common/api_schemas/field_histograms'; import { getHistogramsForFields } from '../../shared_imports'; import { RouteDependencies } from '../../types'; import { addBasePath } from '../index'; import { wrapError } from './error_utils'; -import { fieldHistogramsSchema, indexPatternTitleSchema, IndexPatternTitleSchema } from './schema'; export function registerFieldHistogramsRoutes({ router, license }: RouteDependencies) { - router.post( + router.post( { path: addBasePath('field_histograms/{indexPatternTitle}'), validate: { params: indexPatternTitleSchema, - body: fieldHistogramsSchema, + body: fieldHistogramsRequestSchema, }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { indexPatternTitle } = req.params as IndexPatternTitleSchema; - const { query, fields, samplerShardSize } = req.body; + license.guardApiRoute( + async (ctx, req, res) => { + const { indexPatternTitle } = req.params; + const { query, fields, samplerShardSize } = req.body; - try { - const resp = await getHistogramsForFields( - ctx.core.elasticsearch.client, - indexPatternTitle, - query, - fields, - samplerShardSize - ); + try { + const resp = await getHistogramsForFields( + ctx.core.elasticsearch.client, + indexPatternTitle, + query, + fields, + samplerShardSize + ); - return res.ok({ body: resp }); - } catch (e) { - return res.customError(wrapError(wrapEsError(e))); + return res.ok({ body: resp }); + } catch (e) { + return res.customError(wrapError(wrapEsError(e))); + } } - }) + ) ); } diff --git a/x-pack/plugins/transform/server/routes/api/privileges.ts b/x-pack/plugins/transform/server/routes/api/privileges.ts index 2b7b0544a8bf..605cbde356fd 100644 --- a/x-pack/plugins/transform/server/routes/api/privileges.ts +++ b/x-pack/plugins/transform/server/routes/api/privileges.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { APP_CLUSTER_PRIVILEGES, APP_INDEX_PRIVILEGES } from '../../../common/constants'; -import { Privileges } from '../../../common'; +import { Privileges } from '../../../common/types/privileges'; import { RouteDependencies } from '../../types'; import { addBasePath } from '../index'; diff --git a/x-pack/plugins/transform/server/routes/api/schema.ts b/x-pack/plugins/transform/server/routes/api/schema.ts deleted file mode 100644 index 8aadef81b221..000000000000 --- a/x-pack/plugins/transform/server/routes/api/schema.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 { schema } from '@kbn/config-schema'; - -export const fieldHistogramsSchema = schema.object({ - /** Query to match documents in the index. */ - query: schema.any(), - /** The fields to return histogram data. */ - fields: schema.arrayOf(schema.any()), - /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */ - samplerShardSize: schema.number(), -}); - -export const indexPatternTitleSchema = schema.object({ - /** Title of the index pattern for which to return stats. */ - indexPatternTitle: schema.string(), -}); - -export interface IndexPatternTitleSchema { - indexPatternTitle: string; -} - -export const schemaTransformId = { - params: schema.object({ - transformId: schema.string(), - }), -}; - -export interface SchemaTransformId { - transformId: string; -} - -export const deleteTransformSchema = schema.object({ - /** - * Delete Transform & Destination Index - */ - transformsInfo: schema.arrayOf( - schema.object({ - id: schema.string(), - state: schema.maybe(schema.string()), - }) - ), - deleteDestIndex: schema.maybe(schema.boolean()), - deleteDestIndexPattern: schema.maybe(schema.boolean()), - forceDelete: schema.maybe(schema.boolean()), -}); diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts index efbe813db5e6..c02bc06ad606 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms.ts @@ -14,22 +14,47 @@ import { import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; import { wrapEsError } from '../../../../../legacy/server/lib/create_router/error_wrappers'; +import { TRANSFORM_STATE } from '../../../common/constants'; +import { TransformId } from '../../../common/types/transform'; import { - TransformEndpointRequest, - TransformEndpointResult, - TransformId, - TRANSFORM_STATE, - DeleteTransformEndpointRequest, - DeleteTransformStatus, - ResultData, -} from '../../../common'; + transformIdParamSchema, + ResponseStatus, + TransformIdParamSchema, +} from '../../../common/api_schemas/common'; +import { + deleteTransformsRequestSchema, + DeleteTransformsRequestSchema, + DeleteTransformsResponseSchema, +} from '../../../common/api_schemas/delete_transforms'; +import { + startTransformsRequestSchema, + StartTransformsRequestSchema, + StartTransformsResponseSchema, +} from '../../../common/api_schemas/start_transforms'; +import { + stopTransformsRequestSchema, + StopTransformsRequestSchema, + StopTransformsResponseSchema, +} from '../../../common/api_schemas/stop_transforms'; +import { + postTransformsUpdateRequestSchema, + PostTransformsUpdateRequestSchema, + PostTransformsUpdateResponseSchema, +} from '../../../common/api_schemas/update_transforms'; +import { + GetTransformsResponseSchema, + postTransformsPreviewRequestSchema, + PostTransformsPreviewRequestSchema, + putTransformsRequestSchema, + PutTransformsRequestSchema, + PutTransformsResponseSchema, +} from '../../../common/api_schemas/transforms'; import { RouteDependencies } from '../../types'; import { addBasePath } from '../index'; import { isRequestTimeout, fillResultsWithTimeouts, wrapError } from './error_utils'; -import { deleteTransformSchema, schemaTransformId, SchemaTransformId } from './schema'; import { registerTransformsAuditMessagesRoutes } from './transforms_audit_messages'; import { IIndexPattern } from '../../../../../../src/plugins/data/common/index_patterns'; @@ -47,6 +72,16 @@ interface StopOptions { export function registerTransformsRoutes(routeDependencies: RouteDependencies) { const { router, license } = routeDependencies; + /** + * @apiGroup Transforms + * + * @api {get} /api/transform/transforms Get transforms + * @apiName GetTransforms + * @apiDescription Returns transforms + * + * @apiSchema (params) jobAuditMessagesJobIdSchema + * @apiSchema (query) jobAuditMessagesQuerySchema + */ router.get( { path: addBasePath('transforms'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { @@ -62,16 +97,24 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { } }) ); - router.get( + + /** + * @apiGroup Transforms + * + * @api {get} /api/transform/transforms/:transformId Get transform + * @apiName GetTransform + * @apiDescription Returns a single transform + * + * @apiSchema (params) transformIdParamSchema + */ + router.get( { path: addBasePath('transforms/{transformId}'), - validate: schemaTransformId, + validate: { params: transformIdParamSchema }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { transformId } = req.params as SchemaTransformId; - const options = { - ...(transformId !== undefined ? { transformId } : {}), - }; + license.guardApiRoute(async (ctx, req, res) => { + const { transformId } = req.params; + const options = transformId !== undefined ? { transformId } : {}; try { const transforms = await getTransforms( options, @@ -83,6 +126,14 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { } }) ); + + /** + * @apiGroup Transforms + * + * @api {get} /api/transform/transforms/_stats Get transforms stats + * @apiName GetTransformsStats + * @apiDescription Returns transforms stats + */ router.get( { path: addBasePath('transforms/_stats'), validate: false }, license.guardApiRoute(async (ctx, req, res) => { @@ -98,13 +149,23 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { } }) ); - router.get( + + /** + * @apiGroup Transforms + * + * @api {get} /api/transform/transforms/:transformId/_stats Get transform stats + * @apiName GetTransformStats + * @apiDescription Returns stats for a single transform + * + * @apiSchema (params) transformIdParamSchema + */ + router.get( { path: addBasePath('transforms/{transformId}/_stats'), - validate: schemaTransformId, + validate: { params: transformIdParamSchema }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { transformId } = req.params as SchemaTransformId; + license.guardApiRoute(async (ctx, req, res) => { + const { transformId } = req.params; const options = { ...(transformId !== undefined ? { transformId } : {}), }; @@ -120,134 +181,198 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { }) ); registerTransformsAuditMessagesRoutes(routeDependencies); - router.put( + + /** + * @apiGroup Transforms + * + * @api {put} /api/transform/transforms/:transformId Put transform + * @apiName PutTransform + * @apiDescription Creates a transform + * + * @apiSchema (params) transformIdParamSchema + * @apiSchema (body) putTransformsRequestSchema + */ + router.put( { path: addBasePath('transforms/{transformId}'), validate: { - ...schemaTransformId, - body: schema.maybe(schema.any()), + params: transformIdParamSchema, + body: putTransformsRequestSchema, }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { transformId } = req.params as SchemaTransformId; - - const response: { - transformsCreated: Array<{ transform: string }>; - errors: any[]; - } = { - transformsCreated: [], - errors: [], - }; + license.guardApiRoute( + async (ctx, req, res) => { + const { transformId } = req.params; - await ctx - .transform!.dataClient.callAsCurrentUser('transform.createTransform', { - body: req.body, - transformId, - }) - .then(() => response.transformsCreated.push({ transform: transformId })) - .catch((e) => - response.errors.push({ - id: transformId, - error: wrapEsError(e), + const response: PutTransformsResponseSchema = { + transformsCreated: [], + errors: [], + }; + + await ctx + .transform!.dataClient.callAsCurrentUser('transform.createTransform', { + body: req.body, + transformId, }) - ); + .then(() => response.transformsCreated.push({ transform: transformId })) + .catch((e) => + response.errors.push({ + id: transformId, + error: wrapEsError(e), + }) + ); - return res.ok({ body: response }); - }) + return res.ok({ body: response }); + } + ) ); - router.post( + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/transforms/:transformId/_update Post transform update + * @apiName PostTransformUpdate + * @apiDescription Updates a transform + * + * @apiSchema (params) transformIdParamSchema + * @apiSchema (body) postTransformsUpdateRequestSchema + */ + router.post( { path: addBasePath('transforms/{transformId}/_update'), validate: { - ...schemaTransformId, - body: schema.maybe(schema.any()), + params: transformIdParamSchema, + body: postTransformsUpdateRequestSchema, }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { transformId } = req.params as SchemaTransformId; + license.guardApiRoute( + async (ctx, req, res) => { + const { transformId } = req.params; - try { - return res.ok({ - body: await ctx.transform!.dataClient.callAsCurrentUser('transform.updateTransform', { - body: req.body, - transformId, - }), - }); - } catch (e) { - return res.customError(wrapError(e)); + try { + return res.ok({ + body: (await ctx.transform!.dataClient.callAsCurrentUser('transform.updateTransform', { + body: req.body, + transformId, + })) as PostTransformsUpdateResponseSchema, + }); + } catch (e) { + return res.customError(wrapError(e)); + } } - }) + ) ); - router.post( + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/delete_transforms Post delete transforms + * @apiName DeleteTransforms + * @apiDescription Deletes transforms + * + * @apiSchema (body) deleteTransformsRequestSchema + */ + router.post( { path: addBasePath('delete_transforms'), validate: { - body: deleteTransformSchema, + body: deleteTransformsRequestSchema, }, }, - license.guardApiRoute(async (ctx, req, res) => { - const { - transformsInfo, - deleteDestIndex, - deleteDestIndexPattern, - forceDelete, - } = req.body as DeleteTransformEndpointRequest; - - try { - const body = await deleteTransforms( - transformsInfo, - deleteDestIndex, - deleteDestIndexPattern, - forceDelete, - ctx, - license, - res - ); - - if (body && body.status) { - if (body.status === 404) { - return res.notFound(); - } - if (body.status === 403) { - return res.forbidden(); + license.guardApiRoute( + async (ctx, req, res) => { + try { + const body = await deleteTransforms(req.body, ctx, res); + + if (body && body.status) { + if (body.status === 404) { + return res.notFound(); + } + if (body.status === 403) { + return res.forbidden(); + } } - } - return res.ok({ - body, - }); - } catch (e) { - return res.customError(wrapError(wrapEsError(e))); + return res.ok({ + body, + }); + } catch (e) { + return res.customError(wrapError(wrapEsError(e))); + } } - }) + ) ); - router.post( + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/transforms/_preview Preview transform + * @apiName PreviewTransform + * @apiDescription Previews transform + * + * @apiSchema (body) postTransformsPreviewRequestSchema + */ + router.post( { path: addBasePath('transforms/_preview'), validate: { - body: schema.maybe(schema.any()), + body: postTransformsPreviewRequestSchema, }, }, - license.guardApiRoute(previewTransformHandler) + license.guardApiRoute( + previewTransformHandler + ) ); - router.post( + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/start_transforms Start transforms + * @apiName PostStartTransforms + * @apiDescription Starts transform + * + * @apiSchema (body) startTransformsRequestSchema + */ + router.post( { path: addBasePath('start_transforms'), validate: { - body: schema.maybe(schema.any()), + body: startTransformsRequestSchema, }, }, - license.guardApiRoute(startTransformsHandler) + license.guardApiRoute( + startTransformsHandler + ) ); - router.post( + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/stop_transforms Stop transforms + * @apiName PostStopTransforms + * @apiDescription Stops transform + * + * @apiSchema (body) stopTransformsRequestSchema + */ + router.post( { path: addBasePath('stop_transforms'), validate: { - body: schema.maybe(schema.any()), + body: stopTransformsRequestSchema, }, }, - license.guardApiRoute(stopTransformsHandler) + license.guardApiRoute(stopTransformsHandler) ); + + /** + * @apiGroup Transforms + * + * @api {post} /api/transform/es_search Transform ES Search Proxy + * @apiName PostTransformEsSearchProxy + * @apiDescription ES Search Proxy + * + * @apiSchema (body) any + */ router.post( { path: addBasePath('es_search'), @@ -267,7 +392,10 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { ); } -const getTransforms = async (options: { transformId?: string }, callAsCurrentUser: CallCluster) => { +const getTransforms = async ( + options: { transformId?: string }, + callAsCurrentUser: CallCluster +): Promise => { return await callAsCurrentUser('transform.getTransforms', options); }; @@ -294,22 +422,25 @@ async function deleteDestIndexPatternById( } async function deleteTransforms( - transformsInfo: TransformEndpointRequest[], - deleteDestIndex: boolean | undefined, - deleteDestIndexPattern: boolean | undefined, - shouldForceDelete: boolean = false, + reqBody: DeleteTransformsRequestSchema, ctx: RequestHandlerContext, - license: RouteDependencies['license'], response: KibanaResponseFactory ) { - const results: Record = {}; + const { transformsInfo } = reqBody; + + // Cast possible undefineds as booleans + const deleteDestIndex = !!reqBody.deleteDestIndex; + const deleteDestIndexPattern = !!reqBody.deleteDestIndexPattern; + const shouldForceDelete = !!reqBody.forceDelete; + + const results: DeleteTransformsResponseSchema = {}; for (const transformInfo of transformsInfo) { let destinationIndex: string | undefined; - const transformDeleted: ResultData = { success: false }; - const destIndexDeleted: ResultData = { success: false }; - const destIndexPatternDeleted: ResultData = { + const transformDeleted: ResponseStatus = { success: false }; + const destIndexDeleted: ResponseStatus = { success: false }; + const destIndexPatternDeleted: ResponseStatus = { success: false, }; const transformId = transformInfo.id; @@ -405,7 +536,11 @@ async function deleteTransforms( return results; } -const previewTransformHandler: RequestHandler = async (ctx, req, res) => { +const previewTransformHandler: RequestHandler< + undefined, + undefined, + PostTransformsPreviewRequestSchema +> = async (ctx, req, res) => { try { return res.ok({ body: await ctx.transform!.dataClient.callAsCurrentUser('transform.getTransformsPreview', { @@ -417,8 +552,12 @@ const previewTransformHandler: RequestHandler = async (ctx, req, res) => { } }; -const startTransformsHandler: RequestHandler = async (ctx, req, res) => { - const transformsInfo = req.body as TransformEndpointRequest[]; +const startTransformsHandler: RequestHandler< + undefined, + undefined, + StartTransformsRequestSchema +> = async (ctx, req, res) => { + const transformsInfo = req.body; try { return res.ok({ @@ -430,15 +569,15 @@ const startTransformsHandler: RequestHandler = async (ctx, req, res) => { }; async function startTransforms( - transformsInfo: TransformEndpointRequest[], + transformsInfo: StartTransformsRequestSchema, callAsCurrentUser: CallCluster ) { - const results: TransformEndpointResult = {}; + const results: StartTransformsResponseSchema = {}; for (const transformInfo of transformsInfo) { const transformId = transformInfo.id; try { - await callAsCurrentUser('transform.startTransform', { transformId } as SchemaTransformId); + await callAsCurrentUser('transform.startTransform', { transformId }); results[transformId] = { success: true }; } catch (e) { if (isRequestTimeout(e)) { @@ -455,8 +594,12 @@ async function startTransforms( return results; } -const stopTransformsHandler: RequestHandler = async (ctx, req, res) => { - const transformsInfo = req.body as TransformEndpointRequest[]; +const stopTransformsHandler: RequestHandler< + undefined, + undefined, + StopTransformsRequestSchema +> = async (ctx, req, res) => { + const transformsInfo = req.body; try { return res.ok({ @@ -468,10 +611,10 @@ const stopTransformsHandler: RequestHandler = async (ctx, req, res) => { }; async function stopTransforms( - transformsInfo: TransformEndpointRequest[], + transformsInfo: StopTransformsRequestSchema, callAsCurrentUser: CallCluster ) { - const results: TransformEndpointResult = {}; + const results: StopTransformsResponseSchema = {}; for (const transformInfo of transformsInfo) { const transformId = transformInfo.id; diff --git a/x-pack/plugins/transform/server/routes/api/transforms_audit_messages.ts b/x-pack/plugins/transform/server/routes/api/transforms_audit_messages.ts index 722a3f52376b..f01b2bdb73fd 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms_audit_messages.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms_audit_messages.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AuditMessage } from '../../../common/types/messages'; +import { transformIdParamSchema, TransformIdParamSchema } from '../../../common/api_schemas/common'; +import { AuditMessage, TransformMessage } from '../../../common/types/messages'; import { wrapEsError } from '../../../../../legacy/server/lib/create_router/error_wrappers'; import { RouteDependencies } from '../../types'; @@ -12,7 +13,6 @@ import { RouteDependencies } from '../../types'; import { addBasePath } from '../index'; import { wrapError } from './error_utils'; -import { schemaTransformId, SchemaTransformId } from './schema'; const ML_DF_NOTIFICATION_INDEX_PATTERN = '.transform-notifications-read'; const SIZE = 500; @@ -22,10 +22,22 @@ interface BoolQuery { } export function registerTransformsAuditMessagesRoutes({ router, license }: RouteDependencies) { - router.get( - { path: addBasePath('transforms/{transformId}/messages'), validate: schemaTransformId }, - license.guardApiRoute(async (ctx, req, res) => { - const { transformId } = req.params as SchemaTransformId; + /** + * @apiGroup Transforms Audit Messages + * + * @api {get} /api/transform/transforms/:transformId/messages Transforms Messages + * @apiName GetTransformsMessages + * @apiDescription Get transforms audit messages + * + * @apiSchema (params) transformIdParamSchema + */ + router.get( + { + path: addBasePath('transforms/{transformId}/messages'), + validate: { params: transformIdParamSchema }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { transformId } = req.params; // search for audit messages, // transformId is optional. without it, all transforms will be listed. @@ -77,7 +89,7 @@ export function registerTransformsAuditMessagesRoutes({ router, license }: Route }, }); - let messages = []; + let messages: TransformMessage[] = []; if (resp.hits.total !== 0) { messages = resp.hits.hits.map((hit: AuditMessage) => hit._source); messages.reverse(); diff --git a/x-pack/plugins/transform/server/routes/apidoc.json b/x-pack/plugins/transform/server/routes/apidoc.json new file mode 100644 index 000000000000..ce76b5b302f9 --- /dev/null +++ b/x-pack/plugins/transform/server/routes/apidoc.json @@ -0,0 +1,21 @@ +{ + "name": "transform_kibana_api", + "version": "7.10.0", + "description": "This is the documentation of the REST API provided by the Transform Kibana plugin. Each API is experimental and can include breaking changes in any version.", + "title": "Transform Kibana API", + "order": [ + "GetTransforms", + "GetTransform", + "GetTransformsStats", + "GetTransformStats", + "PutTransform", + "PostTransformUpdate", + "DeleteTransforms", + "PreviewTransform", + "PostStartTransforms", + "PostStopTransforms", + "PostTransformEsSearchProxy", + "DeleteDataFrameAnalytics", + "GetTransformsMessages" + ] +} diff --git a/x-pack/plugins/transform/server/services/license.ts b/x-pack/plugins/transform/server/services/license.ts index 1a2768999fdc..bacf9724a625 100644 --- a/x-pack/plugins/transform/server/services/license.ts +++ b/x-pack/plugins/transform/server/services/license.ts @@ -62,12 +62,12 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute(handler: RequestHandler) { const license = this; return function licenseCheck( ctx: RequestHandlerContext, - request: KibanaRequest, + request: KibanaRequest, response: KibanaResponseFactory ): IKibanaResponse | Promise> { const licenseStatus = license.getStatus(); diff --git a/x-pack/plugins/transform/server/types.ts b/x-pack/plugins/transform/server/types.ts index 5fcc23a6d9f4..c3d7434f14f4 100644 --- a/x-pack/plugins/transform/server/types.ts +++ b/x-pack/plugins/transform/server/types.ts @@ -6,10 +6,12 @@ import { IRouter } from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { License } from './services'; export interface Dependencies { licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; } export interface RouteDependencies { diff --git a/x-pack/plugins/transform/tsconfig.json b/x-pack/plugins/transform/tsconfig.json new file mode 100644 index 000000000000..6f83eb665f83 --- /dev/null +++ b/x-pack/plugins/transform/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json", +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 54c92d323fcf..3938ed163f6c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -816,8 +816,6 @@ "data.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "KQL ネストされたクエリ構文", "data.query.queryBar.kqlOffLabel": "オフ", "data.query.queryBar.kqlOnLabel": "オン", - "data.query.queryBar.licenseOptions": "ライセンスオプションに進む", - "data.query.queryBar.longQueryMessage": "ライセンスをアップグレードすれば、リクエストの完了までに十分な時間を確保できます。", "data.query.queryBar.luceneLanguageName": "Lucene", "data.query.queryBar.luceneSyntaxWarningMessage": "Lucene クエリ構文を使用しているようですが、Kibana クエリ言語 (KQL) が選択されています。KQL ドキュメント {link} を確認してください。", "data.query.queryBar.luceneSyntaxWarningOptOutText": "今後表示しない", @@ -4860,12 +4858,9 @@ "xpack.apm.serviceOverview.upgradeAssistantLink": "アップグレードアシスタント", "xpack.apm.servicesTable.7xOldDataMessage": "また、移行が必要な古いデータがある可能性もあります。", "xpack.apm.servicesTable.7xUpgradeServerMessage": "バージョン7.xより前からのアップグレードですか?また、\n APMサーバーインスタンスを7.0以降にアップグレードしていることも確認してください。", - "xpack.apm.servicesTable.agentColumnLabel": "エージェント", "xpack.apm.servicesTable.avgResponseTimeColumnLabel": "平均応答時間", "xpack.apm.servicesTable.environmentColumnLabel": "環境", "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, one {1 個の環境} other {# 個の環境}}", - "xpack.apm.servicesTable.errorsPerMinuteColumnLabel": "1 分あたりのエラー", - "xpack.apm.servicesTable.errorsPerMinuteUnitLabel": "エラー", "xpack.apm.servicesTable.nameColumnLabel": "名前", "xpack.apm.servicesTable.noServicesLabel": "APM サービスがインストールされていないようです。追加しましょう!", "xpack.apm.servicesTable.notFoundLabel": "サービスが見つかりません", @@ -6676,8 +6671,6 @@ "xpack.data.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText": "より小さいまたは等しい", "xpack.data.kueryAutocomplete.orOperatorDescription": "{oneOrMoreArguments} が true であることを条件とする", "xpack.data.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText": "1つ以上の引数", - "xpack.data.query.queryBar.cancelLongQuery": "キャンセル", - "xpack.data.query.queryBar.runBeyond": "タイムアウトを越えて実行", "xpack.discover.FlyoutCreateDrilldownAction.displayName": "基本データを調査", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "パネルには{count}個のドリルダウンがあります", "xpack.embeddableEnhanced.actions.panelNotifications.oneDrilldown": "パネルには1個のドリルダウンがあります", @@ -7581,7 +7574,6 @@ "xpack.idxMgmt.mappingsEditor.fielddata.frequencyFilterPercentageFieldLabel": "パーセンテージベースの頻度範囲", "xpack.idxMgmt.mappingsEditor.fielddata.useAbsoluteValuesFieldLabel": "絶対値の使用", "xpack.idxMgmt.mappingsEditor.fieldsTabLabel": "マッピングされたフィールド", - "xpack.idxMgmt.mappingsEditor.flattened.ignoreAboveDocLinkText": "上記ドキュメントの無視", "xpack.idxMgmt.mappingsEditor.formatDocLinkText": "フォーマットのドキュメンテーション", "xpack.idxMgmt.mappingsEditor.formatFieldLabel": "フォーマット", "xpack.idxMgmt.mappingsEditor.formatHelpText": "{dateSyntax}構文を使用し、カスタムフォーマットを指定します。", @@ -7712,10 +7704,6 @@ "xpack.idxMgmt.mappingsEditor.joinType.relationshipTable.parentFieldAriaLabel": "親フィールド", "xpack.idxMgmt.mappingsEditor.joinType.relationshipTable.removeRelationshipTooltipLabel": "関係を削除", "xpack.idxMgmt.mappingsEditor.largestShingleSizeFieldLabel": "最大シングルサイズ", - "xpack.idxMgmt.mappingsEditor.leafLengthLimitFieldDescription": "特定の長さ以上のリーフ値のインデックスを無効化。これは、Luceneの文字制限(8,191 UTF-8 文字)に対する保護に役立ちます。", - "xpack.idxMgmt.mappingsEditor.leafLengthLimitFieldTitle": "長さ制限の設定", - "xpack.idxMgmt.mappingsEditor.lengthLimitFieldDescription": "この値よりも長い文字列はインデックスされません。これは、Luceneの文字制限(8,191 UTF-8 文字)に対する保護に役立ちます。", - "xpack.idxMgmt.mappingsEditor.lengthLimitFieldTitle": "長さ制限の設定", "xpack.idxMgmt.mappingsEditor.loadFromJsonButtonLabel": "JSONの読み込み", "xpack.idxMgmt.mappingsEditor.loadJsonModal.acceptWarningLabel": "読み込みの続行", "xpack.idxMgmt.mappingsEditor.loadJsonModal.cancelButtonLabel": "キャンセル", @@ -10890,7 +10878,6 @@ "xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage": "データフレーム分析ジョブ{analyticsId}の削除中にエラーが発生しました。", "xpack.ml.dataframe.analyticsList.deleteAnalyticsPrivilegeErrorMessage": "ユーザーはインデックス{indexName}を削除する権限がありません。{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage": "データフレーム分析ジョブ{analyticsId}の削除リクエストが受け付けられました。", - "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage": "ディスティネーションインデックス{destinationIndex}の削除中にエラーが発生しました。{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternErrorMessage": "インデックスパターン{destinationIndex}の削除中にエラーが発生しました。{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternSuccessMessage": "インデックスパターン{destinationIndex}を削除する要求が確認されました。", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexSuccessMessage": "ディスティネーションインデックス{destinationIndex}を削除する要求が確認されました。", @@ -11358,16 +11345,9 @@ "xpack.ml.jobService.activeDatafeedsLabel": "アクティブなデータフィード", "xpack.ml.jobService.activeMLNodesLabel": "アクティブな ML ノード", "xpack.ml.jobService.closedJobsLabel": "ジョブを作成", - "xpack.ml.jobService.couldNotStartDatafeedErrorMessage": "{jobId} のデータフィードを開始できませんでした", - "xpack.ml.jobService.couldNotStopDatafeedErrorMessage": "{jobId} のデータフィードを停止できませんでした", - "xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage": "データフィードを更新できませんでした: {datafeedId}", - "xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage": "データフィードリストを取得できませんでした", "xpack.ml.jobService.failedJobsLabel": "失敗したジョブ", - "xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage": "ジョブリストを取得できませんでした", "xpack.ml.jobService.openJobsLabel": "ジョブを開く", - "xpack.ml.jobService.requestMayHaveTimedOutErrorMessage": "リクエストがタイムアウトし、まだバックグラウンドで実行中の可能性があります。", "xpack.ml.jobService.totalJobsLabel": "合計ジョブ数", - "xpack.ml.jobService.updateJobErrorTitle": "ジョブを更新できませんでした: {jobId}", "xpack.ml.jobService.validateJobErrorTitle": "ジョブ検証エラー", "xpack.ml.jobsList.actionExecuteSuccessfullyNotificationMessage": "{successesJobsCount, plural, one{{successJob}} other{# 件のジョブ}} {actionTextPT}成功", "xpack.ml.jobsList.actionFailedNotificationMessage": "{failureId} が {actionText} に失敗しました", @@ -11572,7 +11552,6 @@ "xpack.ml.maxFileSizeSettingsDescription": "ファイルデータビジュアライザーでデータをインポートするときのファイルサイズ上限を設定します。この設定でサポートされている最大値は1 GBです。", "xpack.ml.maxFileSizeSettingsError": "200 MB、1 GBなどの有効なデータサイズにしてください。", "xpack.ml.maxFileSizeSettingsName": "ファイルデータビジュアライザーの最大ファイルアップロードサイズ", - "xpack.ml.messagebarService.errorTitle": "エラーが発生しました", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 他のすべてのリクエストはキャンセルされました。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "フィールド値の例のサンプルをトークン化することができませんでした。{message}", "xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "権限が不十分なため、フィールド値の例のトークン化を実行できませんでした。そのため、フィールド値を確認し、カテゴリー分けジョブでの使用が適当かを確認することができません。", @@ -13719,7 +13698,6 @@ "xpack.monitoring.updateLicenseTitle": "ライセンスの更新", "xpack.monitoring.useAvailableLicenseDescription": "既に新しいライセンスがある場合は、今すぐアップロードしてください。", "xpack.monitoring.wedLabel": "水", - "xpack.observability.beta": "ベータ", "xpack.observability.emptySection.apps.alert.description": "503エラーが累積していますか?サービスは応答していますか?CPUとRAMの使用量が跳ね上がっていますか?このような警告を、事後にではなく、発生と同時に把握しましょう。", "xpack.observability.emptySection.apps.alert.link": "アラートの作成", "xpack.observability.emptySection.apps.alert.title": "アラートが見つかりません。", @@ -15107,9 +15085,6 @@ "xpack.securitySolution.case.configureCases.incidentManagementSystemDesc": "オプションとして、セキュリティケースを選択した外部のインシデント管理システムに接続できます。そうすると、選択したサードパーティシステム内でケースデータをインシデントとしてプッシュできます。", "xpack.securitySolution.case.configureCases.incidentManagementSystemLabel": "インシデント管理システム", "xpack.securitySolution.case.configureCases.incidentManagementSystemTitle": "外部のインシデント管理システムに接続", - "xpack.securitySolution.case.configureCases.mappingFieldComments": "コメント", - "xpack.securitySolution.case.configureCases.mappingFieldDescription": "説明", - "xpack.securitySolution.case.configureCases.mappingFieldName": "名前", "xpack.securitySolution.case.configureCases.mappingFieldNotMapped": "マップされません", "xpack.securitySolution.case.configureCases.noConnector": "コネクターを選択していません", "xpack.securitySolution.case.configureCases.updateConnector": "コネクターを更新", @@ -15123,25 +15098,6 @@ "xpack.securitySolution.case.confirmDeleteCase.deleteCases": "ケースを削除", "xpack.securitySolution.case.confirmDeleteCase.deleteTitle": "「{caseTitle}」を削除", "xpack.securitySolution.case.confirmDeleteCase.selectedCases": "選択したケースを削除", - "xpack.securitySolution.case.connectors.common.apiTokenTextFieldLabel": "APIトークン", - "xpack.securitySolution.case.connectors.common.apiUrlTextFieldLabel": "URL", - "xpack.securitySolution.case.connectors.common.emailTextFieldLabel": "メール", - "xpack.securitySolution.case.connectors.common.invalidApiUrlTextField": "URLが無効です", - "xpack.securitySolution.case.connectors.common.passwordTextFieldLabel": "パスワード", - "xpack.securitySolution.case.connectors.common.requiredApiTokenTextField": "APIトークンが必要です", - "xpack.securitySolution.case.connectors.common.requiredApiUrlTextField": "URLが必要です", - "xpack.securitySolution.case.connectors.common.requiredEmailTextField": "電子メールが必要です", - "xpack.securitySolution.case.connectors.common.requiredPasswordTextField": "パスワードが必要です", - "xpack.securitySolution.case.connectors.common.requiredUsernameTextField": "ユーザー名が必要です", - "xpack.securitySolution.case.connectors.common.usernameTextFieldLabel": "ユーザー名", - "xpack.securitySolution.case.connectors.resilient.actionTypeTitle": "IBM Resilient", - "xpack.securitySolution.case.connectors.resilient.apiKeyId": "APIキーID", - "xpack.securitySolution.case.connectors.resilient.apiKeySecret": "APIキーシークレット", - "xpack.securitySolution.case.connectors.resilient.orgId": "組織ID", - "xpack.securitySolution.case.connectors.resilient.requiredApiKeyIdTextField": "APIキーIDが必要です", - "xpack.securitySolution.case.connectors.resilient.requiredApiKeySecretTextField": "APIキーシークレットが必要です", - "xpack.securitySolution.case.connectors.resilient.requiredOrgIdTextField": "組織IDが必要です", - "xpack.securitySolution.case.connectors.resilient.selectMessageText": "Resilientでセキュリティケースデータを更新するか、新しいインシデントにプッシュ", "xpack.securitySolution.case.createCase.descriptionFieldRequiredError": "説明が必要です。", "xpack.securitySolution.case.createCase.fieldTagsHelpText": "このケースの1つ以上のカスタム識別タグを入力します。新しいタグを開始するには、各タグの後でEnterを押します。", "xpack.securitySolution.case.createCase.titleFieldRequiredError": "タイトルが必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index df721cb62466..c8eefb45ea9f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -817,8 +817,6 @@ "data.query.queryBar.KQLNestedQuerySyntaxInfoTitle": "KQL 嵌套查询语法", "data.query.queryBar.kqlOffLabel": "关闭", "data.query.queryBar.kqlOnLabel": "开启", - "data.query.queryBar.licenseOptions": "前往许可证选项", - "data.query.queryBar.longQueryMessage": "使用升级的许可证,您可以确保有足够的时间来完成请求。", "data.query.queryBar.luceneLanguageName": "Lucene", "data.query.queryBar.luceneSyntaxWarningMessage": "尽管您选择了 Kibana 查询语言 (KQL),但似乎您正在尝试使用 Lucene 查询语法。请查看 KQL 文档 {link}。", "data.query.queryBar.luceneSyntaxWarningOptOutText": "不再显示", @@ -4863,12 +4861,9 @@ "xpack.apm.serviceOverview.upgradeAssistantLink": "升级助手", "xpack.apm.servicesTable.7xOldDataMessage": "可能还有需要迁移的旧数据。", "xpack.apm.servicesTable.7xUpgradeServerMessage": "从 7.x 之前的版本升级?另外,确保您已将\n APM Server 实例升级到至少 7.0。", - "xpack.apm.servicesTable.agentColumnLabel": "代理", "xpack.apm.servicesTable.avgResponseTimeColumnLabel": "平均响应时间", "xpack.apm.servicesTable.environmentColumnLabel": "环境", "xpack.apm.servicesTable.environmentCount": "{environmentCount, plural, one {1 个环境} other {# 个环境}}", - "xpack.apm.servicesTable.errorsPerMinuteColumnLabel": "每分钟错误数", - "xpack.apm.servicesTable.errorsPerMinuteUnitLabel": "错误", "xpack.apm.servicesTable.nameColumnLabel": "名称", "xpack.apm.servicesTable.noServicesLabel": "似乎您没有安装任何 APM 服务。让我们添加一些!", "xpack.apm.servicesTable.notFoundLabel": "未找到任何服务", @@ -6679,8 +6674,6 @@ "xpack.data.kueryAutocomplete.lessThanOrEqualOperatorDescription.lessThanOrEqualToText": "小于或等于", "xpack.data.kueryAutocomplete.orOperatorDescription": "需要{oneOrMoreArguments}为 true", "xpack.data.kueryAutocomplete.orOperatorDescription.oneOrMoreArgumentsText": "一个或多个参数", - "xpack.data.query.queryBar.cancelLongQuery": "取消", - "xpack.data.query.queryBar.runBeyond": "运行超时", "xpack.discover.FlyoutCreateDrilldownAction.displayName": "浏览底层数据", "xpack.embeddableEnhanced.actions.panelNotifications.manyDrilldowns": "面板有 {count} 个向下钻取", "xpack.embeddableEnhanced.actions.panelNotifications.oneDrilldown": "面板有 1 个向下钻取", @@ -7584,7 +7577,6 @@ "xpack.idxMgmt.mappingsEditor.fielddata.frequencyFilterPercentageFieldLabel": "基于百分比的频率范围", "xpack.idxMgmt.mappingsEditor.fielddata.useAbsoluteValuesFieldLabel": "使用绝对值", "xpack.idxMgmt.mappingsEditor.fieldsTabLabel": "已映射字段", - "xpack.idxMgmt.mappingsEditor.flattened.ignoreAboveDocLinkText": "“忽略上述”文档", "xpack.idxMgmt.mappingsEditor.formatDocLinkText": "“格式”文档", "xpack.idxMgmt.mappingsEditor.formatFieldLabel": "格式", "xpack.idxMgmt.mappingsEditor.formatHelpText": "使用 {dateSyntax} 语法指定定制格式。", @@ -7715,10 +7707,6 @@ "xpack.idxMgmt.mappingsEditor.joinType.relationshipTable.parentFieldAriaLabel": "父项字段", "xpack.idxMgmt.mappingsEditor.joinType.relationshipTable.removeRelationshipTooltipLabel": "移除关系", "xpack.idxMgmt.mappingsEditor.largestShingleSizeFieldLabel": "最大瓦形大小", - "xpack.idxMgmt.mappingsEditor.leafLengthLimitFieldDescription": "如果叶值超过一定长度,则阻止叶值索引。这用于防止超出 Lucene 的字词字符长度限制,即 8,191 个 UTF-8 字符。", - "xpack.idxMgmt.mappingsEditor.leafLengthLimitFieldTitle": "设置长度限制", - "xpack.idxMgmt.mappingsEditor.lengthLimitFieldDescription": "将不索引超过此值的字符串。这用于防止超出 Lucene 的字词字符长度限制,即 8,191 个 UTF-8 字符。", - "xpack.idxMgmt.mappingsEditor.lengthLimitFieldTitle": "设置长度限制", "xpack.idxMgmt.mappingsEditor.loadFromJsonButtonLabel": "加载 JSON", "xpack.idxMgmt.mappingsEditor.loadJsonModal.acceptWarningLabel": "继续加载", "xpack.idxMgmt.mappingsEditor.loadJsonModal.cancelButtonLabel": "取消", @@ -10896,7 +10884,6 @@ "xpack.ml.dataframe.analyticsList.deleteAnalyticsErrorMessage": "删除数据帧分析作业 {analyticsId} 时发生错误", "xpack.ml.dataframe.analyticsList.deleteAnalyticsPrivilegeErrorMessage": "用户无权删除索引 {indexName}:{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage": "删除的数据帧分析作业 {analyticsId} 的请求已确认。", - "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexErrorMessage": "删除目标索引 {destinationIndex} 时发生错误:{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternErrorMessage": "删除索引模式 {destinationIndex} 时发生错误:{error}", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexPatternSuccessMessage": "删除索引模式 {destinationIndex} 的请求已确认。", "xpack.ml.dataframe.analyticsList.deleteAnalyticsWithIndexSuccessMessage": "删除目标索引 {destinationIndex} 的请求已确认。", @@ -11365,16 +11352,9 @@ "xpack.ml.jobService.activeDatafeedsLabel": "活动数据馈送", "xpack.ml.jobService.activeMLNodesLabel": "活动 ML 节点", "xpack.ml.jobService.closedJobsLabel": "已关闭的作业", - "xpack.ml.jobService.couldNotStartDatafeedErrorMessage": "无法开始 {jobId} 的数据馈送", - "xpack.ml.jobService.couldNotStopDatafeedErrorMessage": "无法停止 {jobId} 的数据馈送", - "xpack.ml.jobService.couldNotUpdateDatafeedErrorMessage": "无法更新数据馈送:{datafeedId}", - "xpack.ml.jobService.datafeedsListCouldNotBeRetrievedErrorMessage": "无法检索数据馈送列表", "xpack.ml.jobService.failedJobsLabel": "失败的作业", - "xpack.ml.jobService.jobsListCouldNotBeRetrievedErrorMessage": "无法检索作业列表", "xpack.ml.jobService.openJobsLabel": "打开的作业", - "xpack.ml.jobService.requestMayHaveTimedOutErrorMessage": "请求可能已超时,并可能仍在后台运行。", "xpack.ml.jobService.totalJobsLabel": "总计作业数", - "xpack.ml.jobService.updateJobErrorTitle": "无法更新作业:{jobId}", "xpack.ml.jobService.validateJobErrorTitle": "作业验证错误", "xpack.ml.jobsList.actionExecuteSuccessfullyNotificationMessage": "{successesJobsCount, plural, one{{successJob}} other{# 个作业}}{actionTextPT}已成功", "xpack.ml.jobsList.actionFailedNotificationMessage": "{failureId} 未能{actionText}", @@ -11579,7 +11559,6 @@ "xpack.ml.maxFileSizeSettingsDescription": "设置在文件数据可视化工具中导入数据时的文件大小限制。此设置支持的最高值为 1GB。", "xpack.ml.maxFileSizeSettingsError": "应为有效的数据大小。如 200MB、1GB", "xpack.ml.maxFileSizeSettingsName": "文件数据可视化工具最大文件上传大小", - "xpack.ml.messagebarService.errorTitle": "发生了错误", "xpack.ml.models.jobService.allOtherRequestsCancelledDescription": " 所有其他请求已取消。", "xpack.ml.models.jobService.categorization.messages.failureToGetTokens": "无法对示例字段值样本进行分词。{message}", "xpack.ml.models.jobService.categorization.messages.insufficientPrivileges": "由于权限不足,无法对字段值示例执行分词。因此,无法检查字段值是否适合用于归类作业。", @@ -13728,7 +13707,6 @@ "xpack.monitoring.updateLicenseTitle": "更新您的许可证", "xpack.monitoring.useAvailableLicenseDescription": "如果已有新的许可证,请立即上传。", "xpack.monitoring.wedLabel": "周三", - "xpack.observability.beta": "公测版", "xpack.observability.emptySection.apps.alert.description": "503 错误是否越来越多?服务是否响应?CPU 和 RAM 利用率是否激增?实时查看警告,而不是事后再进行剖析。", "xpack.observability.emptySection.apps.alert.link": "创建告警", "xpack.observability.emptySection.apps.alert.title": "未找到告警。", @@ -15116,9 +15094,6 @@ "xpack.securitySolution.case.configureCases.incidentManagementSystemDesc": "您可能会根据需要将 Security 案例连接到选择的外部事件管理系统。这将允许您将案例数据作为事件推送到所选第三方系统。", "xpack.securitySolution.case.configureCases.incidentManagementSystemLabel": "事件管理系统", "xpack.securitySolution.case.configureCases.incidentManagementSystemTitle": "连接到外部事件管理系统", - "xpack.securitySolution.case.configureCases.mappingFieldComments": "注释", - "xpack.securitySolution.case.configureCases.mappingFieldDescription": "描述", - "xpack.securitySolution.case.configureCases.mappingFieldName": "名称", "xpack.securitySolution.case.configureCases.mappingFieldNotMapped": "未映射", "xpack.securitySolution.case.configureCases.noConnector": "未选择连接器", "xpack.securitySolution.case.configureCases.updateConnector": "更新连接器", @@ -15132,25 +15107,6 @@ "xpack.securitySolution.case.confirmDeleteCase.deleteCases": "删除案例", "xpack.securitySolution.case.confirmDeleteCase.deleteTitle": "删除“{caseTitle}”", "xpack.securitySolution.case.confirmDeleteCase.selectedCases": "删除选定案例", - "xpack.securitySolution.case.connectors.common.apiTokenTextFieldLabel": "API 令牌", - "xpack.securitySolution.case.connectors.common.apiUrlTextFieldLabel": "URL", - "xpack.securitySolution.case.connectors.common.emailTextFieldLabel": "电子邮件", - "xpack.securitySolution.case.connectors.common.invalidApiUrlTextField": "URL 无效", - "xpack.securitySolution.case.connectors.common.passwordTextFieldLabel": "密码", - "xpack.securitySolution.case.connectors.common.requiredApiTokenTextField": "“API 令牌”必填", - "xpack.securitySolution.case.connectors.common.requiredApiUrlTextField": "“URL”必填", - "xpack.securitySolution.case.connectors.common.requiredEmailTextField": "“电子邮件”必填", - "xpack.securitySolution.case.connectors.common.requiredPasswordTextField": "“密码”必填", - "xpack.securitySolution.case.connectors.common.requiredUsernameTextField": "“用户名”必填", - "xpack.securitySolution.case.connectors.common.usernameTextFieldLabel": "用户名", - "xpack.securitySolution.case.connectors.resilient.actionTypeTitle": "IBM Resilient", - "xpack.securitySolution.case.connectors.resilient.apiKeyId": "API 密钥 ID", - "xpack.securitySolution.case.connectors.resilient.apiKeySecret": "API 密钥密码", - "xpack.securitySolution.case.connectors.resilient.orgId": "组织 ID", - "xpack.securitySolution.case.connectors.resilient.requiredApiKeyIdTextField": "“API 密钥 ID”必填", - "xpack.securitySolution.case.connectors.resilient.requiredApiKeySecretTextField": "“API 密钥密码”必填", - "xpack.securitySolution.case.connectors.resilient.requiredOrgIdTextField": "“组织 ID”必填", - "xpack.securitySolution.case.connectors.resilient.selectMessageText": "将 Security 案例数据推送或更新到 Resilient 中的新问题", "xpack.securitySolution.case.createCase.descriptionFieldRequiredError": "描述必填。", "xpack.securitySolution.case.createCase.fieldTagsHelpText": "为此案例键入一个或多个定制识别标记。在每个标记后按 Enter 键可开始新的标记。", "xpack.securitySolution.case.createCase.titleFieldRequiredError": "标题必填。", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx index be3e8a31820c..8c37dc940a23 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.test.tsx @@ -27,6 +27,7 @@ describe('EmailParamsFields renders', () => { docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} /> ); + expect(wrapper.find('[data-test-subj="toEmailAddressInput"]').length > 0).toBeTruthy(); expect( wrapper.find('[data-test-subj="toEmailAddressInput"]').first().prop('selectedOptions') diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx index 25c04bda3f53..a882e3bc43f3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_params.test.tsx @@ -13,6 +13,7 @@ describe('IndexParamsFields renders', () => { const actionParams = { documents: [{ test: 123 }], }; + const wrapper = mountWithIntl( { group: 'group', class: 'test class', }; + const wrapper = mountWithIntl( > { + return await http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { + body: JSON.stringify({ + params: { subAction: 'incidentTypes', subActionParams: {} }, + }), + signal, + }); +} + +export async function getSeverity({ + http, + signal, + connectorId, +}: { + http: HttpSetup; + signal: AbortSignal; + connectorId: string; +}): Promise> { + return await http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { + body: JSON.stringify({ + params: { subAction: 'severity', subActionParams: {} }, + }), + signal, + }); +} diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/config.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/config.ts similarity index 88% rename from x-pack/plugins/security_solution/public/common/lib/connectors/resilient/config.ts rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/config.ts index 7d4edbf62487..a2054585c19b 100644 --- a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/config.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/config.ts @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ConnectorConfiguration } from './types'; - import * as i18n from './translations'; import logo from './logo.svg'; -export const connector: ConnectorConfiguration = { +export const connectorConfiguration = { id: '.resilient', - name: i18n.RESILIENT_TITLE, + name: i18n.TITLE, logo, enabled: true, enabledInConfig: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts new file mode 100644 index 000000000000..0905bd29493e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { getActionType as getResilientActionType } from './resilient'; diff --git a/x-pack/plugins/security_solution/public/common/lib/connectors/resilient/logo.svg b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.svg similarity index 100% rename from x-pack/plugins/security_solution/public/common/lib/connectors/resilient/logo.svg rename to x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/logo.svg diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx new file mode 100644 index 000000000000..b73eb72f137c --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.test.tsx @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { TypeRegistry } from '../../../type_registry'; +import { registerBuiltInActionTypes } from '.././index'; +import { ActionTypeModel } from '../../../../types'; +import { ResilientActionConnector } from './types'; + +const ACTION_TYPE_ID = '.resilient'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + const actionTypeRegistry = new TypeRegistry(); + registerBuiltInActionTypes({ actionTypeRegistry }); + const getResult = actionTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('action type static data is as expected', () => { + expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); + }); +}); + +describe('resilient connector validation', () => { + test('connector validation succeeds when connector config is valid', () => { + const actionConnector = { + secrets: { + apiKeyId: 'email', + apiKeySecret: 'token', + }, + id: 'test', + actionTypeId: '.resilient', + isPreconfigured: false, + name: 'resilient', + config: { + apiUrl: 'https://test/', + orgId: '201', + }, + } as ResilientActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + apiUrl: [], + apiKeyId: [], + apiKeySecret: [], + orgId: [], + }, + }); + }); + + test('connector validation fails when connector config is not valid', () => { + const actionConnector = ({ + secrets: { + apiKeyId: 'user', + }, + id: '.jira', + actionTypeId: '.jira', + name: 'jira', + config: {}, + } as unknown) as ResilientActionConnector; + + expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ + errors: { + apiUrl: ['URL is required.'], + apiKeyId: [], + apiKeySecret: ['API key secret is required'], + orgId: ['Organization ID is required'], + }, + }); + }); +}); + +describe('resilient action params validation', () => { + test('action params validation succeeds when action params is valid', () => { + const actionParams = { + subActionParams: { title: 'some title {{test}}' }, + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { title: [] }, + }); + }); + + test('params validation fails when body is not valid', () => { + const actionParams = { + subActionParams: { title: '' }, + }; + + expect(actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + title: ['Title is required.'], + }, + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx new file mode 100644 index 000000000000..cda6935f3b73 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { lazy } from 'react'; +import { ValidationResult, ActionTypeModel } from '../../../../types'; +import { connectorConfiguration } from './config'; +import logo from './logo.svg'; +import { ResilientActionConnector, ResilientActionParams } from './types'; +import * as i18n from './translations'; +import { isValidUrl } from '../../../lib/value_validators'; + +const validateConnector = (action: ResilientActionConnector): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + apiUrl: new Array(), + orgId: new Array(), + apiKeyId: new Array(), + apiKeySecret: new Array(), + }; + validationResult.errors = errors; + + if (!action.config.apiUrl) { + errors.apiUrl = [...errors.apiUrl, i18n.API_URL_REQUIRED]; + } + + if (action.config.apiUrl && !isValidUrl(action.config.apiUrl, 'https:')) { + errors.apiUrl = [...errors.apiUrl, i18n.API_URL_INVALID]; + } + + if (!action.config.orgId) { + errors.orgId = [...errors.orgId, i18n.ORG_ID_REQUIRED]; + } + + if (!action.secrets.apiKeyId) { + errors.apiKeyId = [...errors.apiKeyId, i18n.API_KEY_ID_REQUIRED]; + } + + if (!action.secrets.apiKeySecret) { + errors.apiKeySecret = [...errors.apiKeySecret, i18n.API_KEY_SECRET_REQUIRED]; + } + + return validationResult; +}; + +export function getActionType(): ActionTypeModel { + return { + id: connectorConfiguration.id, + iconClass: logo, + selectMessage: i18n.DESC, + actionTypeTitle: connectorConfiguration.name, + validateConnector, + actionConnectorFields: lazy(() => import('./resilient_connectors')), + validateParams: (actionParams: ResilientActionParams): ValidationResult => { + const validationResult = { errors: {} }; + const errors = { + title: new Array(), + }; + validationResult.errors = errors; + if (actionParams.subActionParams && !actionParams.subActionParams.title?.length) { + errors.title.push(i18n.TITLE_REQUIRED); + } + return validationResult; + }, + actionParamsFields: lazy(() => import('./resilient_params')), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx new file mode 100644 index 000000000000..7e242f1f501d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.test.tsx @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { DocLinksStart } from 'kibana/public'; +import ResilientConnectorFields from './resilient_connectors'; +import { ResilientActionConnector } from './types'; + +describe('ResilientActionConnectorFields renders', () => { + test('alerting Resilient connector fields is rendered', () => { + const actionConnector = { + secrets: { + apiKeyId: 'key', + apiKeySecret: 'secret', + }, + id: 'test', + actionTypeId: '.resilient', + isPreconfigured: false, + name: 'resilient', + config: { + apiUrl: 'https://test/', + orgId: '201', + }, + } as ResilientActionConnector; + const deps = { + docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart, + }; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + docLinks={deps!.docLinks} + readOnly={false} + /> + ); + + expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="connector-resilient-orgId-form-input"]').length > 0 + ).toBeTruthy(); + + expect( + wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0 + ).toBeTruthy(); + + expect( + wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0 + ).toBeTruthy(); + }); + + test('case specific Resilient connector fields is rendered', () => { + const actionConnector = { + secrets: { + apiKeyId: 'email', + apiKeySecret: 'token', + }, + id: 'test', + actionTypeId: '.resilient', + isPreconfigured: false, + name: 'resilient', + config: { + apiUrl: 'https://test/', + orgId: '201', + }, + } as ResilientActionConnector; + const deps = { + docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart, + }; + const wrapper = mountWithIntl( + {}} + editActionSecrets={() => {}} + docLinks={deps!.docLinks} + readOnly={false} + consumer={'case'} + /> + ); + + expect(wrapper.find('[data-test-subj="case-resilient-mappings"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="apiUrlFromInput"]').length > 0).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="connector-resilient-orgId-form-input"]').length > 0 + ).toBeTruthy(); + + expect( + wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0 + ).toBeTruthy(); + + expect( + wrapper.find('[data-test-subj="connector-resilient-apiKeySecret-form-input"]').length > 0 + ).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx new file mode 100644 index 000000000000..7965e216f1d6 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_connectors.tsx @@ -0,0 +1,209 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useCallback } from 'react'; + +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldPassword, + EuiSpacer, +} from '@elastic/eui'; + +import { isEmpty } from 'lodash'; +import { ActionConnectorFieldsProps } from '../../../../types'; +import * as i18n from './translations'; +import { ResilientActionConnector } from './types'; +import { connectorConfiguration } from './config'; +import { FieldMapping, CasesConfigurationMapping, createDefaultMapping } from '../case_mappings'; + +const ResilientConnectorFields: React.FC> = ({ + action, + editActionSecrets, + editActionConfig, + errors, + consumer, + readOnly, + docLinks, +}) => { + // TODO: remove incidentConfiguration later, when Case Resilient will move their fields to the level of action execution + const { apiUrl, orgId, incidentConfiguration, isCaseOwned } = action.config; + const mapping = incidentConfiguration ? incidentConfiguration.mapping : []; + + const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; + + const { apiKeyId, apiKeySecret } = action.secrets; + + const isOrgIdInvalid: boolean = errors.orgId.length > 0 && orgId != null; + const isApiKeyInvalid: boolean = errors.apiKeyId.length > 0 && apiKeyId != null; + const isApiKeySecretInvalid: boolean = errors.apiKeySecret.length > 0 && apiKeySecret != null; + + // TODO: remove this block later, when Case ServiceNow will move their fields to the level of action execution + if (consumer === 'case') { + if (isEmpty(mapping)) { + editActionConfig('incidentConfiguration', { + mapping: createDefaultMapping(connectorConfiguration.fields as any), + }); + } + + if (!isCaseOwned) { + editActionConfig('isCaseOwned', true); + } + } + + const handleOnChangeActionConfig = useCallback( + (key: string, value: string) => editActionConfig(key, value), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const handleOnChangeSecretConfig = useCallback( + (key: string, value: string) => editActionSecrets(key, value), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const handleOnChangeMappingConfig = useCallback( + (newMapping: CasesConfigurationMapping[]) => + editActionConfig('incidentConfiguration', { + ...action.config.incidentConfiguration, + mapping: newMapping, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [action.config] + ); + + return ( + <> + + + + handleOnChangeActionConfig('apiUrl', evt.target.value)} + onBlur={() => { + if (!apiUrl) { + editActionConfig('apiUrl', ''); + } + }} + /> + + + + + + + + handleOnChangeActionConfig('orgId', evt.target.value)} + onBlur={() => { + if (!orgId) { + editActionConfig('orgId', ''); + } + }} + /> + + + + + + + + handleOnChangeSecretConfig('apiKeyId', evt.target.value)} + onBlur={() => { + if (!apiKeyId) { + editActionSecrets('apiKeyId', ''); + } + }} + /> + + + + + + + + handleOnChangeSecretConfig('apiKeySecret', evt.target.value)} + onBlur={() => { + if (!apiKeySecret) { + editActionSecrets('apiKeySecret', ''); + } + }} + /> + + + + {consumer === 'case' && ( // TODO: remove this block later, when Case Resilient will move their fields to the level of action execution + <> + + + + + + + + )} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ResilientConnectorFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx new file mode 100644 index 000000000000..17020805757f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.test.tsx @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 ResilientParamsFields from './resilient_params'; +import { DocLinksStart } from 'kibana/public'; + +import { useGetIncidentTypes } from './use_get_incident_types'; +import { useGetSeverity } from './use_get_severity'; + +jest.mock('../../../app_context', () => { + const post = jest.fn(); + return { + useAppDependencies: jest.fn(() => ({ http: { post } })), + }; +}); + +jest.mock('./use_get_incident_types'); +jest.mock('./use_get_severity'); + +const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock; +const useGetSeverityMock = useGetSeverity as jest.Mock; + +const actionParams = { + subAction: 'pushToService', + subActionParams: { + title: 'title', + description: 'some description', + comments: [{ commentId: '1', comment: 'comment for resilient' }], + incidentTypes: [1001], + severityCode: 6, + savedObjectId: '123', + externalId: null, + }, +}; +const connector = { + secrets: {}, + config: {}, + id: 'test', + actionTypeId: '.test', + name: 'Test', + isPreconfigured: false, +}; + +describe('ResilientParamsFields renders', () => { + const useGetIncidentTypesResponse = { + isLoading: false, + incidentTypes: [ + { + id: 19, + name: 'Malware', + }, + { + id: 21, + name: 'Denial of Service', + }, + ], + }; + + const useGetSeverityResponse = { + isLoading: false, + severity: [ + { + id: 4, + name: 'Low', + }, + { + id: 5, + name: 'Medium', + }, + { + id: 6, + name: 'High', + }, + ], + }; + + beforeEach(() => { + useGetIncidentTypesMock.mockReturnValue(useGetIncidentTypesResponse); + useGetSeverityMock.mockReturnValue(useGetSeverityResponse); + }); + + test('all params fields are rendered', () => { + const wrapper = mountWithIntl( + {}} + index={0} + messageVariables={[]} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + actionConnector={connector} + /> + ); + expect(wrapper.find('[data-test-subj="incidentTypeComboBox"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('value')).toStrictEqual( + 6 + ); + expect(wrapper.find('[data-test-subj="titleInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="descriptionTextArea"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="commentsTextArea"]').length > 0).toBeTruthy(); + }); + + test('it shows loading when loading incident types', () => { + useGetIncidentTypesMock.mockReturnValue({ ...useGetIncidentTypesResponse, isLoading: true }); + const wrapper = mountWithIntl( + {}} + index={0} + messageVariables={[]} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + actionConnector={connector} + /> + ); + + expect( + wrapper.find('[data-test-subj="incidentTypeComboBox"]').first().prop('isLoading') + ).toBeTruthy(); + }); + + test('it shows loading when loading severity', () => { + useGetSeverityMock.mockReturnValue({ + ...useGetSeverityResponse, + isLoading: true, + }); + + const wrapper = mountWithIntl( + {}} + index={0} + messageVariables={[]} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + actionConnector={connector} + /> + ); + + expect( + wrapper.find('[data-test-subj="severitySelect"]').first().prop('isLoading') + ).toBeTruthy(); + }); + + test('it disabled the fields when loading issue types', () => { + useGetIncidentTypesMock.mockReturnValue({ ...useGetIncidentTypesResponse, isLoading: true }); + + const wrapper = mountWithIntl( + {}} + index={0} + messageVariables={[]} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + actionConnector={connector} + /> + ); + + expect( + wrapper.find('[data-test-subj="incidentTypeComboBox"]').first().prop('isDisabled') + ).toBeTruthy(); + }); + + test('it disabled the fields when loading severity', () => { + useGetSeverityMock.mockReturnValue({ + ...useGetSeverityResponse, + isLoading: true, + }); + + const wrapper = mountWithIntl( + {}} + index={0} + messageVariables={[]} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + actionConnector={connector} + /> + ); + + expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('disabled')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx new file mode 100644 index 000000000000..4b157c699998 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient_params.tsx @@ -0,0 +1,256 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, useEffect, useState } from 'react'; +import { + EuiFormRow, + EuiComboBox, + EuiSelect, + EuiSpacer, + EuiTitle, + EuiComboBoxOptionOption, + EuiSelectOption, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { ActionParamsProps } from '../../../../types'; +import { useAppDependencies } from '../../../app_context'; +import { ResilientActionParams } from './types'; +import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; + +import { useGetIncidentTypes } from './use_get_incident_types'; +import { useGetSeverity } from './use_get_severity'; + +const ResilientParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + errors, + messageVariables, + actionConnector, +}) => { + const [firstLoad, setFirstLoad] = useState(false); + const { http, toastNotifications } = useAppDependencies(); + const { title, description, comments, incidentTypes, severityCode, savedObjectId } = + actionParams.subActionParams || {}; + + const [incidentTypesComboBoxOptions, setIncidentTypesComboBoxOptions] = useState< + Array> + >([]); + + const [selectedIncidentTypesComboBoxOptions, setSelectedIncidentTypesComboBoxOptions] = useState< + Array> + >([]); + + const [severitySelectOptions, setSeveritySelectOptions] = useState([]); + + useEffect(() => { + setFirstLoad(true); + }, []); + + const { + isLoading: isLoadingIncidentTypes, + incidentTypes: allIncidentTypes, + } = useGetIncidentTypes({ + http, + toastNotifications, + actionConnector, + }); + + const { isLoading: isLoadingSeverity, severity } = useGetSeverity({ + http, + toastNotifications, + actionConnector, + }); + + const editSubActionProperty = (key: string, value: {}) => { + const newProps = { ...actionParams.subActionParams, [key]: value }; + editAction('subActionParams', newProps, index); + }; + + useEffect(() => { + const options = severity.map((s) => ({ + value: s.id.toString(), + text: s.name, + })); + + setSeveritySelectOptions(options); + }, [actionConnector, severity]); + + // Reset parameters when changing connector + useEffect(() => { + if (!firstLoad) { + return; + } + + setIncidentTypesComboBoxOptions([]); + setSelectedIncidentTypesComboBoxOptions([]); + setSeveritySelectOptions([]); + editAction('subActionParams', { title, comments, description: '', savedObjectId }, index); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionConnector]); + + useEffect(() => { + if (!actionParams.subAction) { + editAction('subAction', 'pushToService', index); + } + if (!savedObjectId && messageVariables?.find((variable) => variable.name === 'alertId')) { + editSubActionProperty('savedObjectId', '{{alertId}}'); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionConnector, savedObjectId]); + + useEffect(() => { + setIncidentTypesComboBoxOptions( + allIncidentTypes + ? allIncidentTypes.map((type: { id: number; name: string }) => ({ + label: type.name, + value: type.id.toString(), + })) + : [] + ); + + const allIncidentTypesAsObject = allIncidentTypes.reduce( + (acc, type) => ({ ...acc, [type.id.toString()]: type.name }), + {} as Record + ); + + setSelectedIncidentTypesComboBoxOptions( + incidentTypes + ? incidentTypes + .map((type) => ({ + label: allIncidentTypesAsObject[type.toString()], + value: type.toString(), + })) + .filter((type) => type.label != null) + : [] + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionConnector, allIncidentTypes]); + + return ( + + +

Incident

+
+ + + ) => { + setSelectedIncidentTypesComboBoxOptions( + selectedOptions.map((selectedOption) => ({ + label: selectedOption.label, + value: selectedOption.value, + })) + ); + + editSubActionProperty( + 'incidentTypes', + selectedOptions.map((selectedOption) => selectedOption.value ?? selectedOption.label) + ); + }} + onBlur={() => { + if (!incidentTypes) { + editSubActionProperty('incidentTypes', []); + } + }} + isClearable={true} + /> + + + + { + editSubActionProperty('severityCode', e.target.value); + }} + /> + + + 0 && title !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.titleFieldLabel', + { + defaultMessage: 'Name', + } + )} + > + + + + { + editSubActionProperty(key, [{ commentId: 'alert-comment', comment: value }]); + }} + messageVariables={messageVariables} + paramsProperty={'comments'} + inputTargetValue={comments && comments.length > 0 ? comments[0].comment : ''} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.commentsTextAreaFieldLabel', + { + defaultMessage: 'Additional comments (optional)', + } + )} + errors={errors.comments as string[]} + /> +
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export { ResilientParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts new file mode 100644 index 000000000000..71ad05abfdec --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/translations.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const DESC = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.selectMessageText', + { + defaultMessage: 'Push or update data to a new incident in Resilient.', + } +); + +export const TITLE = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.actionTypeTitle', + { + defaultMessage: 'Resilient', + } +); + +export const API_URL_LABEL = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiUrlTextFieldLabel', + { + defaultMessage: 'URL', + } +); + +export const API_URL_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiUrlTextField', + { + defaultMessage: 'URL is required.', + } +); + +export const API_URL_INVALID = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.invalidApiUrlTextField', + { + defaultMessage: 'URL is invalid.', + } +); + +export const ORG_ID_LABEL = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.orgId', + { + defaultMessage: 'Organization ID', + } +); + +export const ORG_ID_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredOrgIdTextField', + { + defaultMessage: 'Organization ID is required', + } +); + +export const API_KEY_ID_LABEL = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeyId', + { + defaultMessage: 'API key ID', + } +); + +export const API_KEY_ID_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeyIdTextField', + { + defaultMessage: 'API key ID is required', + } +); + +export const API_KEY_SECRET_LABEL = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.apiKeySecret', + { + defaultMessage: 'API key secret', + } +); + +export const API_KEY_SECRET_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.requiredApiKeySecretTextField', + { + defaultMessage: 'API key secret is required', + } +); + +export const MAPPING_FIELD_NAME = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldShortDescription', + { + defaultMessage: 'Name', + } +); + +export const MAPPING_FIELD_DESC = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldDescription', + { + defaultMessage: 'Description', + } +); + +export const MAPPING_FIELD_COMMENTS = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.mappingFieldComments', + { + defaultMessage: 'Comments', + } +); + +export const DESCRIPTION_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.common.requiredDescriptionTextField', + { + defaultMessage: 'Description is required.', + } +); + +export const TITLE_REQUIRED = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.common.requiredTitleTextField', + { + defaultMessage: 'Title is required.', + } +); + +export const INCIDENT_TYPES_API_ERROR = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetIncidentTypesMessage', + { + defaultMessage: 'Unable to get incident types', + } +); + +export const SEVERITY_API_ERROR = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.resilient.unableToGetSeverityMessage', + { + defaultMessage: 'Unable to get severity', + } +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts new file mode 100644 index 000000000000..37516f5bac37 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CasesConfigurationMapping } from '../case_mappings'; + +export interface ResilientActionConnector { + config: ResilientConfig; + secrets: ResilientSecrets; +} + +export interface ResilientActionParams { + subAction: string; + subActionParams: { + savedObjectId: string; + title: string; + description: string; + externalId: string | null; + incidentTypes: number[]; + severityCode: number; + comments: Array<{ commentId: string; comment: string }>; + }; +} + +interface IncidentConfiguration { + mapping: CasesConfigurationMapping[]; +} + +interface ResilientConfig { + apiUrl: string; + orgId: string; + incidentConfiguration?: IncidentConfiguration; + isCaseOwned?: boolean; +} + +interface ResilientSecrets { + apiKeyId: string; + apiKeySecret: string; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx new file mode 100644 index 000000000000..219c6ac77d08 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx @@ -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 { useState, useEffect, useRef } from 'react'; +import { HttpSetup, ToastsApi } from 'kibana/public'; +import { ActionConnector } from '../../../../types'; +import { getIncidentTypes } from './api'; +import * as i18n from './translations'; + +type IncidentTypes = Array<{ id: number; name: string }>; + +interface Props { + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + actionConnector?: ActionConnector; +} + +export interface UseGetIncidentTypes { + incidentTypes: IncidentTypes; + isLoading: boolean; +} + +export const useGetIncidentTypes = ({ + http, + toastNotifications, + actionConnector, +}: Props): UseGetIncidentTypes => { + const [isLoading, setIsLoading] = useState(true); + const [incidentTypes, setIncidentTypes] = useState([]); + const abortCtrl = useRef(new AbortController()); + + useEffect(() => { + let didCancel = false; + const fetchData = async () => { + if (!actionConnector) { + setIsLoading(false); + return; + } + + abortCtrl.current = new AbortController(); + setIsLoading(true); + + try { + const res = await getIncidentTypes({ + http, + signal: abortCtrl.current.signal, + connectorId: actionConnector.id, + }); + + if (!didCancel) { + setIsLoading(false); + setIncidentTypes(res.data ?? []); + if (res.status && res.status === 'error') { + toastNotifications.addDanger({ + title: i18n.INCIDENT_TYPES_API_ERROR, + text: `${res.serviceMessage ?? res.message}`, + }); + } + } + } catch (error) { + if (!didCancel) { + toastNotifications.addDanger({ + title: i18n.INCIDENT_TYPES_API_ERROR, + text: error.message, + }); + } + } + }; + + abortCtrl.current.abort(); + fetchData(); + + return () => { + didCancel = true; + setIsLoading(false); + abortCtrl.current.abort(); + }; + }, [http, actionConnector, toastNotifications]); + + return { + incidentTypes, + isLoading, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx new file mode 100644 index 000000000000..83689254f000 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useState, useEffect, useRef } from 'react'; +import { HttpSetup, ToastsApi } from 'kibana/public'; +import { ActionConnector } from '../../../../types'; +import { getSeverity } from './api'; +import * as i18n from './translations'; + +type Severity = Array<{ id: number; name: string }>; + +interface Props { + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + actionConnector?: ActionConnector; +} + +export interface UseGetSeverity { + severity: Severity; + isLoading: boolean; +} + +export const useGetSeverity = ({ + http, + toastNotifications, + actionConnector, +}: Props): UseGetSeverity => { + const [isLoading, setIsLoading] = useState(true); + const [severity, setSeverity] = useState([]); + const abortCtrl = useRef(new AbortController()); + + useEffect(() => { + let didCancel = false; + const fetchData = async () => { + if (!actionConnector) { + setIsLoading(false); + return; + } + + abortCtrl.current = new AbortController(); + setIsLoading(true); + + try { + const res = await getSeverity({ + http, + signal: abortCtrl.current.signal, + connectorId: actionConnector.id, + }); + + if (!didCancel) { + setIsLoading(false); + setSeverity(res.data ?? []); + + if (res.status && res.status === 'error') { + toastNotifications.addDanger({ + title: i18n.SEVERITY_API_ERROR, + text: `${res.serviceMessage ?? res.message}`, + }); + } + } + } catch (error) { + if (!didCancel) { + toastNotifications.addDanger({ + title: i18n.SEVERITY_API_ERROR, + text: error.message, + }); + } + } + }; + + abortCtrl.current.abort(); + fetchData(); + + return () => { + didCancel = true; + setIsLoading(false); + abortCtrl.current.abort(); + }; + }, [http, actionConnector, toastNotifications]); + + return { + severity, + isLoading, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_params.test.tsx index 1fc856b1e1ab..f4d831d7234e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_params.test.tsx @@ -23,6 +23,7 @@ describe('ServiceNowParamsFields renders', () => { externalId: null, }, }; + const wrapper = mountWithIntl( { const actionParams = { message: 'test message', }; + const wrapper = mountWithIntl( { const actionParams = { body: 'test message', }; + const wrapper = mountWithIntl( { acc[next.id] = next; @@ -161,6 +163,7 @@ export function createFlyoutManageDrilldowns({ return ( ; + /** + * General overview of drilldowns + */ docsLink?: string; + /** + * Link that explains different triggers + */ + triggerPickerDocsLink?: string; + getTrigger: (triggerId: TriggerId) => Trigger; /** @@ -145,6 +153,7 @@ export function FlyoutDrilldownWizard) { @@ -217,7 +226,7 @@ export function FlyoutDrilldownWizard {mode === 'edit' && ( <> diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx index dabf09e4b6e9..bd0191443d78 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.tsx @@ -41,6 +41,7 @@ export interface UrlDrilldownCollectConfig { onConfig: (newConfig: UrlDrilldownConfig) => void; scope: UrlDrilldownScope; syntaxHelpDocsLink?: string; + variablesHelpDocsLink?: string; } export const UrlDrilldownCollectConfig: React.FC = ({ @@ -48,6 +49,7 @@ export const UrlDrilldownCollectConfig: React.FC = ({ onConfig, scope, syntaxHelpDocsLink, + variablesHelpDocsLink, }) => { const textAreaRef = useRef(null); const urlTemplate = config.url.template ?? ''; @@ -95,7 +97,7 @@ export const UrlDrilldownCollectConfig: React.FC = ({ labelAppend={ { if (textAreaRef.current) { updateUrlTemplate( diff --git a/x-pack/plugins/ui_actions_enhanced/public/plugin.ts b/x-pack/plugins/ui_actions_enhanced/public/plugin.ts index 015531aab974..b38bc44abe2b 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/plugin.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/plugin.ts @@ -132,6 +132,7 @@ export class AdvancedUiActionsPublicPlugin storage: new Storage(window?.localStorage), toastService: core.notifications.toasts, docsLink: core.docLinks.links.dashboard.drilldowns, + triggerPickerDocsLink: core.docLinks.links.dashboard.drilldownsTriggerPicker, }), }; } diff --git a/x-pack/plugins/upgrade_assistant/kibana.json b/x-pack/plugins/upgrade_assistant/kibana.json index 273036a653ae..c4c6f23611f2 100644 --- a/x-pack/plugins/upgrade_assistant/kibana.json +++ b/x-pack/plugins/upgrade_assistant/kibana.json @@ -4,6 +4,6 @@ "server": true, "ui": true, "configPath": ["xpack", "upgrade_assistant"], - "requiredPlugins": ["management", "licensing"], + "requiredPlugins": ["management", "licensing", "features"], "optionalPlugins": ["cloud", "usageCollection"] } diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index 0cdf1ca05fea..9ef0f250da8e 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -16,6 +16,7 @@ import { } from '../../../../src/core/server'; import { CloudSetup } from '../../cloud/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { CredentialStore, credentialStoreFactory } from './lib/reindexing/credential_store'; @@ -32,6 +33,7 @@ import { RouteDependencies } from './types'; interface PluginsSetup { usageCollection: UsageCollectionSetup; licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; cloud?: CloudSetup; } @@ -60,13 +62,26 @@ export class UpgradeAssistantServerPlugin implements Plugin { setup( { http, getStartServices, capabilities, savedObjects }: CoreSetup, - { usageCollection, cloud, licensing }: PluginsSetup + { usageCollection, cloud, features, licensing }: PluginsSetup ) { this.licensing = licensing; savedObjects.registerType(reindexOperationSavedObjectType); savedObjects.registerType(telemetrySavedObjectType); + features.registerElasticsearchFeature({ + id: 'upgrade_assistant', + management: { + stack: ['upgrade_assistant'], + }, + privileges: [ + { + requiredClusterPrivileges: ['manage'], + ui: [], + }, + ], + }); + const router = http.createRouter(); const dependencies: RouteDependencies = { diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts index 76359a3b60a6..5c3211eff3b4 100644 --- a/x-pack/plugins/uptime/server/kibana.index.ts +++ b/x-pack/plugins/uptime/server/kibana.index.ts @@ -27,7 +27,7 @@ export const initServerWithKibana = (server: UptimeCoreSetup, plugins: UptimeCor const { features } = plugins; const libs = compose(server); - features.registerFeature({ + features.registerKibanaFeature({ id: PLUGIN.ID, name: PLUGIN.NAME, order: 1000, diff --git a/x-pack/plugins/watcher/kibana.json b/x-pack/plugins/watcher/kibana.json index ba6a9bfa5e19..695686715cb6 100644 --- a/x-pack/plugins/watcher/kibana.json +++ b/x-pack/plugins/watcher/kibana.json @@ -7,7 +7,8 @@ "licensing", "management", "charts", - "data" + "data", + "features" ], "server": true, "ui": true, diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx index 2ff0f53d07e9..935f0209e73c 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx @@ -126,7 +126,7 @@ export const WatchVisualization = () => { isLoading, data: watchVisualizationData, error, - sendRequest: reload, + resendRequest: reload, } = useGetWatchVisualizationData(watchWithoutActions, visualizeOptions); useEffect( diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index 70c4f980580e..9ff46283a72a 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -18,7 +18,7 @@ import { Plugin, PluginInitializerContext, } from 'kibana/server'; -import { PLUGIN } from '../common/constants'; +import { PLUGIN, INDEX_NAMES } from '../common/constants'; import { Dependencies, LicenseStatus, RouteDependencies } from './types'; import { registerSettingsRoutes } from './routes/api/settings'; @@ -52,13 +52,39 @@ export class WatcherServerPlugin implements Plugin { this.log = ctx.logger.get(); } - async setup({ http, getStartServices }: CoreSetup, { licensing }: Dependencies) { + async setup({ http, getStartServices }: CoreSetup, { licensing, features }: Dependencies) { const router = http.createRouter(); const routeDependencies: RouteDependencies = { router, getLicenseStatus: () => this.licenseStatus, }; + features.registerElasticsearchFeature({ + id: 'watcher', + management: { + insightsAndAlerting: ['watcher'], + }, + catalogue: ['watcher'], + privileges: [ + { + requiredClusterPrivileges: ['manage_watcher'], + requiredIndexPrivileges: { + [INDEX_NAMES.WATCHES]: ['read'], + [INDEX_NAMES.WATCHER_HISTORY]: ['read'], + }, + ui: [], + }, + { + requiredClusterPrivileges: ['monitor_watcher'], + requiredIndexPrivileges: { + [INDEX_NAMES.WATCHES]: ['read'], + [INDEX_NAMES.WATCHER_HISTORY]: ['read'], + }, + ui: [], + }, + ], + }); + http.registerRouteHandlerContext('watcher', async (ctx, request) => { this.watcherESClient = this.watcherESClient ?? (await getCustomEsClient(getStartServices)); return { diff --git a/x-pack/plugins/watcher/server/types.ts b/x-pack/plugins/watcher/server/types.ts index dd941054114a..167dcb3ab64c 100644 --- a/x-pack/plugins/watcher/server/types.ts +++ b/x-pack/plugins/watcher/server/types.ts @@ -5,12 +5,14 @@ */ import { IRouter } from 'kibana/server'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { XPackMainPlugin } from '../../../legacy/plugins/xpack_main/server/xpack_main'; export interface Dependencies { licensing: LicensingPluginSetup; + features: FeaturesPluginSetup; } export interface ServerShim { diff --git a/x-pack/run_functional_tests.sh b/x-pack/run_functional_tests.sh deleted file mode 100755 index e94f283ea039..000000000000 --- a/x-pack/run_functional_tests.sh +++ /dev/null @@ -1,3 +0,0 @@ -export TEST_KIBANA_URL="http://elastic:mlqa_admin@localhost:5601" -export TEST_ES_URL="http://elastic:mlqa_admin@localhost:9200" -node ../scripts/functional_test_runner --include-tag walterra diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts new file mode 100644 index 000000000000..576ed4bbc5df --- /dev/null +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/builtin_action_types/resilient.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +import { + getExternalServiceSimulatorPath, + ExternalServiceSimulator, +} from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +const mapping = [ + { + source: 'title', + target: 'description', + actionType: 'nothing', + }, + { + source: 'description', + target: 'short_description', + actionType: 'nothing', + }, + { + source: 'comments', + target: 'comments', + actionType: 'nothing', + }, +]; + +// eslint-disable-next-line import/no-default-export +export default function resilientTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const mockResilient = { + config: { + apiUrl: 'www.resilientisinkibanaactions.com', + orgId: '201', + incidentConfiguration: { mapping: [...mapping] }, + isCaseOwned: true, + }, + secrets: { + apiKeyId: 'elastic', + apiKeySecret: 'changeme', + }, + params: { + savedObjectId: '123', + title: 'a title', + description: 'a description', + incidentTypes: [1001], + severityCode: 'High', + comments: [ + { + commentId: '456', + comment: 'first comment', + }, + ], + }, + }; + describe('resilient', () => { + let resilientSimulatorURL: string = ''; + + // need to wait for kibanaServer to settle ... + before(() => { + resilientSimulatorURL = kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.RESILIENT) + ); + }); + + it('should return 403 when creating a resilient action', async () => { + await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A resilient action', + actionTypeId: '.resilient', + config: { + apiUrl: resilientSimulatorURL, + incidentConfiguration: { ...mockResilient.config.incidentConfiguration }, + isCaseOwned: true, + }, + secrets: mockResilient.secrets, + }) + .expect(403, { + statusCode: 403, + error: 'Forbidden', + message: + 'Action type .resilient is disabled because your basic license does not support it. Please upgrade your license.', + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts index 88f0f02794c9..68ff3dad9ae8 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/plugin.ts @@ -72,7 +72,7 @@ export class FixturePlugin implements Plugin { public setup(core: CoreSetup, { features, actions, alerts }: FixtureSetupDeps) { - features.registerFeature({ + features.registerKibanaFeature({ id: 'alertsFixture', name: 'Alerts', app: ['alerts', 'kibana'], diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/plugin.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/plugin.ts index e297733fb47e..e1ef1255c6e1 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/plugin.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/plugin.ts @@ -23,7 +23,7 @@ export interface FixtureStartDeps { export class FixturePlugin implements Plugin { public setup(core: CoreSetup, { features, alerts }: FixtureSetupDeps) { - features.registerFeature({ + features.registerKibanaFeature({ id: 'alertsRestrictedFixture', name: 'AlertRestricted', app: ['alerts', 'kibana'], diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts index 9cbc2373ef94..d1d19da423e6 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts @@ -41,9 +41,10 @@ export default function resilientTest({ getService }: FtrProviderContext) { const mockResilient = { config: { - apiUrl: 'www.jiraisinkibanaactions.com', + apiUrl: 'www.resilientisinkibanaactions.com', orgId: '201', - casesConfiguration: { mapping }, + incidentConfiguration: { mapping }, + isCaseOwned: true, }, secrets: { apiKeyId: 'key', @@ -55,6 +56,8 @@ export default function resilientTest({ getService }: FtrProviderContext) { savedObjectId: '123', title: 'a title', description: 'a description', + incidentTypes: [1001], + severityCode: 6, createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, updatedAt: null, @@ -108,7 +111,8 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: mockResilient.config.casesConfiguration, + incidentConfiguration: mockResilient.config.incidentConfiguration, + isCaseOwned: true, }, }); @@ -124,7 +128,8 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: mockResilient.config.casesConfiguration, + incidentConfiguration: mockResilient.config.incidentConfiguration, + isCaseOwned: true, }, }); }); @@ -179,7 +184,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: 'http://resilient.mynonexistent.com', orgId: mockResilient.config.orgId, - casesConfiguration: mockResilient.config.casesConfiguration, + incidentConfiguration: mockResilient.config.incidentConfiguration, }, secrets: mockResilient.secrets, }) @@ -204,7 +209,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: mockResilient.config.casesConfiguration, + incidentConfiguration: mockResilient.config.incidentConfiguration, }, }) .expect(400) @@ -218,30 +223,6 @@ export default function resilientTest({ getService }: FtrProviderContext) { }); }); - it('should respond with a 400 Bad Request when creating a ibm resilient action without casesConfiguration', async () => { - await supertest - .post('/api/actions/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'An IBM Resilient', - actionTypeId: '.resilient', - config: { - apiUrl: resilientSimulatorURL, - orgId: mockResilient.config.orgId, - }, - secrets: mockResilient.secrets, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [casesConfiguration.mapping]: expected value of type [array] but got [undefined]', - }); - }); - }); - it('should respond with a 400 Bad Request when creating a ibm resilient action with empty mapping', async () => { await supertest .post('/api/actions/action') @@ -252,7 +233,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: { mapping: [] }, + incidentConfiguration: { mapping: [] }, }, secrets: mockResilient.secrets, }) @@ -262,7 +243,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type config: [casesConfiguration.mapping]: expected non-empty but got empty', + 'error validating action type config: [incidentConfiguration.mapping]: expected non-empty but got empty', }); }); }); @@ -277,7 +258,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: { + incidentConfiguration: { mapping: [ { source: 'title', @@ -307,7 +288,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { config: { apiUrl: resilientSimulatorURL, orgId: mockResilient.config.orgId, - casesConfiguration: mockResilient.config.casesConfiguration, + incidentConfiguration: mockResilient.config.incidentConfiguration, }, secrets: mockResilient.secrets, }); @@ -353,7 +334,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -371,7 +352,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -389,7 +370,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.savedObjectId]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -412,31 +393,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]', - }); - }); - }); - - it('should handle failing with a simulated success without createdAt', async () => { - await supertest - .post(`/api/actions/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockResilient.params, - subActionParams: { - savedObjectId: 'success', - title: 'success', - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.createdAt]: expected value of type [string] but got [undefined]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -464,7 +421,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -492,35 +449,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: false, message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', - }); - }); - }); - - it('should handle failing with a simulated success without comment.createdAt', async () => { - await supertest - .post(`/api/actions/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - ...mockResilient.params, - subActionParams: { - ...mockResilient.params.subActionParams, - savedObjectId: 'success', - title: 'success', - createdAt: 'success', - createdBy: { username: 'elastic' }, - comments: [{ commentId: 'success', comment: 'success' }], - }, - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.createdAt]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]\n- [3.subAction]: expected value to equal [incidentTypes]\n- [4.subAction]: expected value to equal [severity]', }); }); }); @@ -536,7 +465,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { ...mockResilient.params, subActionParams: { ...mockResilient.params.subActionParams, - comments: [], + comments: null, }, }, }) diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index 9c44bfeb810f..37809a3b7aeb 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; -import { Feature } from '../../../../../plugins/features/server'; +import { KibanaFeature } from '../../../../../plugins/features/server'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -90,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) { expect(body).to.be.an(Array); - const featureIds = body.map((b: Feature) => b.id); + const featureIds = body.map((b: KibanaFeature) => b.id); expect(featureIds.sort()).to.eql( [ 'discover', diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts index c6043b7a282d..53a9d9e790d6 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts @@ -120,7 +120,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(404); expect(body.error).to.eql('Not Found'); - expect(body.message).to.eql('Not Found'); + expect(body.message).to.eql('resource_not_found_exception'); }); describe('with deleteDestIndex setting', function () { diff --git a/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts new file mode 100644 index 000000000000..c48376b6a14f --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/jobs/jobs_exist.ts @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { SINGLE_METRIC_JOB_CONFIG, DATAFEED_CONFIG } from './common_jobs'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + const testSetupJobConfigs = [SINGLE_METRIC_JOB_CONFIG]; + + const responseBody = { + [SINGLE_METRIC_JOB_CONFIG.job_id]: true, + [`${SINGLE_METRIC_JOB_CONFIG.job_id.slice(0, 10)}*`]: true, // wildcard, use first 10 chars + [`${SINGLE_METRIC_JOB_CONFIG.job_id}_fail`]: false, + [`${SINGLE_METRIC_JOB_CONFIG.job_id.slice(0, 10)}_fail*`]: false, // wildcard, use first 10 chars + }; + + const testDataList = [ + { + testTitle: 'as ML Poweruser', + user: USER.ML_POWERUSER, + requestBody: { + jobIds: Object.keys(responseBody), + }, + expected: { + responseCode: 200, + responseBody, + }, + }, + { + testTitle: 'as ML Viewer', + user: USER.ML_VIEWER, + requestBody: { + jobIds: Object.keys(responseBody), + }, + expected: { + responseCode: 200, + responseBody, + }, + }, + ]; + + const testDataListUnauthorized = [ + { + testTitle: 'as ML Unauthorized user', + user: USER.ML_UNAUTHORIZED, + requestBody: { + jobIds: Object.keys(responseBody), + }, + expected: { + responseCode: 404, + error: 'Not Found', + }, + }, + ]; + + async function runJobsExistRequest( + user: USER, + requestBody: object, + expectedResponsecode: number + ): Promise { + const { body } = await supertest + .post('/api/ml/jobs/jobs_exist') + .auth(user, ml.securityCommon.getPasswordForUser(user)) + .set(COMMON_REQUEST_HEADERS) + .send(requestBody) + .expect(expectedResponsecode); + + return body; + } + + describe('jobs_exist', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.setKibanaTimeZoneToUTC(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('sets up jobs', async () => { + for (const job of testSetupJobConfigs) { + const datafeedId = `datafeed-${job.job_id}`; + await ml.api.createAnomalyDetectionJob(job); + await ml.api.openAnomalyDetectionJob(job.job_id); + await ml.api.createDatafeed({ + ...DATAFEED_CONFIG, + datafeed_id: datafeedId, + job_id: job.job_id, + }); + } + }); + + describe('jobs exist', function () { + for (const testData of testDataList) { + it(`${testData.testTitle}`, async () => { + const body = await runJobsExistRequest( + testData.user, + testData.requestBody, + testData.expected.responseCode + ); + const expectedResponse = testData.expected.responseBody; + const expectedRspJobIds = Object.keys(expectedResponse).sort((a, b) => + a.localeCompare(b) + ); + const actualRspJobIds = Object.keys(body).sort((a, b) => a.localeCompare(b)); + + expect(actualRspJobIds).to.have.length(expectedRspJobIds.length); + expect(actualRspJobIds).to.eql(expectedRspJobIds); + expectedRspJobIds.forEach((id) => { + expect(body[id]).to.eql(testData.expected.responseBody[id]); + }); + }); + } + }); + + describe('rejects request', function () { + for (const testData of testDataListUnauthorized) { + describe('fails to check jobs exist', function () { + it(`${testData.testTitle}`, async () => { + const body = await runJobsExistRequest( + testData.user, + testData.requestBody, + testData.expected.responseCode + ); + + expect(body).to.have.property('error').eql(testData.expected.error); + }); + }); + } + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json index b7c3aee5471d..a000324d121e 100644 --- a/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json +++ b/x-pack/test/api_integration/apis/monitoring/cluster/fixtures/multicluster.json @@ -102,7 +102,7 @@ }, "alerts": { "alertsMeta": { - "enabled": true + "enabled": false }, "clusterMeta": { "enabled": false, diff --git a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json index f93847957880..7091e584344e 100644 --- a/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json +++ b/x-pack/test/api_integration/apis/monitoring/standalone_cluster/fixtures/clusters.json @@ -102,7 +102,7 @@ }, "alerts": { "alertsMeta": { - "enabled": true + "enabled": false }, "clusterMeta": { "enabled": false, @@ -170,7 +170,7 @@ }, "alerts": { "alertsMeta": { - "enabled": true + "enabled": false }, "clusterMeta": { "enabled": false, diff --git a/x-pack/test/api_integration/apis/transform/common.ts b/x-pack/test/api_integration/apis/transform/common.ts new file mode 100644 index 000000000000..1a48ee987bc7 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/common.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import type { PutTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/transforms'; + +export async function asyncForEach(array: any[], callback: Function) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +} + +export function generateDestIndex(transformId: string): string { + return `user-${transformId}`; +} + +export function generateTransformConfig(transformId: string): PutTransformsRequestSchema { + const destinationIndex = generateDestIndex(transformId); + + return { + source: { index: ['ft_farequote'] }, + pivot: { + group_by: { airline: { terms: { field: 'airline' } } }, + aggregations: { '@timestamp.value_count': { value_count: { field: '@timestamp' } } }, + }, + dest: { index: destinationIndex }, + }; +} diff --git a/x-pack/test/api_integration/apis/transform/delete_transforms.ts b/x-pack/test/api_integration/apis/transform/delete_transforms.ts index 7f01d2741ad1..41b2bffb1f0a 100644 --- a/x-pack/test/api_integration/apis/transform/delete_transforms.ts +++ b/x-pack/test/api_integration/apis/transform/delete_transforms.ts @@ -4,41 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { TransformEndpointRequest } from '../../../../plugins/transform/common'; -import { FtrProviderContext } from '../../ftr_provider_context'; + +import { DeleteTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/delete_transforms'; +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; import { USER } from '../../../functional/services/transform/security_common'; -async function asyncForEach(array: any[], callback: Function) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array); - } -} +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { asyncForEach, generateDestIndex, generateTransformConfig } from './common'; export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const supertest = getService('supertestWithoutAuth'); const transform = getService('transform'); - function generateDestIndex(transformId: string): string { - return `user-${transformId}`; + async function createTransform(transformId: string) { + const config = generateTransformConfig(transformId); + await transform.api.createTransform(transformId, config); } - async function createTransform(transformId: string, destinationIndex: string) { - const config = { - id: transformId, - source: { index: ['farequote-*'] }, - pivot: { - group_by: { airline: { terms: { field: 'airline' } } }, - aggregations: { '@timestamp.value_count': { value_count: { field: '@timestamp' } } }, - }, - dest: { index: destinationIndex }, - }; - - await transform.api.createTransform(config); - } - - describe('delete_transforms', function () { + describe('/api/transform/delete_transforms', function () { before(async () => { await esArchiver.loadIfNeeded('ml/farequote'); await transform.testResources.setKibanaTimeZoneToUTC(); @@ -49,11 +36,11 @@ export default ({ getService }: FtrProviderContext) => { }); describe('single transform deletion', function () { - const transformId = 'test1'; + const transformId = 'transform-test-delete'; const destinationIndex = generateDestIndex(transformId); beforeEach(async () => { - await createTransform(transformId, destinationIndex); + await createTransform(transformId); await transform.api.createIndices(destinationIndex); }); @@ -62,7 +49,9 @@ export default ({ getService }: FtrProviderContext) => { }); it('should delete transform by transformId', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: transformId }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: transformId, state: TRANSFORM_STATE.STOPPED }], + }; const { body } = await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -70,9 +59,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - }) + .send(reqBody) .expect(200); expect(body[transformId].transformDeleted.success).to.eql(true); @@ -83,7 +70,9 @@ export default ({ getService }: FtrProviderContext) => { }); it('should return 403 for unauthorized user', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: transformId }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: transformId, state: TRANSFORM_STATE.STOPPED }], + }; await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -91,9 +80,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - }) + .send(reqBody) .expect(403); await transform.api.waitForTransformToExist(transformId); await transform.api.waitForIndicesToExist(destinationIndex); @@ -102,7 +89,9 @@ export default ({ getService }: FtrProviderContext) => { describe('single transform deletion with invalid transformId', function () { it('should return 200 with error in response if invalid transformId', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: 'invalid_transform_id' }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: 'invalid_transform_id', state: TRANSFORM_STATE.STOPPED }], + }; const { body } = await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -110,9 +99,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - }) + .send(reqBody) .expect(200); expect(body.invalid_transform_id.transformDeleted.success).to.eql(false); expect(body.invalid_transform_id.transformDeleted).to.have.property('error'); @@ -120,15 +107,17 @@ export default ({ getService }: FtrProviderContext) => { }); describe('bulk deletion', function () { - const transformsInfo: TransformEndpointRequest[] = [ - { id: 'bulk_delete_test_1' }, - { id: 'bulk_delete_test_2' }, - ]; - const destinationIndices = transformsInfo.map((d) => generateDestIndex(d.id)); + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [ + { id: 'bulk_delete_test_1', state: TRANSFORM_STATE.STOPPED }, + { id: 'bulk_delete_test_2', state: TRANSFORM_STATE.STOPPED }, + ], + }; + const destinationIndices = reqBody.transformsInfo.map((d) => generateDestIndex(d.id)); beforeEach(async () => { - await asyncForEach(transformsInfo, async ({ id }: { id: string }, idx: number) => { - await createTransform(id, destinationIndices[idx]); + await asyncForEach(reqBody.transformsInfo, async ({ id }: { id: string }, idx: number) => { + await createTransform(id); await transform.api.createIndices(destinationIndices[idx]); }); }); @@ -147,13 +136,11 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - }) + .send(reqBody) .expect(200); await asyncForEach( - transformsInfo, + reqBody.transformsInfo, async ({ id: transformId }: { id: string }, idx: number) => { expect(body[transformId].transformDeleted.success).to.eql(true); expect(body[transformId].destIndexDeleted.success).to.eql(false); @@ -174,16 +161,16 @@ export default ({ getService }: FtrProviderContext) => { ) .set(COMMON_REQUEST_HEADERS) .send({ + ...reqBody, transformsInfo: [ - { id: transformsInfo[0].id }, - { id: invalidTransformId }, - { id: transformsInfo[1].id }, + ...reqBody.transformsInfo, + { id: invalidTransformId, state: TRANSFORM_STATE.STOPPED }, ], }) .expect(200); await asyncForEach( - transformsInfo, + reqBody.transformsInfo, async ({ id: transformId }: { id: string }, idx: number) => { expect(body[transformId].transformDeleted.success).to.eql(true); expect(body[transformId].destIndexDeleted.success).to.eql(false); @@ -203,7 +190,7 @@ export default ({ getService }: FtrProviderContext) => { const destinationIndex = generateDestIndex(transformId); before(async () => { - await createTransform(transformId, destinationIndex); + await createTransform(transformId); await transform.api.createIndices(destinationIndex); }); @@ -212,7 +199,10 @@ export default ({ getService }: FtrProviderContext) => { }); it('should delete transform and destination index', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: transformId }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: transformId, state: TRANSFORM_STATE.STOPPED }], + deleteDestIndex: true, + }; const { body } = await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -220,10 +210,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - deleteDestIndex: true, - }) + .send(reqBody) .expect(200); expect(body[transformId].transformDeleted.success).to.eql(true); @@ -239,7 +226,7 @@ export default ({ getService }: FtrProviderContext) => { const destinationIndex = generateDestIndex(transformId); before(async () => { - await createTransform(transformId, destinationIndex); + await createTransform(transformId); await transform.api.createIndices(destinationIndex); await transform.testResources.createIndexPatternIfNeeded(destinationIndex); }); @@ -250,7 +237,11 @@ export default ({ getService }: FtrProviderContext) => { }); it('should delete transform and destination index pattern', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: transformId }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: transformId, state: TRANSFORM_STATE.STOPPED }], + deleteDestIndex: false, + deleteDestIndexPattern: true, + }; const { body } = await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -258,11 +249,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - deleteDestIndex: false, - deleteDestIndexPattern: true, - }) + .send(reqBody) .expect(200); expect(body[transformId].transformDeleted.success).to.eql(true); @@ -279,7 +266,7 @@ export default ({ getService }: FtrProviderContext) => { const destinationIndex = generateDestIndex(transformId); before(async () => { - await createTransform(transformId, destinationIndex); + await createTransform(transformId); await transform.api.createIndices(destinationIndex); await transform.testResources.createIndexPatternIfNeeded(destinationIndex); }); @@ -290,7 +277,11 @@ export default ({ getService }: FtrProviderContext) => { }); it('should delete transform, destination index, & destination index pattern', async () => { - const transformsInfo: TransformEndpointRequest[] = [{ id: transformId }]; + const reqBody: DeleteTransformsRequestSchema = { + transformsInfo: [{ id: transformId, state: TRANSFORM_STATE.STOPPED }], + deleteDestIndex: true, + deleteDestIndexPattern: true, + }; const { body } = await supertest .post(`/api/transform/delete_transforms`) .auth( @@ -298,11 +289,7 @@ export default ({ getService }: FtrProviderContext) => { transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) ) .set(COMMON_REQUEST_HEADERS) - .send({ - transformsInfo, - deleteDestIndex: true, - deleteDestIndexPattern: true, - }) + .send(reqBody) .expect(200); expect(body[transformId].transformDeleted.success).to.eql(true); diff --git a/x-pack/test/api_integration/apis/transform/index.ts b/x-pack/test/api_integration/apis/transform/index.ts index 93a951a55ece..ef08883534d1 100644 --- a/x-pack/test/api_integration/apis/transform/index.ts +++ b/x-pack/test/api_integration/apis/transform/index.ts @@ -28,5 +28,11 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { }); loadTestFile(require.resolve('./delete_transforms')); + loadTestFile(require.resolve('./start_transforms')); + loadTestFile(require.resolve('./stop_transforms')); + loadTestFile(require.resolve('./transforms')); + loadTestFile(require.resolve('./transforms_preview')); + loadTestFile(require.resolve('./transforms_stats')); + loadTestFile(require.resolve('./transforms_update')); }); } diff --git a/x-pack/test/api_integration/apis/transform/start_transforms.ts b/x-pack/test/api_integration/apis/transform/start_transforms.ts new file mode 100644 index 000000000000..288a3caae390 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/start_transforms.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { StartTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/start_transforms'; +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { asyncForEach, generateDestIndex, generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + async function createTransform(transformId: string) { + const config = generateTransformConfig(transformId); + await transform.api.createTransform(transformId, config); + } + + describe('/api/transform/start_transforms', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + }); + + describe('single transform start', function () { + const transformId = 'transform-test-start'; + const destinationIndex = generateDestIndex(transformId); + + beforeEach(async () => { + await createTransform(transformId); + }); + + afterEach(async () => { + await transform.api.cleanTransformIndices(); + await transform.api.deleteIndices(destinationIndex); + }); + + it('should start the transform by transformId', async () => { + const reqBody: StartTransformsRequestSchema = [{ id: transformId }]; + const { body } = await supertest + .post(`/api/transform/start_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(body[transformId].success).to.eql(true); + expect(typeof body[transformId].error).to.eql('undefined'); + await transform.api.waitForBatchTransformToComplete(transformId); + await transform.api.waitForIndicesToExist(destinationIndex); + }); + + it('should return 200 with success:false for unauthorized user', async () => { + const reqBody: StartTransformsRequestSchema = [{ id: transformId }]; + const { body } = await supertest + .post(`/api/transform/start_transforms`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(body[transformId].success).to.eql(false); + expect(typeof body[transformId].error).to.eql('string'); + + await transform.api.waitForTransformState(transformId, TRANSFORM_STATE.STOPPED); + await transform.api.waitForIndicesNotToExist(destinationIndex); + }); + }); + + describe('single transform start with invalid transformId', function () { + it('should return 200 with error in response if invalid transformId', async () => { + const reqBody: StartTransformsRequestSchema = [{ id: 'invalid_transform_id' }]; + const { body } = await supertest + .post(`/api/transform/start_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(body.invalid_transform_id.success).to.eql(false); + expect(body.invalid_transform_id).to.have.property('error'); + }); + }); + + describe('bulk start', function () { + const reqBody: StartTransformsRequestSchema = [ + { id: 'bulk_start_test_1' }, + { id: 'bulk_start_test_2' }, + ]; + const destinationIndices = reqBody.map((d) => generateDestIndex(d.id)); + + beforeEach(async () => { + await asyncForEach(reqBody, async ({ id }: { id: string }, idx: number) => { + await createTransform(id); + }); + }); + + afterEach(async () => { + await transform.api.cleanTransformIndices(); + await asyncForEach(destinationIndices, async (destinationIndex: string) => { + await transform.api.deleteIndices(destinationIndex); + }); + }); + + it('should start multiple transforms by transformIds', async () => { + const { body } = await supertest + .post(`/api/transform/start_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + await asyncForEach(reqBody, async ({ id: transformId }: { id: string }, idx: number) => { + expect(body[transformId].success).to.eql(true); + await transform.api.waitForBatchTransformToComplete(transformId); + await transform.api.waitForIndicesToExist(destinationIndices[idx]); + }); + }); + + it('should start multiple transforms by transformIds, even if one of the transformIds is invalid', async () => { + const invalidTransformId = 'invalid_transform_id'; + const { body } = await supertest + .post(`/api/transform/start_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send([{ id: reqBody[0].id }, { id: invalidTransformId }, { id: reqBody[1].id }]) + .expect(200); + + await asyncForEach(reqBody, async ({ id: transformId }: { id: string }, idx: number) => { + expect(body[transformId].success).to.eql(true); + await transform.api.waitForBatchTransformToComplete(transformId); + await transform.api.waitForIndicesToExist(destinationIndices[idx]); + }); + + expect(body[invalidTransformId].success).to.eql(false); + expect(body[invalidTransformId]).to.have.property('error'); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/transform/stop_transforms.ts b/x-pack/test/api_integration/apis/transform/stop_transforms.ts new file mode 100644 index 000000000000..4f30db0794ea --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/stop_transforms.ts @@ -0,0 +1,197 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import type { PutTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/transforms'; +import type { StopTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/stop_transforms'; +import { isStopTransformsResponseSchema } from '../../../../plugins/transform/common/api_schemas/type_guards'; + +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { asyncForEach, generateDestIndex, generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + async function createAndRunTransform(transformId: string) { + // to be able to test stopping transforms, + // we create a slow continuous transform + // so it doesn't stop automatically. + const config: PutTransformsRequestSchema = { + ...generateTransformConfig(transformId), + settings: { + docs_per_second: 10, + max_page_search_size: 10, + }, + sync: { + time: { field: '@timestamp' }, + }, + }; + + await transform.api.createAndRunTransform(transformId, config); + } + + describe('/api/transform/stop_transforms', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + }); + + describe('single transform stop', function () { + const transformId = 'transform-test-stop'; + const destinationIndex = generateDestIndex(transformId); + + beforeEach(async () => { + await createAndRunTransform(transformId); + }); + + afterEach(async () => { + await transform.api.cleanTransformIndices(); + await transform.api.deleteIndices(destinationIndex); + }); + + it('should stop the transform by transformId', async () => { + const reqBody: StopTransformsRequestSchema = [ + { id: transformId, state: TRANSFORM_STATE.STARTED }, + ]; + const { body } = await supertest + .post(`/api/transform/stop_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(isStopTransformsResponseSchema(body)).to.eql(true); + expect(body[transformId].success).to.eql(true); + expect(typeof body[transformId].error).to.eql('undefined'); + await transform.api.waitForTransformState(transformId, TRANSFORM_STATE.STOPPED); + await transform.api.waitForIndicesToExist(destinationIndex); + }); + + it('should return 200 with success:false for unauthorized user', async () => { + const reqBody: StopTransformsRequestSchema = [ + { id: transformId, state: TRANSFORM_STATE.STARTED }, + ]; + const { body } = await supertest + .post(`/api/transform/stop_transforms`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(isStopTransformsResponseSchema(body)).to.eql(true); + expect(body[transformId].success).to.eql(false); + expect(typeof body[transformId].error).to.eql('string'); + + await transform.api.waitForTransformStateNotToBe(transformId, TRANSFORM_STATE.STOPPED); + await transform.api.waitForIndicesToExist(destinationIndex); + }); + }); + + describe('single transform stop with invalid transformId', function () { + it('should return 200 with error in response if invalid transformId', async () => { + const reqBody: StopTransformsRequestSchema = [ + { id: 'invalid_transform_id', state: TRANSFORM_STATE.STARTED }, + ]; + const { body } = await supertest + .post(`/api/transform/stop_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(isStopTransformsResponseSchema(body)).to.eql(true); + expect(body.invalid_transform_id.success).to.eql(false); + expect(body.invalid_transform_id).to.have.property('error'); + }); + }); + + describe('bulk stop', function () { + const reqBody: StopTransformsRequestSchema = [ + { id: 'bulk_stop_test_1', state: TRANSFORM_STATE.STARTED }, + { id: 'bulk_stop_test_2', state: TRANSFORM_STATE.STARTED }, + ]; + const destinationIndices = reqBody.map((d) => generateDestIndex(d.id)); + + beforeEach(async () => { + await asyncForEach(reqBody, async ({ id }: { id: string }, idx: number) => { + await createAndRunTransform(id); + }); + }); + + afterEach(async () => { + await transform.api.cleanTransformIndices(); + await asyncForEach(destinationIndices, async (destinationIndex: string) => { + await transform.api.deleteIndices(destinationIndex); + }); + }); + + it('should stop multiple transforms by transformIds', async () => { + const { body } = await supertest + .post(`/api/transform/stop_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(reqBody) + .expect(200); + + expect(isStopTransformsResponseSchema(body)).to.eql(true); + + await asyncForEach(reqBody, async ({ id: transformId }: { id: string }, idx: number) => { + expect(body[transformId].success).to.eql(true); + await transform.api.waitForTransformState(transformId, TRANSFORM_STATE.STOPPED); + await transform.api.waitForIndicesToExist(destinationIndices[idx]); + }); + }); + + it('should stop multiple transforms by transformIds, even if one of the transformIds is invalid', async () => { + const invalidTransformId = 'invalid_transform_id'; + const { body } = await supertest + .post(`/api/transform/stop_transforms`) + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send([ + { id: reqBody[0].id, state: reqBody[0].state }, + { id: invalidTransformId, state: TRANSFORM_STATE.STOPPED }, + { id: reqBody[1].id, state: reqBody[1].state }, + ]) + .expect(200); + + expect(isStopTransformsResponseSchema(body)).to.eql(true); + + await asyncForEach(reqBody, async ({ id: transformId }: { id: string }, idx: number) => { + expect(body[transformId].success).to.eql(true); + await transform.api.waitForTransformState(transformId, TRANSFORM_STATE.STOPPED); + await transform.api.waitForIndicesToExist(destinationIndices[idx]); + }); + + expect(body[invalidTransformId].success).to.eql(false); + expect(body[invalidTransformId]).to.have.property('error'); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/transform/transforms.ts b/x-pack/test/api_integration/apis/transform/transforms.ts new file mode 100644 index 000000000000..c44c2b58e620 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/transforms.ts @@ -0,0 +1,165 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import type { GetTransformsResponseSchema } from '../../../../plugins/transform/common/api_schemas/transforms'; +import { isGetTransformsResponseSchema } from '../../../../plugins/transform/common/api_schemas/type_guards'; +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + const expected = { + apiTransformTransforms: { + count: 2, + transform1: { id: 'transform-test-get-1', destIndex: 'user-transform-test-get-1' }, + transform2: { id: 'transform-test-get-2', destIndex: 'user-transform-test-get-2' }, + typeOfVersion: 'string', + typeOfCreateTime: 'number', + }, + apiTransformTransformsTransformId: { + count: 1, + transform1: { id: 'transform-test-get-1', destIndex: 'user-transform-test-get-1' }, + typeOfVersion: 'string', + typeOfCreateTime: 'number', + }, + }; + + async function createTransform(transformId: string) { + const config = generateTransformConfig(transformId); + await transform.api.createTransform(transformId, config); + } + + function assertTransformsResponseBody(body: GetTransformsResponseSchema) { + expect(isGetTransformsResponseSchema(body)).to.eql(true); + + expect(body.count).to.eql(expected.apiTransformTransforms.count); + expect(body.transforms).to.have.length(expected.apiTransformTransforms.count); + + const transform1 = body.transforms[0]; + expect(transform1.id).to.eql(expected.apiTransformTransforms.transform1.id); + expect(transform1.dest.index).to.eql(expected.apiTransformTransforms.transform1.destIndex); + expect(typeof transform1.version).to.eql(expected.apiTransformTransforms.typeOfVersion); + expect(typeof transform1.create_time).to.eql(expected.apiTransformTransforms.typeOfCreateTime); + + const transform2 = body.transforms[1]; + expect(transform2.id).to.eql(expected.apiTransformTransforms.transform2.id); + expect(transform2.dest.index).to.eql(expected.apiTransformTransforms.transform2.destIndex); + expect(typeof transform2.version).to.eql(expected.apiTransformTransforms.typeOfVersion); + expect(typeof transform2.create_time).to.eql(expected.apiTransformTransforms.typeOfCreateTime); + } + + function assertSingleTransformResponseBody(body: GetTransformsResponseSchema) { + expect(isGetTransformsResponseSchema(body)).to.eql(true); + + expect(body.count).to.eql(expected.apiTransformTransformsTransformId.count); + expect(body.transforms).to.have.length(expected.apiTransformTransformsTransformId.count); + + const transform1 = body.transforms[0]; + expect(transform1.id).to.eql(expected.apiTransformTransformsTransformId.transform1.id); + expect(transform1.dest.index).to.eql( + expected.apiTransformTransformsTransformId.transform1.destIndex + ); + expect(typeof transform1.version).to.eql( + expected.apiTransformTransformsTransformId.typeOfVersion + ); + expect(typeof transform1.create_time).to.eql( + expected.apiTransformTransformsTransformId.typeOfCreateTime + ); + } + + describe('/api/transform/transforms', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + await createTransform('transform-test-get-1'); + await createTransform('transform-test-get-2'); + }); + + after(async () => { + await transform.api.cleanTransformIndices(); + }); + + describe('/transforms', function () { + it('should return a list of transforms for super-user', async () => { + const { body } = await supertest + .get('/api/transform/transforms') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertTransformsResponseBody(body); + }); + + it('should return a list of transforms for transform view-only user', async () => { + const { body } = await supertest + .get(`/api/transform/transforms`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertTransformsResponseBody(body); + }); + }); + + describe('/transforms/{transformId}', function () { + it('should return a specific transform configuration for super-user', async () => { + const { body } = await supertest + .get('/api/transform/transforms/transform-test-get-1') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertSingleTransformResponseBody(body); + }); + + it('should return a specific transform configuration transform view-only user', async () => { + const { body } = await supertest + .get(`/api/transform/transforms/transform-test-get-1`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertSingleTransformResponseBody(body); + }); + + it('should report 404 for a non-existing transform', async () => { + await supertest + .get('/api/transform/transforms/the-non-existing-transform') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(404); + }); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/transform/transforms_preview.ts b/x-pack/test/api_integration/apis/transform/transforms_preview.ts new file mode 100644 index 000000000000..d0fc44cf28fd --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/transforms_preview.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 expect from '@kbn/expect'; + +import type { PostTransformsPreviewRequestSchema } from '../../../../plugins/transform/common/api_schemas/transforms'; + +import { FtrProviderContext } from '../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + const expected = { + apiTransformTransformsPreview: { + previewItemCount: 19, + typeOfGeneratedDestIndex: 'object', + }, + }; + + function getTransformPreviewConfig() { + // passing in an empty string for transform id since we will not use + // it as part of the config request schema. Destructuring will + // remove the `dest` part of the config. + const { dest, ...config } = generateTransformConfig(''); + return config as PostTransformsPreviewRequestSchema; + } + + describe('/api/transform/transforms/_preview', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + await transform.api.waitForIndicesToExist('ft_farequote'); + }); + + it('should return a transform preview', async () => { + const { body } = await supertest + .post('/api/transform/transforms/_preview') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(getTransformPreviewConfig()) + .expect(200); + + expect(body.preview).to.have.length(expected.apiTransformTransformsPreview.previewItemCount); + expect(typeof body.generated_dest_index).to.eql( + expected.apiTransformTransformsPreview.typeOfGeneratedDestIndex + ); + }); + + it('should return 403 for transform view-only user', async () => { + await supertest + .post(`/api/transform/transforms/_preview`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(getTransformPreviewConfig()) + .expect(403); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/transform/transforms_stats.ts b/x-pack/test/api_integration/apis/transform/transforms_stats.ts new file mode 100644 index 000000000000..07856e5095a9 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/transforms_stats.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import type { GetTransformsStatsResponseSchema } from '../../../../plugins/transform/common/api_schemas/transforms_stats'; +import { isGetTransformsStatsResponseSchema } from '../../../../plugins/transform/common/api_schemas/type_guards'; +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +import { generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + const expected = { + apiTransformTransforms: { + count: 2, + transform1: { id: 'transform-test-stats-1', state: TRANSFORM_STATE.STOPPED }, + transform2: { id: 'transform-test-stats-2', state: TRANSFORM_STATE.STOPPED }, + typeOfStats: 'object', + typeOfCheckpointing: 'object', + }, + }; + + async function createTransform(transformId: string) { + const config = generateTransformConfig(transformId); + await transform.api.createTransform(transformId, config); + } + + function assertTransformsStatsResponseBody(body: GetTransformsStatsResponseSchema) { + expect(isGetTransformsStatsResponseSchema(body)).to.eql(true); + expect(body.count).to.eql(expected.apiTransformTransforms.count); + expect(body.transforms).to.have.length(expected.apiTransformTransforms.count); + + const transform1 = body.transforms[0]; + expect(transform1.id).to.eql(expected.apiTransformTransforms.transform1.id); + expect(transform1.state).to.eql(expected.apiTransformTransforms.transform1.state); + expect(typeof transform1.stats).to.eql(expected.apiTransformTransforms.typeOfStats); + expect(typeof transform1.checkpointing).to.eql( + expected.apiTransformTransforms.typeOfCheckpointing + ); + + const transform2 = body.transforms[1]; + expect(transform2.id).to.eql(expected.apiTransformTransforms.transform2.id); + expect(transform2.state).to.eql(expected.apiTransformTransforms.transform2.state); + expect(typeof transform2.stats).to.eql(expected.apiTransformTransforms.typeOfStats); + expect(typeof transform2.checkpointing).to.eql( + expected.apiTransformTransforms.typeOfCheckpointing + ); + } + + describe('/api/transform/transforms/_stats', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + await createTransform('transform-test-stats-1'); + await createTransform('transform-test-stats-2'); + }); + + after(async () => { + await transform.api.cleanTransformIndices(); + }); + + it('should return a list of transforms statistics for super-user', async () => { + const { body } = await supertest + .get('/api/transform/transforms/_stats') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertTransformsStatsResponseBody(body); + }); + + it('should return a list of transforms statistics view-only user', async () => { + const { body } = await supertest + .get(`/api/transform/transforms/_stats`) + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + assertTransformsStatsResponseBody(body); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/transform/transforms_update.ts b/x-pack/test/api_integration/apis/transform/transforms_update.ts new file mode 100644 index 000000000000..3ad5b5b47c79 --- /dev/null +++ b/x-pack/test/api_integration/apis/transform/transforms_update.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { COMMON_REQUEST_HEADERS } from '../../../functional/services/ml/common_api'; +import { USER } from '../../../functional/services/transform/security_common'; + +import { generateTransformConfig } from './common'; + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const transform = getService('transform'); + + const expected = { + transformOriginalConfig: { + count: 1, + id: 'transform-test-update-1', + source: { + index: ['ft_farequote'], + query: { match_all: {} }, + }, + }, + apiTransformTransformsPreview: { + previewItemCount: 19, + typeOfGeneratedDestIndex: 'object', + }, + }; + + async function createTransform(transformId: string) { + const config = generateTransformConfig(transformId); + await transform.api.createTransform(transformId, config); + } + + function getTransformUpdateConfig() { + return { + source: { + index: 'ft_*', + query: { + term: { + airline: { + value: 'AAL', + }, + }, + }, + }, + description: 'the-updated-description', + dest: { + index: 'user-the-updated-destination-index', + }, + frequency: '60m', + }; + } + + describe('/api/transform/transforms/{transformId}/_update', function () { + before(async () => { + await esArchiver.loadIfNeeded('ml/farequote'); + await transform.testResources.setKibanaTimeZoneToUTC(); + await createTransform('transform-test-update-1'); + }); + + after(async () => { + await transform.api.cleanTransformIndices(); + }); + + it('should update a transform', async () => { + // assert the original transform for comparison + const { body: transformOriginalBody } = await supertest + .get('/api/transform/transforms/transform-test-update-1') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + expect(transformOriginalBody.count).to.eql(expected.transformOriginalConfig.count); + expect(transformOriginalBody.transforms).to.have.length( + expected.transformOriginalConfig.count + ); + + const transformOriginalConfig = transformOriginalBody.transforms[0]; + expect(transformOriginalConfig.id).to.eql(expected.transformOriginalConfig.id); + expect(transformOriginalConfig.source).to.eql(expected.transformOriginalConfig.source); + expect(transformOriginalConfig.description).to.eql(undefined); + expect(transformOriginalConfig.settings).to.eql({}); + + // update the transform and assert the response + const { body: transformUpdateResponseBody } = await supertest + .post('/api/transform/transforms/transform-test-update-1/_update') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(getTransformUpdateConfig()) + .expect(200); + + const expectedUpdateConfig = getTransformUpdateConfig(); + expect(transformUpdateResponseBody.id).to.eql(expected.transformOriginalConfig.id); + expect(transformUpdateResponseBody.source).to.eql({ + ...expectedUpdateConfig.source, + index: ['ft_*'], + }); + expect(transformUpdateResponseBody.description).to.eql(expectedUpdateConfig.description); + expect(transformUpdateResponseBody.settings).to.eql({}); + + // assert the updated transform for comparison + const { body: transformUpdatedBody } = await supertest + .get('/api/transform/transforms/transform-test-update-1') + .auth( + USER.TRANSFORM_POWERUSER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_POWERUSER) + ) + .set(COMMON_REQUEST_HEADERS) + .send() + .expect(200); + + expect(transformUpdatedBody.count).to.eql(expected.transformOriginalConfig.count); + expect(transformUpdatedBody.transforms).to.have.length( + expected.transformOriginalConfig.count + ); + + const transformUpdatedConfig = transformUpdatedBody.transforms[0]; + expect(transformUpdatedConfig.id).to.eql(expected.transformOriginalConfig.id); + expect(transformUpdatedConfig.source).to.eql({ + ...expectedUpdateConfig.source, + index: ['ft_*'], + }); + expect(transformUpdatedConfig.description).to.eql(expectedUpdateConfig.description); + expect(transformUpdatedConfig.settings).to.eql({}); + }); + + it('should return 403 for transform view-only user', async () => { + await supertest + .post('/api/transform/transforms/transform-test-update-1/_update') + .auth( + USER.TRANSFORM_VIEWER, + transform.securityCommon.getPasswordForUser(USER.TRANSFORM_VIEWER) + ) + .set(COMMON_REQUEST_HEADERS) + .send(getTransformUpdateConfig()) + .expect(403); + }); + }); +}; diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts index 33c00105e74f..bae94d89e745 100644 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ b/x-pack/test/apm_api_integration/basic/tests/index.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registerMochaHooksForSnapshots } from '../../common/match_snapshot'; export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('APM specs (basic)', function () { + registerMochaHooksForSnapshots(); + this.tags('ciGroup1'); loadTestFile(require.resolve('./feature_controls')); diff --git a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts b/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts index bd8b0c6126fa..96ac3c3a5e49 100644 --- a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts +++ b/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -22,7 +23,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` ); expect(response.status).to.be(200); - expect(response.body).to.eql({ serviceCount: 0, transactionCoordinates: [] }); + expectSnapshot(response.body).toMatchInline(` + Object { + "serviceCount": 0, + "transactionCoordinates": Array [], + } + `); }); }); describe('when data is loaded', () => { @@ -34,13 +40,21 @@ export default function ApiTest({ getService }: FtrProviderContext) { `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` ); expect(response.status).to.be(200); - expect(response.body).to.eql({ - serviceCount: 3, - transactionCoordinates: [ - { x: 1593413220000, y: 0.016666666666666666 }, - { x: 1593413280000, y: 1.0458333333333334 }, - ], - }); + expectSnapshot(response.body).toMatchInline(` + Object { + "serviceCount": 3, + "transactionCoordinates": Array [ + Object { + "x": 1593413220000, + "y": 0.016666666666666666, + }, + Object { + "x": 1593413280000, + "y": 1.0458333333333334, + }, + ], + } + `); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts b/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts index e4cceca573ce..a87d080e564a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts @@ -12,7 +12,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const range = archives['apm_8.0.0']; + const archiveName = 'apm_8.0.0'; + const range = archives[archiveName]; const start = encodeURIComponent(range.start); const end = encodeURIComponent(range.end); @@ -29,8 +30,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('when data is loaded', () => { - before(() => esArchiver.load('apm_8.0.0')); - after(() => esArchiver.unload('apm_8.0.0')); + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); it('returns the agent name', async () => { const response = await supertest.get( diff --git a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts index ea3ed2539c12..116b2987db32 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts @@ -4,17 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { sortBy } from 'lodash'; import expect from '@kbn/expect'; +import { isEmpty, pick } from 'lodash'; +import { PromiseReturnType } from '../../../../../plugins/apm/typings/common'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../../common/archives_metadata'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const archiveName = 'apm_8.0.0'; + + const range = archives_metadata[archiveName]; + // url parameters - const start = encodeURIComponent('2020-06-29T06:45:00.000Z'); - const end = encodeURIComponent('2020-06-29T06:49:00.000Z'); + const start = encodeURIComponent(range.start); + const end = encodeURIComponent(range.end); + const uiFilters = encodeURIComponent(JSON.stringify({})); describe('APM Services Overview', () => { @@ -30,46 +38,189 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); describe('when data is loaded', () => { - before(() => esArchiver.load('8.0.0')); - after(() => esArchiver.unload('8.0.0')); + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); - it('returns a list of services', async () => { - const response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - // sort services to mitigate unstable sort order - const services = sortBy(response.body.items, ['serviceName']); + describe('and fetching a list of services', () => { + let response: PromiseReturnType; + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + }); - expect(response.status).to.be(200); - expect(services).to.eql([ - { - serviceName: 'client', - agentName: 'rum-js', - transactionsPerMinute: 2, - errorsPerMinute: 2.75, - avgResponseTime: 116375, - environments: [], - }, - { - serviceName: 'opbeans-java', - agentName: 'java', - transactionsPerMinute: 30.75, - errorsPerMinute: 4.5, - avgResponseTime: 25636.349593495936, - environments: ['production'], - }, - { - serviceName: 'opbeans-node', - agentName: 'nodejs', - transactionsPerMinute: 31, - errorsPerMinute: 3.75, - avgResponseTime: 38682.52419354839, - environments: ['production'], - }, - ]); - - expect(response.body.hasHistoricalData).to.be(true); - expect(response.body.hasLegacyData).to.be(false); + it('the response is successful', () => { + expect(response.status).to.eql(200); + }); + + it('returns hasHistoricalData: true', () => { + expect(response.body.hasHistoricalData).to.be(true); + }); + + it('returns hasLegacyData: false', () => { + expect(response.body.hasLegacyData).to.be(false); + }); + + it('returns the correct service names', () => { + expectSnapshot(response.body.items.map((item: any) => item.serviceName)).toMatchInline(` + Array [ + "opbeans-python", + "opbeans-node", + "opbeans-ruby", + "opbeans-go", + "opbeans-dotnet", + "opbeans-java", + "opbeans-rum", + ] + `); + }); + + it('returns the correct metrics averages', () => { + expectSnapshot( + response.body.items.map((item: any) => + pick( + item, + 'transactionErrorRate.value', + 'avgResponseTime.value', + 'transactionsPerMinute.value' + ) + ) + ).toMatchInline(` + Array [ + Object { + "avgResponseTime": Object { + "value": 208079.9121184089, + }, + "transactionErrorRate": Object { + "value": 0.041666666666666664, + }, + "transactionsPerMinute": Object { + "value": 18.016666666666666, + }, + }, + Object { + "avgResponseTime": Object { + "value": 578297.1431623931, + }, + "transactionErrorRate": Object { + "value": 0.03317535545023697, + }, + "transactionsPerMinute": Object { + "value": 7.8, + }, + }, + Object { + "avgResponseTime": Object { + "value": 60518.587926509186, + }, + "transactionErrorRate": Object { + "value": 0.013123359580052493, + }, + "transactionsPerMinute": Object { + "value": 6.35, + }, + }, + Object { + "avgResponseTime": Object { + "value": 25259.78717201166, + }, + "transactionErrorRate": Object { + "value": 0.014577259475218658, + }, + "transactionsPerMinute": Object { + "value": 5.716666666666667, + }, + }, + Object { + "avgResponseTime": Object { + "value": 527290.3218390804, + }, + "transactionErrorRate": Object { + "value": 0.01532567049808429, + }, + "transactionsPerMinute": Object { + "value": 4.35, + }, + }, + Object { + "avgResponseTime": Object { + "value": 530245.8571428572, + }, + "transactionErrorRate": Object { + "value": 0.15384615384615385, + }, + "transactionsPerMinute": Object { + "value": 3.033333333333333, + }, + }, + Object { + "avgResponseTime": Object { + "value": 896134.328358209, + }, + "transactionsPerMinute": Object { + "value": 2.2333333333333334, + }, + }, + ] + `); + }); + + it('returns environments', () => { + expectSnapshot(response.body.items.map((item: any) => item.environments ?? [])) + .toMatchInline(` + Array [ + Array [ + "production", + ], + Array [ + "testing", + ], + Array [ + "production", + ], + Array [ + "testing", + ], + Array [ + "production", + ], + Array [ + "production", + ], + Array [ + "testing", + ], + ] + `); + }); + + it(`RUM services don't report any transaction error rates`, () => { + // RUM transactions don't have event.outcome set, + // so they should not have an error rate + + const rumServices = response.body.items.filter( + (item: any) => item.agentName === 'rum-js' + ); + + expect(rumServices.length).to.be.greaterThan(0); + + expect(rumServices.every((item: any) => isEmpty(item.transactionErrorRate?.value))); + }); + + it('non-RUM services all report transaction error rates', () => { + const nonRumServices = response.body.items.filter( + (item: any) => item.agentName !== 'rum-js' + ); + + expect( + nonRumServices.every((item: any) => { + return ( + typeof item.transactionErrorRate?.value === 'number' && + item.transactionErrorRate.timeseries.length > 0 + ); + }) + ).to.be(true); + }); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts index 3e8f320ad6b2..a6c6bad21a8b 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -23,7 +24,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql({ transactionTypes: [] }); + + expect(response.body.transactionTypes.length).to.be(0); }); }); @@ -37,7 +39,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql({ transactionTypes: ['request', 'Worker'] }); + expectSnapshot(response.body).toMatchInline(` + Object { + "transactionTypes": Array [ + "request", + "Worker", + ], + } + `); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts index d868a2a0e71c..b178c27467c7 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts @@ -25,14 +25,18 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns an error because the user does not have access', async () => { const { body } = await getAnomalyDetectionJobs(); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); describe('when calling create endpoint', () => { it('returns an error because the user does not have access', async () => { const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts index 070762a1d944..60d9fcf7f09c 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { @@ -25,19 +26,21 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns an error because the user is on basic license', async () => { const { body } = await getAnomalyDetectionJobs(); - expect(body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: - "To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning.", - }); + + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + + expectSnapshot(body.message).toMatchInline( + `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` + ); }); }); describe('when calling create endpoint', () => { it('returns an error because the user does not have access', async () => { const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts index c7bd7f0c96fa..d1dbd15f4dce 100644 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts +++ b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; export default function apiTest({ getService }: FtrProviderContext) { @@ -25,24 +26,25 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns an error because the user is on basic license', async () => { const { body } = await getAnomalyDetectionJobs(); - expect(body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: - "To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning.", - }); + + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + expectSnapshot(body.message).toMatchInline( + `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` + ); }); }); describe('when calling create endpoint', () => { it('returns an error because the user is on basic license', async () => { const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - expect(body).to.eql({ - statusCode: 403, - error: 'Forbidden', - message: - "To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning.", - }); + + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + + expectSnapshot(body.message).toMatchInline( + `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` + ); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap b/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap new file mode 100644 index 000000000000..5557e0828a33 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap @@ -0,0 +1,303 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Top traces when data is loaded returns the correct buckets 1`] = ` +Array [ + Object { + "averageResponseTime": 2577, + "impact": 0, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /throw-error", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 3147, + "impact": 0.06552270160444405, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#orders", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 3392.5, + "impact": 0.09374344413758617, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#order", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 4713.5, + "impact": 0.24559517890858723, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#product", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 4757, + "impact": 0.25059559560997896, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/products/:id/customers", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 6787, + "impact": 0.4839483750082622, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#products", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 4749.666666666667, + "impact": 0.5227447114845778, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/orders/:id", + }, + "transactionsPerMinute": 0.75, + }, + Object { + "averageResponseTime": 7624.5, + "impact": 0.5802207655235637, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/orders", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 5098, + "impact": 0.582807187955318, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/stats", + }, + "transactionsPerMinute": 0.75, + }, + Object { + "averageResponseTime": 8181, + "impact": 0.6441916136689552, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/types/:id", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 20011, + "impact": 0.853921734857215, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "POST /api", + }, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 6583, + "impact": 1.2172278724376455, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/products", + }, + "transactionsPerMinute": 1, + }, + Object { + "averageResponseTime": 33097, + "impact": 1.6060533780113861, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/products/top", + }, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 4825, + "impact": 1.6450221426498186, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#topProducts", + }, + "transactionsPerMinute": 1.75, + }, + Object { + "averageResponseTime": 35846, + "impact": 1.7640550505645587, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /log-error", + }, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 3742.153846153846, + "impact": 2.4998634943716573, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#customerWhoBought", + }, + "transactionsPerMinute": 3.25, + }, + Object { + "averageResponseTime": 3492.9285714285716, + "impact": 2.5144049360435208, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET static file", + }, + "transactionsPerMinute": 3.5, + }, + Object { + "averageResponseTime": 26992.5, + "impact": 2.8066131947777255, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/types", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 13516.5, + "impact": 2.8112687551548836, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/products/:id", + }, + "transactionsPerMinute": 1, + }, + Object { + "averageResponseTime": 20092, + "impact": 3.168195050736987, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/customers", + }, + "transactionsPerMinute": 0.75, + }, + Object { + "averageResponseTime": 15535, + "impact": 3.275330415465657, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#stats", + }, + "transactionsPerMinute": 1, + }, + Object { + "averageResponseTime": 32667.5, + "impact": 3.458966408120217, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /log-message", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 16690.75, + "impact": 3.541042213287889, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#customers", + }, + "transactionsPerMinute": 1, + }, + Object { + "averageResponseTime": 33500, + "impact": 3.5546640380951287, + "key": Object { + "service.name": "client", + "transaction.name": "/customers", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 77000, + "impact": 4.129424578484989, + "key": Object { + "service.name": "client", + "transaction.name": "/products", + }, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 19370.6, + "impact": 5.270496679320978, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#customer", + }, + "transactionsPerMinute": 1.25, + }, + Object { + "averageResponseTime": 81500, + "impact": 9.072365225837785, + "key": Object { + "service.name": "client", + "transaction.name": "/orders", + }, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 14419.42857142857, + "impact": 11.30657439844125, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "ResourceHttpRequestHandler", + }, + "transactionsPerMinute": 3.5, + }, + Object { + "averageResponseTime": 270684, + "impact": 15.261616628971955, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "POST /api/orders", + }, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 36010.53846153846, + "impact": 26.61043592713186, + "key": Object { + "service.name": "opbeans-java", + "transaction.name": "DispatcherServlet#doGet", + }, + "transactionsPerMinute": 3.25, + }, + Object { + "averageResponseTime": 208000, + "impact": 35.56882613781033, + "key": Object { + "service.name": "client", + "transaction.name": "/dashboard", + }, + "transactionsPerMinute": 0.75, + }, + Object { + "averageResponseTime": 49816.15625, + "impact": 91.32732325394932, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api", + }, + "transactionsPerMinute": 8, + }, + Object { + "averageResponseTime": 1745009, + "impact": 100, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "Process payment", + }, + "transactionsPerMinute": 0.25, + }, +] +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json b/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json deleted file mode 100644 index 4db040e92e7f..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/traces/expectation/top_traces.expectation.json +++ /dev/null @@ -1,5160 +0,0 @@ -[ - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "Process payment" - }, - "averageResponseTime": 1745009, - "transactionsPerMinute": 0.25, - "impact": 100, - "sample": { - "@timestamp": "2020-06-29T06:48:29.892Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:39.379730Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "timestamp": { - "us": 1593413309892019 - }, - "trace": { - "id": "bc393b659bef63291b6fa08e6f1d3f14" - }, - "transaction": { - "duration": { - "us": 1745009 - }, - "id": "a58333df6d851cf1", - "name": "Process payment", - "result": "success", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "Worker" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api" - }, - "averageResponseTime": 49816.15625, - "transactionsPerMinute": 8, - "impact": 91.32732325394932, - "sample": { - "@timestamp": "2020-06-29T06:48:06.969Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:08.306961Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "0" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:06 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413286969018 - }, - "trace": { - "id": "87a828bcedd44d9e872d8f552fb04aa6" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 25229 - }, - "id": "b1843afd04271423", - "name": "GET /api", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders/474", - "original": "/api/orders/474", - "path": "/api/orders/474", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "client", - "transaction.name": "/dashboard" - }, - "averageResponseTime": 208000, - "transactionsPerMinute": 0.75, - "impact": 35.56882613781033, - "sample": { - "@timestamp": "2020-06-29T06:48:07.275Z", - "agent": { - "name": "rum-js", - "version": "5.2.0" - }, - "client": { - "ip": "172.18.0.8" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:08.291261Z" - }, - "http": { - "request": { - "referrer": "" - }, - "response": { - "decoded_body_size": 813, - "encoded_body_size": 813, - "transfer_size": 962 - } - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "language": { - "name": "javascript" - }, - "name": "client", - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413287275113 - }, - "trace": { - "id": "ca86ffcac7753ec8733933bd8fd45d11" - }, - "transaction": { - "custom": { - "userConfig": { - "featureFlags": [ - "double-trouble", - "4423-hotfix" - ], - "showDashboard": true - } - }, - "duration": { - "us": 342000 - }, - "id": "c40f735132c8e864", - "marks": { - "agent": { - "domComplete": 335, - "domInteractive": 327, - "timeToFirstByte": 16 - }, - "navigationTiming": { - "connectEnd": 12, - "connectStart": 12, - "domComplete": 335, - "domContentLoadedEventEnd": 327, - "domContentLoadedEventStart": 327, - "domInteractive": 327, - "domLoading": 21, - "domainLookupEnd": 12, - "domainLookupStart": 10, - "fetchStart": 0, - "loadEventEnd": 335, - "loadEventStart": 335, - "requestStart": 12, - "responseEnd": 17, - "responseStart": 16 - } - }, - "name": "/dashboard", - "page": { - "referer": "", - "url": "http://opbeans-node:3000/dashboard" - }, - "sampled": true, - "span_count": { - "started": 9 - }, - "type": "page-load" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/dashboard", - "original": "http://opbeans-node:3000/dashboard", - "path": "/dashboard", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "arthur.dent@example.com", - "id": "1", - "name": "arthurdent" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "DispatcherServlet#doGet" - }, - "averageResponseTime": 36010.53846153846, - "transactionsPerMinute": 3.25, - "impact": 26.61043592713186, - "sample": { - "@timestamp": "2020-06-29T06:48:10.529Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.757591Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers_sent": false, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Servlet API" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413290529006 - }, - "trace": { - "id": "66e3db4cf016b138a43d319d15174891" - }, - "transaction": { - "duration": { - "us": 34366 - }, - "id": "7ea720a0175e7ffa", - "name": "DispatcherServlet#doGet", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/products", - "path": "/api/products", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "POST /api/orders" - }, - "averageResponseTime": 270684, - "transactionsPerMinute": 0.25, - "impact": 15.261616628971955, - "sample": { - "@timestamp": "2020-06-29T06:48:39.953Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:43.991549Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "body": { - "original": "[REDACTED]" - }, - "headers": { - "Accept": [ - "application/json" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "129" - ], - "Content-Type": [ - "application/json" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "post", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "13" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:40 GMT" - ], - "Etag": [ - "W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413319953033 - }, - "trace": { - "id": "52b8fda5f6df745b990740ba18378620" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 270684 - }, - "id": "a3afc2a112e9c893", - "name": "POST /api/orders", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 16 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "ResourceHttpRequestHandler" - }, - "averageResponseTime": 14419.42857142857, - "transactionsPerMinute": 3.5, - "impact": 11.30657439844125, - "sample": { - "@timestamp": "2020-06-29T06:48:06.640Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.517678Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers_sent": true, - "status_code": 404 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413286640008 - }, - "trace": { - "id": "81d8ffb0a39e755eed400f6486e15672" - }, - "transaction": { - "duration": { - "us": 2953 - }, - "id": "353d42a2f9046e99", - "name": "ResourceHttpRequestHandler", - "result": "HTTP 4xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/types/3", - "path": "/api/types/3", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "client", - "transaction.name": "/orders" - }, - "averageResponseTime": 81500, - "transactionsPerMinute": 0.5, - "impact": 9.072365225837785, - "sample": { - "@timestamp": "2020-06-29T06:48:29.296Z", - "agent": { - "name": "rum-js", - "version": "5.2.0" - }, - "client": { - "ip": "172.18.0.8" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.986555Z" - }, - "http": { - "request": { - "referrer": "" - } - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "language": { - "name": "javascript" - }, - "name": "client", - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413309296660 - }, - "trace": { - "id": "978b56807e0b7a27cbc41a0dfb665f47" - }, - "transaction": { - "custom": { - "userConfig": { - "featureFlags": [ - "double-trouble", - "4423-hotfix" - ], - "showDashboard": true - } - }, - "duration": { - "us": 23000 - }, - "id": "c3801eadbdef5c7c", - "name": "/orders", - "page": { - "referer": "", - "url": "http://opbeans-node:3000/orders" - }, - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "route-change" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/orders", - "original": "http://opbeans-node:3000/orders", - "path": "/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "arthur.dent@example.com", - "id": "1", - "name": "arthurdent" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customer" - }, - "averageResponseTime": 19370.6, - "transactionsPerMinute": 1.25, - "impact": 5.270496679320978, - "sample": { - "@timestamp": "2020-06-29T06:48:08.631Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.536897Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:08 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413288631008 - }, - "trace": { - "id": "c00da24c5c793cd679ce3df47cee8f37" - }, - "transaction": { - "duration": { - "us": 76826 - }, - "id": "3c8403055ff75866", - "name": "APIRestController#customer", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/customers/56", - "path": "/api/customers/56", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "client", - "transaction.name": "/products" - }, - "averageResponseTime": 77000, - "transactionsPerMinute": 0.25, - "impact": 4.129424578484989, - "sample": { - "@timestamp": "2020-06-29T06:48:48.824Z", - "agent": { - "name": "rum-js", - "version": "5.2.0" - }, - "client": { - "ip": "172.18.0.8" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:49.293664Z" - }, - "http": { - "request": { - "referrer": "" - }, - "response": { - "decoded_body_size": 813, - "encoded_body_size": 813, - "transfer_size": 962 - } - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "language": { - "name": "javascript" - }, - "name": "client", - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413328824656 - }, - "trace": { - "id": "f6c4a9197bbd080bd45072970f251525" - }, - "transaction": { - "custom": { - "userConfig": { - "featureFlags": [ - "double-trouble", - "4423-hotfix" - ], - "showDashboard": true - } - }, - "duration": { - "us": 77000 - }, - "id": "a11ede1968973bc5", - "marks": { - "agent": { - "domComplete": 68, - "domInteractive": 58, - "timeToFirstByte": 5 - }, - "navigationTiming": { - "connectEnd": 1, - "connectStart": 1, - "domComplete": 68, - "domContentLoadedEventEnd": 59, - "domContentLoadedEventStart": 59, - "domInteractive": 58, - "domLoading": 23, - "domainLookupEnd": 1, - "domainLookupStart": 1, - "fetchStart": 0, - "loadEventEnd": 68, - "loadEventStart": 68, - "requestStart": 2, - "responseEnd": 5, - "responseStart": 5 - } - }, - "name": "/products", - "page": { - "referer": "", - "url": "http://opbeans-node:3000/products" - }, - "sampled": true, - "span_count": { - "started": 5 - }, - "type": "page-load" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/products", - "original": "http://opbeans-node:3000/products", - "path": "/products", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "z@example.com", - "id": "4", - "name": "zaphod" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": { - "service.name": "client", - "transaction.name": "/customers" - }, - "averageResponseTime": 33500, - "transactionsPerMinute": 0.5, - "impact": 3.5546640380951287, - "sample": { - "@timestamp": "2020-06-29T06:48:35.071Z", - "agent": { - "name": "rum-js", - "version": "5.2.0" - }, - "client": { - "ip": "172.18.0.8" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:36.077184Z" - }, - "http": { - "request": { - "referrer": "" - } - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "language": { - "name": "javascript" - }, - "name": "client", - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413315071116 - }, - "trace": { - "id": "547a92e82a25387321d1b967f2dd0f48" - }, - "transaction": { - "custom": { - "userConfig": { - "featureFlags": [ - "double-trouble", - "4423-hotfix" - ], - "showDashboard": true - } - }, - "duration": { - "us": 28000 - }, - "id": "d24f9b9dacb83450", - "name": "/customers", - "page": { - "referer": "", - "url": "http://opbeans-node:3000/customers" - }, - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "route-change" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/customers", - "original": "http://opbeans-node:3000/customers", - "path": "/customers", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "arthur.dent@example.com", - "id": "1", - "name": "arthurdent" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customers" - }, - "averageResponseTime": 16690.75, - "transactionsPerMinute": 1, - "impact": 3.541042213287889, - "sample": { - "@timestamp": "2020-06-29T06:48:22.372Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:25.888154Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:21 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 500 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413302372009 - }, - "trace": { - "id": "21dd795dc3a260b1bf7ebbbac1e86fb8" - }, - "transaction": { - "duration": { - "us": 14795 - }, - "id": "0157fc513282138f", - "name": "APIRestController#customers", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/customers", - "path": "/api/customers", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /log-message" - }, - "averageResponseTime": 32667.5, - "transactionsPerMinute": 0.5, - "impact": 3.458966408120217, - "sample": { - "@timestamp": "2020-06-29T06:48:25.944Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.976822Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "24" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:25 GMT" - ], - "Etag": [ - "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413305944023 - }, - "trace": { - "id": "cd2ad726ad164d701c5d3103cbab0c81" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 38547 - }, - "id": "9e41667eb64dea55", - "name": "GET /log-message", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/log-message", - "original": "/log-message", - "path": "/log-message", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#stats" - }, - "averageResponseTime": 15535, - "transactionsPerMinute": 1, - "impact": 3.275330415465657, - "sample": { - "@timestamp": "2020-06-29T06:48:09.912Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.543824Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers_sent": true, - "status_code": 500 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413289912007 - }, - "trace": { - "id": "a17ceae4e18d50430ca15ecca5a3e69f" - }, - "transaction": { - "duration": { - "us": 10930 - }, - "id": "9fb330060bb73271", - "name": "APIRestController#stats", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 5 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/stats", - "path": "/api/stats", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/customers" - }, - "averageResponseTime": 20092, - "transactionsPerMinute": 0.75, - "impact": 3.168195050736987, - "sample": { - "@timestamp": "2020-06-29T06:48:28.444Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.982737Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "186769" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:28 GMT" - ], - "Etag": [ - "W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413308444015 - }, - "trace": { - "id": "792fb0b00256164e88b277ec40b65e14" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 26471 - }, - "id": "6c1f848752563d2b", - "name": "GET /api/customers", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/customers", - "original": "/api/customers", - "path": "/api/customers", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/:id" - }, - "averageResponseTime": 13516.5, - "transactionsPerMinute": 1, - "impact": 2.8112687551548836, - "sample": { - "@timestamp": "2020-06-29T06:47:57.555Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:59.085077Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "231" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:57 GMT" - ], - "Etag": [ - "W/\"e7-6JlJegaJ+ir0C8I8EmmOjms1dnc\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 87, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413277555176 - }, - "trace": { - "id": "8365e1763f19e4067b88521d4d9247a0" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 37709 - }, - "id": "be2722a418272f10", - "name": "GET /api/products/:id", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/1", - "original": "/api/products/1", - "path": "/api/products/1", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/types" - }, - "averageResponseTime": 26992.5, - "transactionsPerMinute": 0.5, - "impact": 2.8066131947777255, - "sample": { - "@timestamp": "2020-06-29T06:47:52.935Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.471071Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "112" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:52 GMT" - ], - "Etag": [ - "W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413272935117 - }, - "trace": { - "id": "2946c536a33d163d0c984d00d1f3839a" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 45093 - }, - "id": "103482fda88b9400", - "name": "GET /api/types", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/types", - "original": "/api/types", - "path": "/api/types", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET static file" - }, - "averageResponseTime": 3492.9285714285716, - "transactionsPerMinute": 3.5, - "impact": 2.5144049360435208, - "sample": { - "@timestamp": "2020-06-29T06:47:53.427Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.472070Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Accept-Ranges": [ - "bytes" - ], - "Cache-Control": [ - "public, max-age=0" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "15086" - ], - "Content-Type": [ - "image/x-icon" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:53 GMT" - ], - "Etag": [ - "W/\"3aee-1725aff14f0\"" - ], - "Last-Modified": [ - "Thu, 28 May 2020 11:16:06 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413273427016 - }, - "trace": { - "id": "ec8a804fedf28fcf81d5682d69a16970" - }, - "transaction": { - "duration": { - "us": 4934 - }, - "id": "ab90a62901b770e6", - "name": "GET static file", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/favicon.ico", - "original": "/favicon.ico", - "path": "/favicon.ico", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#customerWhoBought" - }, - "averageResponseTime": 3742.153846153846, - "transactionsPerMinute": 3.25, - "impact": 2.4998634943716573, - "sample": { - "@timestamp": "2020-06-29T06:48:11.166Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.763228Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:10 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413291166005 - }, - "trace": { - "id": "fa0d353eb7967b344ed37674f40b2884" - }, - "transaction": { - "duration": { - "us": 4453 - }, - "id": "bce4ce4b09ded6ca", - "name": "APIRestController#customerWhoBought", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/products/3/customers", - "path": "/api/products/3/customers", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /log-error" - }, - "averageResponseTime": 35846, - "transactionsPerMinute": 0.25, - "impact": 1.7640550505645587, - "sample": { - "@timestamp": "2020-06-29T06:48:07.467Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:18.533253Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "24" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:07 GMT" - ], - "Etag": [ - "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413287467017 - }, - "trace": { - "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 35846 - }, - "id": "c7a30c1b076907ec", - "name": "GET /log-error", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/log-error", - "original": "/log-error", - "path": "/log-error", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#topProducts" - }, - "averageResponseTime": 4825, - "transactionsPerMinute": 1.75, - "impact": 1.6450221426498186, - "sample": { - "@timestamp": "2020-06-29T06:48:11.778Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.764351Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:11 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413291778008 - }, - "trace": { - "id": "d65e9816f1f6db3961867f7b6d1d4e6a" - }, - "transaction": { - "duration": { - "us": 4168 - }, - "id": "a72f4bb8149ecdc5", - "name": "APIRestController#topProducts", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/products/top", - "path": "/api/products/top", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/top" - }, - "averageResponseTime": 33097, - "transactionsPerMinute": 0.25, - "impact": 1.6060533780113861, - "sample": { - "@timestamp": "2020-06-29T06:48:01.200Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:02.734903Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:01 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 115, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413281200133 - }, - "trace": { - "id": "195f32efeb6f91e2f71b6bc8bb74ae3a" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 33097 - }, - "id": "22e72956dfc8967a", - "name": "GET /api/products/top", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/top", - "original": "/api/products/top", - "path": "/api/products/top", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products" - }, - "averageResponseTime": 6583, - "transactionsPerMinute": 1, - "impact": 1.2172278724376455, - "sample": { - "@timestamp": "2020-06-29T06:48:21.475Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:26.996210Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "1023" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:21 GMT" - ], - "Etag": [ - "W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413301475015 - }, - "trace": { - "id": "389b26b16949c7f783223de4f14b788c" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 6775 - }, - "id": "d2d4088a0b104fb4", - "name": "GET /api/products", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products", - "original": "/api/products", - "path": "/api/products", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "POST /api" - }, - "averageResponseTime": 20011, - "transactionsPerMinute": 0.25, - "impact": 0.853921734857215, - "sample": { - "@timestamp": "2020-06-29T06:48:25.478Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:27.005671Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "body": { - "original": "[REDACTED]" - }, - "headers": { - "Accept": [ - "application/json" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "129" - ], - "Content-Type": [ - "application/json" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "post", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Allow": [ - "GET" - ], - "Connection": [ - "close" - ], - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:25 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 405 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413305478010 - }, - "trace": { - "id": "4bd9027dd1e355ec742970e2d6333124" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 20011 - }, - "id": "94104435cf151478", - "name": "POST /api", - "result": "HTTP 4xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/types/:id" - }, - "averageResponseTime": 8181, - "transactionsPerMinute": 0.5, - "impact": 0.6441916136689552, - "sample": { - "@timestamp": "2020-06-29T06:47:53.928Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.472718Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "205" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:53 GMT" - ], - "Etag": [ - "W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413273928016 - }, - "trace": { - "id": "0becaafb422bfeb69e047bf7153aa469" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 10062 - }, - "id": "0cee4574091bda3b", - "name": "GET /api/types/:id", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/types/2", - "original": "/api/types/2", - "path": "/api/types/2", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/stats" - }, - "averageResponseTime": 5098, - "transactionsPerMinute": 0.75, - "impact": 0.582807187955318, - "sample": { - "@timestamp": "2020-06-29T06:48:34.949Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:39.479316Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "92" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:34 GMT" - ], - "Etag": [ - "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413314949017 - }, - "trace": { - "id": "616b3b77abd5534c61d6c0438469aee2" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 5459 - }, - "id": "5b4971de59d2099d", - "name": "GET /api/stats", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 4 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/stats", - "original": "/api/stats", - "path": "/api/stats", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/orders" - }, - "averageResponseTime": 7624.5, - "transactionsPerMinute": 0.5, - "impact": 0.5802207655235637, - "sample": { - "@timestamp": "2020-06-29T06:48:35.450Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:39.483715Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:35 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413315450014 - }, - "trace": { - "id": "2da70ccf10599b271f65273d169cde9f" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 8784 - }, - "id": "a3f4a4f339758440", - "name": "GET /api/orders", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/orders/:id" - }, - "averageResponseTime": 4749.666666666667, - "transactionsPerMinute": 0.75, - "impact": 0.5227447114845778, - "sample": { - "@timestamp": "2020-06-29T06:48:35.951Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:39.484133Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "0" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:35 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 404 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413315951017 - }, - "trace": { - "id": "95979caa80e6622cbbb2d308800c3016" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 3210 - }, - "id": "30344988dace0b43", - "name": "GET /api/orders/:id", - "result": "HTTP 4xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders/117", - "original": "/api/orders/117", - "path": "/api/orders/117", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#products" - }, - "averageResponseTime": 6787, - "transactionsPerMinute": 0.5, - "impact": 0.4839483750082622, - "sample": { - "@timestamp": "2020-06-29T06:48:13.595Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.755614Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:12 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413293595007 - }, - "trace": { - "id": "8519b6c3dbc32a0582228506526e1d74" - }, - "transaction": { - "duration": { - "us": 7929 - }, - "id": "b0354de660cd3698", - "name": "APIRestController#products", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 3 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/products", - "path": "/api/products", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /api/products/:id/customers" - }, - "averageResponseTime": 4757, - "transactionsPerMinute": 0.5, - "impact": 0.25059559560997896, - "sample": { - "@timestamp": "2020-06-29T06:48:22.977Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:27.000765Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:22 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413302977008 - }, - "trace": { - "id": "da8f22fe652ccb6680b3029ab6efd284" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 5618 - }, - "id": "bc51c1523afaf57a", - "name": "GET /api/products/:id/customers", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/3/customers", - "original": "/api/products/3/customers", - "path": "/api/products/3/customers", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#product" - }, - "averageResponseTime": 4713.5, - "transactionsPerMinute": 0.5, - "impact": 0.24559517890858723, - "sample": { - "@timestamp": "2020-06-29T06:48:36.383Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:46.666467Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:36 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413316383008 - }, - "trace": { - "id": "386b450aef87fc079b20136eda542af1" - }, - "transaction": { - "duration": { - "us": 4888 - }, - "id": "5a4aa02158b5658c", - "name": "APIRestController#product", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 3 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/products/1", - "path": "/api/products/1", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#order" - }, - "averageResponseTime": 3392.5, - "transactionsPerMinute": 0.5, - "impact": 0.09374344413758617, - "sample": { - "@timestamp": "2020-06-29T06:48:07.416Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:15.534378Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers_sent": false, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413287416007 - }, - "trace": { - "id": "25c46380df3d44a192ed07279a08b329" - }, - "transaction": { - "duration": { - "us": 4282 - }, - "id": "d4d5b23c685d2ee5", - "name": "APIRestController#order", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/orders/391", - "path": "/api/orders/391", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-java", - "transaction.name": "APIRestController#orders" - }, - "averageResponseTime": 3147, - "transactionsPerMinute": 0.5, - "impact": 0.06552270160444405, - "sample": { - "@timestamp": "2020-06-29T06:48:16.028Z", - "agent": { - "ephemeral_id": "222af346-6dd9-45ef-ac85-d86b67edd2de", - "name": "java", - "version": "1.17.1-SNAPSHOT" - }, - "client": { - "ip": "172.18.0.9" - }, - "container": { - "id": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:25.800962Z" - }, - "host": { - "architecture": "amd64", - "hostname": "918ebbd99b4f", - "ip": "172.18.0.6", - "name": "918ebbd99b4f", - "os": { - "platform": "Linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Host": [ - "172.18.0.6:3000" - ], - "User-Agent": [ - "Python/3.7 aiohttp/3.3.2" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "172.18.0.9" - } - }, - "response": { - "finished": true, - "headers": { - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:15 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ] - }, - "headers_sent": true, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "pid": 6, - "ppid": 1, - "title": "/opt/java/openjdk/bin/java" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "Spring Web MVC", - "version": "5.0.6.RELEASE" - }, - "language": { - "name": "Java", - "version": "11.0.7" - }, - "name": "opbeans-java", - "node": { - "name": "918ebbd99b4f40003cf5713c080bb8120fa3bbe7ac4a96acb3aec558ced91ec0" - }, - "runtime": { - "name": "Java", - "version": "11.0.7" - }, - "version": "None" - }, - "source": { - "ip": "172.18.0.9" - }, - "timestamp": { - "us": 1593413296028008 - }, - "trace": { - "id": "4110227ecacbccf79894165ae5df932d" - }, - "transaction": { - "duration": { - "us": 2903 - }, - "id": "8e3732f0f0da942b", - "name": "APIRestController#orders", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "dropped": 0, - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "172.18.0.6", - "full": "http://172.18.0.6:3000/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "Python/3.7 aiohttp/3.3.2" - } - } - }, - { - "key": { - "service.name": "opbeans-node", - "transaction.name": "GET /throw-error" - }, - "averageResponseTime": 2577, - "transactionsPerMinute": 0.5, - "impact": 0, - "sample": { - "@timestamp": "2020-06-29T06:48:19.975Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:21.012520Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "148" - ], - "Content-Security-Policy": [ - "default-src 'none'" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:19 GMT" - ], - "X-Content-Type-Options": [ - "nosniff" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413299975019 - }, - "trace": { - "id": "106f3a55b0b0ea327d1bbe4be66c3bcc" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 3226 - }, - "id": "247b9141552a9e73", - "name": "GET /throw-error", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/throw-error", - "original": "/throw-error", - "path": "/throw-error", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - } -] diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts index b4a037436adb..2935fb8e2839 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts @@ -5,8 +5,8 @@ */ import expect from '@kbn/expect'; import { sortBy, omit } from 'lodash'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import expectTopTraces from './expectation/top_traces.expectation.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -25,7 +25,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql({ items: [], isAggregationAccurate: true, bucketSize: 1000 }); + expectSnapshot(response.body).toMatchInline(` + Object { + "bucketSize": 1000, + "isAggregationAccurate": true, + "items": Array [], + } + `); }); }); @@ -44,7 +50,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the correct number of buckets', async () => { - expect(response.body.items.length).to.be(33); + expectSnapshot(response.body.items.length).toMatchInline(`33`); }); it('returns the correct buckets', async () => { @@ -53,12 +59,61 @@ export default function ApiTest({ getService }: FtrProviderContext) { 'impact' ); - const expectedTracesWithoutSamples = sortBy( - expectTopTraces.map((item: any) => omit(item, 'sample')), - 'impact' - ); + const firstItem = responseWithoutSamples[0]; + const lastItem = responseWithoutSamples[responseWithoutSamples.length - 1]; + + const groups = responseWithoutSamples.map((item) => item.key).slice(0, 5); + + expectSnapshot(responseWithoutSamples).toMatch(); + + expectSnapshot(firstItem).toMatchInline(` + Object { + "averageResponseTime": 2577, + "impact": 0, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "GET /throw-error", + }, + "transactionsPerMinute": 0.5, + } + `); + + expectSnapshot(lastItem).toMatchInline(` + Object { + "averageResponseTime": 1745009, + "impact": 100, + "key": Object { + "service.name": "opbeans-node", + "transaction.name": "Process payment", + }, + "transactionsPerMinute": 0.25, + } + `); - expect(responseWithoutSamples).to.eql(expectedTracesWithoutSamples); + expectSnapshot(groups).toMatchInline(` + Array [ + Object { + "service.name": "opbeans-node", + "transaction.name": "GET /throw-error", + }, + Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#orders", + }, + Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#order", + }, + Object { + "service.name": "opbeans-java", + "transaction.name": "APIRestController#product", + }, + Object { + "service.name": "opbeans-node", + "transaction.name": "GET /api/products/:id/customers", + }, + ] + `); }); it('returns a sample', async () => { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap new file mode 100644 index 000000000000..326797919a09 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap @@ -0,0 +1,1473 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Average duration by browser when data is loaded returns the average duration by browser 1`] = ` +Array [ + Object { + "data": Array [ + Object { + "x": 1593413100000, + }, + Object { + "x": 1593413101000, + }, + Object { + "x": 1593413102000, + }, + Object { + "x": 1593413103000, + }, + Object { + "x": 1593413104000, + }, + Object { + "x": 1593413105000, + }, + Object { + "x": 1593413106000, + }, + Object { + "x": 1593413107000, + }, + Object { + "x": 1593413108000, + }, + Object { + "x": 1593413109000, + }, + Object { + "x": 1593413110000, + }, + Object { + "x": 1593413111000, + }, + Object { + "x": 1593413112000, + }, + Object { + "x": 1593413113000, + }, + Object { + "x": 1593413114000, + }, + Object { + "x": 1593413115000, + }, + Object { + "x": 1593413116000, + }, + Object { + "x": 1593413117000, + }, + Object { + "x": 1593413118000, + }, + Object { + "x": 1593413119000, + }, + Object { + "x": 1593413120000, + }, + Object { + "x": 1593413121000, + }, + Object { + "x": 1593413122000, + }, + Object { + "x": 1593413123000, + }, + Object { + "x": 1593413124000, + }, + Object { + "x": 1593413125000, + }, + Object { + "x": 1593413126000, + }, + Object { + "x": 1593413127000, + }, + Object { + "x": 1593413128000, + }, + Object { + "x": 1593413129000, + }, + Object { + "x": 1593413130000, + }, + Object { + "x": 1593413131000, + }, + Object { + "x": 1593413132000, + }, + Object { + "x": 1593413133000, + }, + Object { + "x": 1593413134000, + }, + Object { + "x": 1593413135000, + }, + Object { + "x": 1593413136000, + }, + Object { + "x": 1593413137000, + }, + Object { + "x": 1593413138000, + }, + Object { + "x": 1593413139000, + }, + Object { + "x": 1593413140000, + }, + Object { + "x": 1593413141000, + }, + Object { + "x": 1593413142000, + }, + Object { + "x": 1593413143000, + }, + Object { + "x": 1593413144000, + }, + Object { + "x": 1593413145000, + }, + Object { + "x": 1593413146000, + }, + Object { + "x": 1593413147000, + }, + Object { + "x": 1593413148000, + }, + Object { + "x": 1593413149000, + }, + Object { + "x": 1593413150000, + }, + Object { + "x": 1593413151000, + }, + Object { + "x": 1593413152000, + }, + Object { + "x": 1593413153000, + }, + Object { + "x": 1593413154000, + }, + Object { + "x": 1593413155000, + }, + Object { + "x": 1593413156000, + }, + Object { + "x": 1593413157000, + }, + Object { + "x": 1593413158000, + }, + Object { + "x": 1593413159000, + }, + Object { + "x": 1593413160000, + }, + Object { + "x": 1593413161000, + }, + Object { + "x": 1593413162000, + }, + Object { + "x": 1593413163000, + }, + Object { + "x": 1593413164000, + }, + Object { + "x": 1593413165000, + }, + Object { + "x": 1593413166000, + }, + Object { + "x": 1593413167000, + }, + Object { + "x": 1593413168000, + }, + Object { + "x": 1593413169000, + }, + Object { + "x": 1593413170000, + }, + Object { + "x": 1593413171000, + }, + Object { + "x": 1593413172000, + }, + Object { + "x": 1593413173000, + }, + Object { + "x": 1593413174000, + }, + Object { + "x": 1593413175000, + }, + Object { + "x": 1593413176000, + }, + Object { + "x": 1593413177000, + }, + Object { + "x": 1593413178000, + }, + Object { + "x": 1593413179000, + }, + Object { + "x": 1593413180000, + }, + Object { + "x": 1593413181000, + }, + Object { + "x": 1593413182000, + }, + Object { + "x": 1593413183000, + }, + Object { + "x": 1593413184000, + }, + Object { + "x": 1593413185000, + }, + Object { + "x": 1593413186000, + }, + Object { + "x": 1593413187000, + }, + Object { + "x": 1593413188000, + }, + Object { + "x": 1593413189000, + }, + Object { + "x": 1593413190000, + }, + Object { + "x": 1593413191000, + }, + Object { + "x": 1593413192000, + }, + Object { + "x": 1593413193000, + }, + Object { + "x": 1593413194000, + }, + Object { + "x": 1593413195000, + }, + Object { + "x": 1593413196000, + }, + Object { + "x": 1593413197000, + }, + Object { + "x": 1593413198000, + }, + Object { + "x": 1593413199000, + }, + Object { + "x": 1593413200000, + }, + Object { + "x": 1593413201000, + }, + Object { + "x": 1593413202000, + }, + Object { + "x": 1593413203000, + }, + Object { + "x": 1593413204000, + }, + Object { + "x": 1593413205000, + }, + Object { + "x": 1593413206000, + }, + Object { + "x": 1593413207000, + }, + Object { + "x": 1593413208000, + }, + Object { + "x": 1593413209000, + }, + Object { + "x": 1593413210000, + }, + Object { + "x": 1593413211000, + }, + Object { + "x": 1593413212000, + }, + Object { + "x": 1593413213000, + }, + Object { + "x": 1593413214000, + }, + Object { + "x": 1593413215000, + }, + Object { + "x": 1593413216000, + }, + Object { + "x": 1593413217000, + }, + Object { + "x": 1593413218000, + }, + Object { + "x": 1593413219000, + }, + Object { + "x": 1593413220000, + }, + Object { + "x": 1593413221000, + }, + Object { + "x": 1593413222000, + }, + Object { + "x": 1593413223000, + }, + Object { + "x": 1593413224000, + }, + Object { + "x": 1593413225000, + }, + Object { + "x": 1593413226000, + }, + Object { + "x": 1593413227000, + }, + Object { + "x": 1593413228000, + }, + Object { + "x": 1593413229000, + }, + Object { + "x": 1593413230000, + }, + Object { + "x": 1593413231000, + }, + Object { + "x": 1593413232000, + }, + Object { + "x": 1593413233000, + }, + Object { + "x": 1593413234000, + }, + Object { + "x": 1593413235000, + }, + Object { + "x": 1593413236000, + }, + Object { + "x": 1593413237000, + }, + Object { + "x": 1593413238000, + }, + Object { + "x": 1593413239000, + }, + Object { + "x": 1593413240000, + }, + Object { + "x": 1593413241000, + }, + Object { + "x": 1593413242000, + }, + Object { + "x": 1593413243000, + }, + Object { + "x": 1593413244000, + }, + Object { + "x": 1593413245000, + }, + Object { + "x": 1593413246000, + }, + Object { + "x": 1593413247000, + }, + Object { + "x": 1593413248000, + }, + Object { + "x": 1593413249000, + }, + Object { + "x": 1593413250000, + }, + Object { + "x": 1593413251000, + }, + Object { + "x": 1593413252000, + }, + Object { + "x": 1593413253000, + }, + Object { + "x": 1593413254000, + }, + Object { + "x": 1593413255000, + }, + Object { + "x": 1593413256000, + }, + Object { + "x": 1593413257000, + }, + Object { + "x": 1593413258000, + }, + Object { + "x": 1593413259000, + }, + Object { + "x": 1593413260000, + }, + Object { + "x": 1593413261000, + }, + Object { + "x": 1593413262000, + }, + Object { + "x": 1593413263000, + }, + Object { + "x": 1593413264000, + }, + Object { + "x": 1593413265000, + }, + Object { + "x": 1593413266000, + }, + Object { + "x": 1593413267000, + }, + Object { + "x": 1593413268000, + }, + Object { + "x": 1593413269000, + }, + Object { + "x": 1593413270000, + }, + Object { + "x": 1593413271000, + }, + Object { + "x": 1593413272000, + }, + Object { + "x": 1593413273000, + }, + Object { + "x": 1593413274000, + }, + Object { + "x": 1593413275000, + }, + Object { + "x": 1593413276000, + }, + Object { + "x": 1593413277000, + }, + Object { + "x": 1593413278000, + }, + Object { + "x": 1593413279000, + }, + Object { + "x": 1593413280000, + }, + Object { + "x": 1593413281000, + }, + Object { + "x": 1593413282000, + }, + Object { + "x": 1593413283000, + }, + Object { + "x": 1593413284000, + }, + Object { + "x": 1593413285000, + }, + Object { + "x": 1593413286000, + }, + Object { + "x": 1593413287000, + "y": 342000, + }, + Object { + "x": 1593413288000, + }, + Object { + "x": 1593413289000, + }, + Object { + "x": 1593413290000, + }, + Object { + "x": 1593413291000, + }, + Object { + "x": 1593413292000, + }, + Object { + "x": 1593413293000, + }, + Object { + "x": 1593413294000, + }, + Object { + "x": 1593413295000, + }, + Object { + "x": 1593413296000, + }, + Object { + "x": 1593413297000, + }, + Object { + "x": 1593413298000, + "y": 173000, + }, + Object { + "x": 1593413299000, + }, + Object { + "x": 1593413300000, + }, + Object { + "x": 1593413301000, + "y": 109000, + }, + Object { + "x": 1593413302000, + }, + Object { + "x": 1593413303000, + }, + Object { + "x": 1593413304000, + }, + Object { + "x": 1593413305000, + }, + Object { + "x": 1593413306000, + }, + Object { + "x": 1593413307000, + }, + Object { + "x": 1593413308000, + }, + Object { + "x": 1593413309000, + }, + Object { + "x": 1593413310000, + }, + Object { + "x": 1593413311000, + }, + Object { + "x": 1593413312000, + }, + Object { + "x": 1593413313000, + }, + Object { + "x": 1593413314000, + }, + Object { + "x": 1593413315000, + }, + Object { + "x": 1593413316000, + }, + Object { + "x": 1593413317000, + }, + Object { + "x": 1593413318000, + "y": 140000, + }, + Object { + "x": 1593413319000, + }, + Object { + "x": 1593413320000, + }, + Object { + "x": 1593413321000, + }, + Object { + "x": 1593413322000, + }, + Object { + "x": 1593413323000, + }, + Object { + "x": 1593413324000, + }, + Object { + "x": 1593413325000, + }, + Object { + "x": 1593413326000, + }, + Object { + "x": 1593413327000, + }, + Object { + "x": 1593413328000, + "y": 77000, + }, + Object { + "x": 1593413329000, + }, + Object { + "x": 1593413330000, + }, + Object { + "x": 1593413331000, + }, + Object { + "x": 1593413332000, + }, + Object { + "x": 1593413333000, + }, + Object { + "x": 1593413334000, + }, + Object { + "x": 1593413335000, + }, + Object { + "x": 1593413336000, + }, + Object { + "x": 1593413337000, + }, + Object { + "x": 1593413338000, + }, + Object { + "x": 1593413339000, + }, + Object { + "x": 1593413340000, + }, + ], + "title": "HeadlessChrome", + }, +] +`; + +exports[`Average duration by browser when data is loaded returns the average duration by browser filtering by transaction name 1`] = ` +Array [ + Object { + "data": Array [ + Object { + "x": 1593413100000, + }, + Object { + "x": 1593413101000, + }, + Object { + "x": 1593413102000, + }, + Object { + "x": 1593413103000, + }, + Object { + "x": 1593413104000, + }, + Object { + "x": 1593413105000, + }, + Object { + "x": 1593413106000, + }, + Object { + "x": 1593413107000, + }, + Object { + "x": 1593413108000, + }, + Object { + "x": 1593413109000, + }, + Object { + "x": 1593413110000, + }, + Object { + "x": 1593413111000, + }, + Object { + "x": 1593413112000, + }, + Object { + "x": 1593413113000, + }, + Object { + "x": 1593413114000, + }, + Object { + "x": 1593413115000, + }, + Object { + "x": 1593413116000, + }, + Object { + "x": 1593413117000, + }, + Object { + "x": 1593413118000, + }, + Object { + "x": 1593413119000, + }, + Object { + "x": 1593413120000, + }, + Object { + "x": 1593413121000, + }, + Object { + "x": 1593413122000, + }, + Object { + "x": 1593413123000, + }, + Object { + "x": 1593413124000, + }, + Object { + "x": 1593413125000, + }, + Object { + "x": 1593413126000, + }, + Object { + "x": 1593413127000, + }, + Object { + "x": 1593413128000, + }, + Object { + "x": 1593413129000, + }, + Object { + "x": 1593413130000, + }, + Object { + "x": 1593413131000, + }, + Object { + "x": 1593413132000, + }, + Object { + "x": 1593413133000, + }, + Object { + "x": 1593413134000, + }, + Object { + "x": 1593413135000, + }, + Object { + "x": 1593413136000, + }, + Object { + "x": 1593413137000, + }, + Object { + "x": 1593413138000, + }, + Object { + "x": 1593413139000, + }, + Object { + "x": 1593413140000, + }, + Object { + "x": 1593413141000, + }, + Object { + "x": 1593413142000, + }, + Object { + "x": 1593413143000, + }, + Object { + "x": 1593413144000, + }, + Object { + "x": 1593413145000, + }, + Object { + "x": 1593413146000, + }, + Object { + "x": 1593413147000, + }, + Object { + "x": 1593413148000, + }, + Object { + "x": 1593413149000, + }, + Object { + "x": 1593413150000, + }, + Object { + "x": 1593413151000, + }, + Object { + "x": 1593413152000, + }, + Object { + "x": 1593413153000, + }, + Object { + "x": 1593413154000, + }, + Object { + "x": 1593413155000, + }, + Object { + "x": 1593413156000, + }, + Object { + "x": 1593413157000, + }, + Object { + "x": 1593413158000, + }, + Object { + "x": 1593413159000, + }, + Object { + "x": 1593413160000, + }, + Object { + "x": 1593413161000, + }, + Object { + "x": 1593413162000, + }, + Object { + "x": 1593413163000, + }, + Object { + "x": 1593413164000, + }, + Object { + "x": 1593413165000, + }, + Object { + "x": 1593413166000, + }, + Object { + "x": 1593413167000, + }, + Object { + "x": 1593413168000, + }, + Object { + "x": 1593413169000, + }, + Object { + "x": 1593413170000, + }, + Object { + "x": 1593413171000, + }, + Object { + "x": 1593413172000, + }, + Object { + "x": 1593413173000, + }, + Object { + "x": 1593413174000, + }, + Object { + "x": 1593413175000, + }, + Object { + "x": 1593413176000, + }, + Object { + "x": 1593413177000, + }, + Object { + "x": 1593413178000, + }, + Object { + "x": 1593413179000, + }, + Object { + "x": 1593413180000, + }, + Object { + "x": 1593413181000, + }, + Object { + "x": 1593413182000, + }, + Object { + "x": 1593413183000, + }, + Object { + "x": 1593413184000, + }, + Object { + "x": 1593413185000, + }, + Object { + "x": 1593413186000, + }, + Object { + "x": 1593413187000, + }, + Object { + "x": 1593413188000, + }, + Object { + "x": 1593413189000, + }, + Object { + "x": 1593413190000, + }, + Object { + "x": 1593413191000, + }, + Object { + "x": 1593413192000, + }, + Object { + "x": 1593413193000, + }, + Object { + "x": 1593413194000, + }, + Object { + "x": 1593413195000, + }, + Object { + "x": 1593413196000, + }, + Object { + "x": 1593413197000, + }, + Object { + "x": 1593413198000, + }, + Object { + "x": 1593413199000, + }, + Object { + "x": 1593413200000, + }, + Object { + "x": 1593413201000, + }, + Object { + "x": 1593413202000, + }, + Object { + "x": 1593413203000, + }, + Object { + "x": 1593413204000, + }, + Object { + "x": 1593413205000, + }, + Object { + "x": 1593413206000, + }, + Object { + "x": 1593413207000, + }, + Object { + "x": 1593413208000, + }, + Object { + "x": 1593413209000, + }, + Object { + "x": 1593413210000, + }, + Object { + "x": 1593413211000, + }, + Object { + "x": 1593413212000, + }, + Object { + "x": 1593413213000, + }, + Object { + "x": 1593413214000, + }, + Object { + "x": 1593413215000, + }, + Object { + "x": 1593413216000, + }, + Object { + "x": 1593413217000, + }, + Object { + "x": 1593413218000, + }, + Object { + "x": 1593413219000, + }, + Object { + "x": 1593413220000, + }, + Object { + "x": 1593413221000, + }, + Object { + "x": 1593413222000, + }, + Object { + "x": 1593413223000, + }, + Object { + "x": 1593413224000, + }, + Object { + "x": 1593413225000, + }, + Object { + "x": 1593413226000, + }, + Object { + "x": 1593413227000, + }, + Object { + "x": 1593413228000, + }, + Object { + "x": 1593413229000, + }, + Object { + "x": 1593413230000, + }, + Object { + "x": 1593413231000, + }, + Object { + "x": 1593413232000, + }, + Object { + "x": 1593413233000, + }, + Object { + "x": 1593413234000, + }, + Object { + "x": 1593413235000, + }, + Object { + "x": 1593413236000, + }, + Object { + "x": 1593413237000, + }, + Object { + "x": 1593413238000, + }, + Object { + "x": 1593413239000, + }, + Object { + "x": 1593413240000, + }, + Object { + "x": 1593413241000, + }, + Object { + "x": 1593413242000, + }, + Object { + "x": 1593413243000, + }, + Object { + "x": 1593413244000, + }, + Object { + "x": 1593413245000, + }, + Object { + "x": 1593413246000, + }, + Object { + "x": 1593413247000, + }, + Object { + "x": 1593413248000, + }, + Object { + "x": 1593413249000, + }, + Object { + "x": 1593413250000, + }, + Object { + "x": 1593413251000, + }, + Object { + "x": 1593413252000, + }, + Object { + "x": 1593413253000, + }, + Object { + "x": 1593413254000, + }, + Object { + "x": 1593413255000, + }, + Object { + "x": 1593413256000, + }, + Object { + "x": 1593413257000, + }, + Object { + "x": 1593413258000, + }, + Object { + "x": 1593413259000, + }, + Object { + "x": 1593413260000, + }, + Object { + "x": 1593413261000, + }, + Object { + "x": 1593413262000, + }, + Object { + "x": 1593413263000, + }, + Object { + "x": 1593413264000, + }, + Object { + "x": 1593413265000, + }, + Object { + "x": 1593413266000, + }, + Object { + "x": 1593413267000, + }, + Object { + "x": 1593413268000, + }, + Object { + "x": 1593413269000, + }, + Object { + "x": 1593413270000, + }, + Object { + "x": 1593413271000, + }, + Object { + "x": 1593413272000, + }, + Object { + "x": 1593413273000, + }, + Object { + "x": 1593413274000, + }, + Object { + "x": 1593413275000, + }, + Object { + "x": 1593413276000, + }, + Object { + "x": 1593413277000, + }, + Object { + "x": 1593413278000, + }, + Object { + "x": 1593413279000, + }, + Object { + "x": 1593413280000, + }, + Object { + "x": 1593413281000, + }, + Object { + "x": 1593413282000, + }, + Object { + "x": 1593413283000, + }, + Object { + "x": 1593413284000, + }, + Object { + "x": 1593413285000, + }, + Object { + "x": 1593413286000, + }, + Object { + "x": 1593413287000, + }, + Object { + "x": 1593413288000, + }, + Object { + "x": 1593413289000, + }, + Object { + "x": 1593413290000, + }, + Object { + "x": 1593413291000, + }, + Object { + "x": 1593413292000, + }, + Object { + "x": 1593413293000, + }, + Object { + "x": 1593413294000, + }, + Object { + "x": 1593413295000, + }, + Object { + "x": 1593413296000, + }, + Object { + "x": 1593413297000, + }, + Object { + "x": 1593413298000, + }, + Object { + "x": 1593413299000, + }, + Object { + "x": 1593413300000, + }, + Object { + "x": 1593413301000, + }, + Object { + "x": 1593413302000, + }, + Object { + "x": 1593413303000, + }, + Object { + "x": 1593413304000, + }, + Object { + "x": 1593413305000, + }, + Object { + "x": 1593413306000, + }, + Object { + "x": 1593413307000, + }, + Object { + "x": 1593413308000, + }, + Object { + "x": 1593413309000, + }, + Object { + "x": 1593413310000, + }, + Object { + "x": 1593413311000, + }, + Object { + "x": 1593413312000, + }, + Object { + "x": 1593413313000, + }, + Object { + "x": 1593413314000, + }, + Object { + "x": 1593413315000, + }, + Object { + "x": 1593413316000, + }, + Object { + "x": 1593413317000, + }, + Object { + "x": 1593413318000, + }, + Object { + "x": 1593413319000, + }, + Object { + "x": 1593413320000, + }, + Object { + "x": 1593413321000, + }, + Object { + "x": 1593413322000, + }, + Object { + "x": 1593413323000, + }, + Object { + "x": 1593413324000, + }, + Object { + "x": 1593413325000, + }, + Object { + "x": 1593413326000, + }, + Object { + "x": 1593413327000, + }, + Object { + "x": 1593413328000, + "y": 77000, + }, + Object { + "x": 1593413329000, + }, + Object { + "x": 1593413330000, + }, + Object { + "x": 1593413331000, + }, + Object { + "x": 1593413332000, + }, + Object { + "x": 1593413333000, + }, + Object { + "x": 1593413334000, + }, + Object { + "x": 1593413335000, + }, + Object { + "x": 1593413336000, + }, + Object { + "x": 1593413337000, + }, + Object { + "x": 1593413338000, + }, + Object { + "x": 1593413339000, + }, + Object { + "x": 1593413340000, + }, + ], + "title": "HeadlessChrome", + }, +] +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap new file mode 100644 index 000000000000..e204ff41dfa4 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/breakdown.snap @@ -0,0 +1,188 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Breakdown when data is loaded returns the transaction breakdown for a service 1`] = ` +Object { + "timeseries": Array [ + Object { + "color": "#54b399", + "data": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413310000, + "y": 0.16700861715223636, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "hideLegend": false, + "legendValue": "17%", + "title": "app", + "type": "areaStacked", + }, + Object { + "color": "#6092c0", + "data": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413310000, + "y": 0.7702092736971686, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "hideLegend": false, + "legendValue": "77%", + "title": "http", + "type": "areaStacked", + }, + Object { + "color": "#d36086", + "data": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413310000, + "y": 0.0508822322527698, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "hideLegend": false, + "legendValue": "5.1%", + "title": "postgresql", + "type": "areaStacked", + }, + Object { + "color": "#9170b8", + "data": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413310000, + "y": 0.011899876897825195, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "hideLegend": false, + "legendValue": "1.2%", + "title": "redis", + "type": "areaStacked", + }, + ], +} +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap new file mode 100644 index 000000000000..16a5640c5305 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/top_transaction_groups.snap @@ -0,0 +1,132 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` +Array [ + Object { + "averageResponseTime": 2577, + "impact": 0, + "key": "GET /throw-error", + "p95": 3224, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 4757, + "impact": 0.20830834986820673, + "key": "GET /api/products/:id/customers", + "p95": 5616, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 4749.666666666667, + "impact": 0.43453312891085794, + "key": "GET /api/orders/:id", + "p95": 7184, + "transactionsPerMinute": 0.75, + }, + Object { + "averageResponseTime": 8181, + "impact": 0.5354862351657939, + "key": "GET /api/types/:id", + "p95": 10080, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 20011, + "impact": 0.7098250353192541, + "key": "POST /api", + "p95": 19968, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 35846, + "impact": 1.466376117925459, + "key": "GET /log-error", + "p95": 35840, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 7105.333333333333, + "impact": 1.7905918202662048, + "key": "GET /api/stats", + "p95": 15136, + "transactionsPerMinute": 1.5, + }, + Object { + "averageResponseTime": 22958.5, + "impact": 1.9475397398343375, + "key": "GET /api/products/top", + "p95": 33216, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 3492.9285714285716, + "impact": 2.0901067389184496, + "key": "GET static file", + "p95": 11900, + "transactionsPerMinute": 3.5, + }, + Object { + "averageResponseTime": 26992.5, + "impact": 2.3330057413794503, + "key": "GET /api/types", + "p95": 45248, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 13516.5, + "impact": 2.3368756900811305, + "key": "GET /api/products/:id", + "p95": 37856, + "transactionsPerMinute": 1, + }, + Object { + "averageResponseTime": 8585, + "impact": 2.624924094061731, + "key": "GET /api/products", + "p95": 22112, + "transactionsPerMinute": 1.75, + }, + Object { + "averageResponseTime": 7615.625, + "impact": 2.6645791239678345, + "key": "GET /api/orders", + "p95": 11616, + "transactionsPerMinute": 2, + }, + Object { + "averageResponseTime": 3262.95, + "impact": 2.8716452680799467, + "key": "GET /*", + "p95": 4472, + "transactionsPerMinute": 5, + }, + Object { + "averageResponseTime": 32667.5, + "impact": 2.875276331059301, + "key": "GET /log-message", + "p95": 38528, + "transactionsPerMinute": 0.5, + }, + Object { + "averageResponseTime": 16896.8, + "impact": 3.790160870423129, + "key": "GET /api/customers", + "p95": 26432, + "transactionsPerMinute": 1.25, + }, + Object { + "averageResponseTime": 270684, + "impact": 12.686265169840583, + "key": "POST /api/orders", + "p95": 270336, + "transactionsPerMinute": 0.25, + }, + Object { + "averageResponseTime": 51175.73170731707, + "impact": 100, + "key": "GET /api", + "p95": 259040, + "transactionsPerMinute": 10.25, + }, +] +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap new file mode 100644 index 000000000000..0ac7741396fd --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/transaction_charts.snap @@ -0,0 +1,7761 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Transaction charts when data is loaded returns the transaction charts 1`] = ` +Object { + "apmTimeseries": Object { + "overallAvgDuration": 38682.52419354839, + "responseTimes": Object { + "avg": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413101000, + "y": null, + }, + Object { + "x": 1593413102000, + "y": null, + }, + Object { + "x": 1593413103000, + "y": null, + }, + Object { + "x": 1593413104000, + "y": null, + }, + Object { + "x": 1593413105000, + "y": null, + }, + Object { + "x": 1593413106000, + "y": null, + }, + Object { + "x": 1593413107000, + "y": null, + }, + Object { + "x": 1593413108000, + "y": null, + }, + Object { + "x": 1593413109000, + "y": null, + }, + Object { + "x": 1593413110000, + "y": null, + }, + Object { + "x": 1593413111000, + "y": null, + }, + Object { + "x": 1593413112000, + "y": null, + }, + Object { + "x": 1593413113000, + "y": null, + }, + Object { + "x": 1593413114000, + "y": null, + }, + Object { + "x": 1593413115000, + "y": null, + }, + Object { + "x": 1593413116000, + "y": null, + }, + Object { + "x": 1593413117000, + "y": null, + }, + Object { + "x": 1593413118000, + "y": null, + }, + Object { + "x": 1593413119000, + "y": null, + }, + Object { + "x": 1593413120000, + "y": null, + }, + Object { + "x": 1593413121000, + "y": null, + }, + Object { + "x": 1593413122000, + "y": null, + }, + Object { + "x": 1593413123000, + "y": null, + }, + Object { + "x": 1593413124000, + "y": null, + }, + Object { + "x": 1593413125000, + "y": null, + }, + Object { + "x": 1593413126000, + "y": null, + }, + Object { + "x": 1593413127000, + "y": null, + }, + Object { + "x": 1593413128000, + "y": null, + }, + Object { + "x": 1593413129000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413131000, + "y": null, + }, + Object { + "x": 1593413132000, + "y": null, + }, + Object { + "x": 1593413133000, + "y": null, + }, + Object { + "x": 1593413134000, + "y": null, + }, + Object { + "x": 1593413135000, + "y": null, + }, + Object { + "x": 1593413136000, + "y": null, + }, + Object { + "x": 1593413137000, + "y": null, + }, + Object { + "x": 1593413138000, + "y": null, + }, + Object { + "x": 1593413139000, + "y": null, + }, + Object { + "x": 1593413140000, + "y": null, + }, + Object { + "x": 1593413141000, + "y": null, + }, + Object { + "x": 1593413142000, + "y": null, + }, + Object { + "x": 1593413143000, + "y": null, + }, + Object { + "x": 1593413144000, + "y": null, + }, + Object { + "x": 1593413145000, + "y": null, + }, + Object { + "x": 1593413146000, + "y": null, + }, + Object { + "x": 1593413147000, + "y": null, + }, + Object { + "x": 1593413148000, + "y": null, + }, + Object { + "x": 1593413149000, + "y": null, + }, + Object { + "x": 1593413150000, + "y": null, + }, + Object { + "x": 1593413151000, + "y": null, + }, + Object { + "x": 1593413152000, + "y": null, + }, + Object { + "x": 1593413153000, + "y": null, + }, + Object { + "x": 1593413154000, + "y": null, + }, + Object { + "x": 1593413155000, + "y": null, + }, + Object { + "x": 1593413156000, + "y": null, + }, + Object { + "x": 1593413157000, + "y": null, + }, + Object { + "x": 1593413158000, + "y": null, + }, + Object { + "x": 1593413159000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413161000, + "y": null, + }, + Object { + "x": 1593413162000, + "y": null, + }, + Object { + "x": 1593413163000, + "y": null, + }, + Object { + "x": 1593413164000, + "y": null, + }, + Object { + "x": 1593413165000, + "y": null, + }, + Object { + "x": 1593413166000, + "y": null, + }, + Object { + "x": 1593413167000, + "y": null, + }, + Object { + "x": 1593413168000, + "y": null, + }, + Object { + "x": 1593413169000, + "y": null, + }, + Object { + "x": 1593413170000, + "y": null, + }, + Object { + "x": 1593413171000, + "y": null, + }, + Object { + "x": 1593413172000, + "y": null, + }, + Object { + "x": 1593413173000, + "y": null, + }, + Object { + "x": 1593413174000, + "y": null, + }, + Object { + "x": 1593413175000, + "y": null, + }, + Object { + "x": 1593413176000, + "y": null, + }, + Object { + "x": 1593413177000, + "y": null, + }, + Object { + "x": 1593413178000, + "y": null, + }, + Object { + "x": 1593413179000, + "y": null, + }, + Object { + "x": 1593413180000, + "y": null, + }, + Object { + "x": 1593413181000, + "y": null, + }, + Object { + "x": 1593413182000, + "y": null, + }, + Object { + "x": 1593413183000, + "y": null, + }, + Object { + "x": 1593413184000, + "y": null, + }, + Object { + "x": 1593413185000, + "y": null, + }, + Object { + "x": 1593413186000, + "y": null, + }, + Object { + "x": 1593413187000, + "y": null, + }, + Object { + "x": 1593413188000, + "y": null, + }, + Object { + "x": 1593413189000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413191000, + "y": null, + }, + Object { + "x": 1593413192000, + "y": null, + }, + Object { + "x": 1593413193000, + "y": null, + }, + Object { + "x": 1593413194000, + "y": null, + }, + Object { + "x": 1593413195000, + "y": null, + }, + Object { + "x": 1593413196000, + "y": null, + }, + Object { + "x": 1593413197000, + "y": null, + }, + Object { + "x": 1593413198000, + "y": null, + }, + Object { + "x": 1593413199000, + "y": null, + }, + Object { + "x": 1593413200000, + "y": null, + }, + Object { + "x": 1593413201000, + "y": null, + }, + Object { + "x": 1593413202000, + "y": null, + }, + Object { + "x": 1593413203000, + "y": null, + }, + Object { + "x": 1593413204000, + "y": null, + }, + Object { + "x": 1593413205000, + "y": null, + }, + Object { + "x": 1593413206000, + "y": null, + }, + Object { + "x": 1593413207000, + "y": null, + }, + Object { + "x": 1593413208000, + "y": null, + }, + Object { + "x": 1593413209000, + "y": null, + }, + Object { + "x": 1593413210000, + "y": null, + }, + Object { + "x": 1593413211000, + "y": null, + }, + Object { + "x": 1593413212000, + "y": null, + }, + Object { + "x": 1593413213000, + "y": null, + }, + Object { + "x": 1593413214000, + "y": null, + }, + Object { + "x": 1593413215000, + "y": null, + }, + Object { + "x": 1593413216000, + "y": null, + }, + Object { + "x": 1593413217000, + "y": null, + }, + Object { + "x": 1593413218000, + "y": null, + }, + Object { + "x": 1593413219000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413221000, + "y": null, + }, + Object { + "x": 1593413222000, + "y": null, + }, + Object { + "x": 1593413223000, + "y": null, + }, + Object { + "x": 1593413224000, + "y": null, + }, + Object { + "x": 1593413225000, + "y": null, + }, + Object { + "x": 1593413226000, + "y": null, + }, + Object { + "x": 1593413227000, + "y": null, + }, + Object { + "x": 1593413228000, + "y": null, + }, + Object { + "x": 1593413229000, + "y": null, + }, + Object { + "x": 1593413230000, + "y": null, + }, + Object { + "x": 1593413231000, + "y": null, + }, + Object { + "x": 1593413232000, + "y": null, + }, + Object { + "x": 1593413233000, + "y": null, + }, + Object { + "x": 1593413234000, + "y": null, + }, + Object { + "x": 1593413235000, + "y": null, + }, + Object { + "x": 1593413236000, + "y": null, + }, + Object { + "x": 1593413237000, + "y": null, + }, + Object { + "x": 1593413238000, + "y": null, + }, + Object { + "x": 1593413239000, + "y": null, + }, + Object { + "x": 1593413240000, + "y": null, + }, + Object { + "x": 1593413241000, + "y": null, + }, + Object { + "x": 1593413242000, + "y": null, + }, + Object { + "x": 1593413243000, + "y": null, + }, + Object { + "x": 1593413244000, + "y": null, + }, + Object { + "x": 1593413245000, + "y": null, + }, + Object { + "x": 1593413246000, + "y": null, + }, + Object { + "x": 1593413247000, + "y": null, + }, + Object { + "x": 1593413248000, + "y": null, + }, + Object { + "x": 1593413249000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413251000, + "y": null, + }, + Object { + "x": 1593413252000, + "y": null, + }, + Object { + "x": 1593413253000, + "y": null, + }, + Object { + "x": 1593413254000, + "y": null, + }, + Object { + "x": 1593413255000, + "y": null, + }, + Object { + "x": 1593413256000, + "y": null, + }, + Object { + "x": 1593413257000, + "y": null, + }, + Object { + "x": 1593413258000, + "y": null, + }, + Object { + "x": 1593413259000, + "y": null, + }, + Object { + "x": 1593413260000, + "y": null, + }, + Object { + "x": 1593413261000, + "y": null, + }, + Object { + "x": 1593413262000, + "y": null, + }, + Object { + "x": 1593413263000, + "y": null, + }, + Object { + "x": 1593413264000, + "y": null, + }, + Object { + "x": 1593413265000, + "y": null, + }, + Object { + "x": 1593413266000, + "y": null, + }, + Object { + "x": 1593413267000, + "y": null, + }, + Object { + "x": 1593413268000, + "y": null, + }, + Object { + "x": 1593413269000, + "y": null, + }, + Object { + "x": 1593413270000, + "y": null, + }, + Object { + "x": 1593413271000, + "y": null, + }, + Object { + "x": 1593413272000, + "y": 45093, + }, + Object { + "x": 1593413273000, + "y": 7498, + }, + Object { + "x": 1593413274000, + "y": null, + }, + Object { + "x": 1593413275000, + "y": null, + }, + Object { + "x": 1593413276000, + "y": null, + }, + Object { + "x": 1593413277000, + "y": 37709, + }, + Object { + "x": 1593413278000, + "y": null, + }, + Object { + "x": 1593413279000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413281000, + "y": 33097, + }, + Object { + "x": 1593413282000, + "y": null, + }, + Object { + "x": 1593413283000, + "y": null, + }, + Object { + "x": 1593413284000, + "y": 388507, + }, + Object { + "x": 1593413285000, + "y": 42331.5, + }, + Object { + "x": 1593413286000, + "y": 99104.25, + }, + Object { + "x": 1593413287000, + "y": 18939.5, + }, + Object { + "x": 1593413288000, + "y": 23229.5, + }, + Object { + "x": 1593413289000, + "y": 11318, + }, + Object { + "x": 1593413290000, + "y": 15651.25, + }, + Object { + "x": 1593413291000, + "y": 2376, + }, + Object { + "x": 1593413292000, + "y": 7796, + }, + Object { + "x": 1593413293000, + "y": 7571, + }, + Object { + "x": 1593413294000, + "y": 4219.333333333333, + }, + Object { + "x": 1593413295000, + "y": 6827.5, + }, + Object { + "x": 1593413296000, + "y": 10415.5, + }, + Object { + "x": 1593413297000, + "y": 10082, + }, + Object { + "x": 1593413298000, + "y": 6459.375, + }, + Object { + "x": 1593413299000, + "y": 3131.5, + }, + Object { + "x": 1593413300000, + "y": 6713.333333333333, + }, + Object { + "x": 1593413301000, + "y": 8800, + }, + Object { + "x": 1593413302000, + "y": 3743.5, + }, + Object { + "x": 1593413303000, + "y": 9239.5, + }, + Object { + "x": 1593413304000, + "y": 8402, + }, + Object { + "x": 1593413305000, + "y": 20520.666666666668, + }, + Object { + "x": 1593413306000, + "y": 9319.5, + }, + Object { + "x": 1593413307000, + "y": 7694.333333333333, + }, + Object { + "x": 1593413308000, + "y": 20131, + }, + Object { + "x": 1593413309000, + "y": 439937.75, + }, + Object { + "x": 1593413310000, + "y": 11933, + }, + Object { + "x": 1593413311000, + "y": 18670.5, + }, + Object { + "x": 1593413312000, + "y": 9232, + }, + Object { + "x": 1593413313000, + "y": 7602, + }, + Object { + "x": 1593413314000, + "y": 10428.8, + }, + Object { + "x": 1593413315000, + "y": 8405.25, + }, + Object { + "x": 1593413316000, + "y": 10654.5, + }, + Object { + "x": 1593413317000, + "y": 10250, + }, + Object { + "x": 1593413318000, + "y": 5775, + }, + Object { + "x": 1593413319000, + "y": 137867, + }, + Object { + "x": 1593413320000, + "y": 5694.333333333333, + }, + Object { + "x": 1593413321000, + "y": 6115, + }, + Object { + "x": 1593413322000, + "y": 1832.5, + }, + Object { + "x": 1593413323000, + "y": null, + }, + Object { + "x": 1593413324000, + "y": null, + }, + Object { + "x": 1593413325000, + "y": null, + }, + Object { + "x": 1593413326000, + "y": null, + }, + Object { + "x": 1593413327000, + "y": null, + }, + Object { + "x": 1593413328000, + "y": null, + }, + Object { + "x": 1593413329000, + "y": null, + }, + Object { + "x": 1593413330000, + "y": null, + }, + Object { + "x": 1593413331000, + "y": null, + }, + Object { + "x": 1593413332000, + "y": null, + }, + Object { + "x": 1593413333000, + "y": null, + }, + Object { + "x": 1593413334000, + "y": null, + }, + Object { + "x": 1593413335000, + "y": null, + }, + Object { + "x": 1593413336000, + "y": null, + }, + Object { + "x": 1593413337000, + "y": null, + }, + Object { + "x": 1593413338000, + "y": null, + }, + Object { + "x": 1593413339000, + "y": null, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "p95": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413101000, + "y": null, + }, + Object { + "x": 1593413102000, + "y": null, + }, + Object { + "x": 1593413103000, + "y": null, + }, + Object { + "x": 1593413104000, + "y": null, + }, + Object { + "x": 1593413105000, + "y": null, + }, + Object { + "x": 1593413106000, + "y": null, + }, + Object { + "x": 1593413107000, + "y": null, + }, + Object { + "x": 1593413108000, + "y": null, + }, + Object { + "x": 1593413109000, + "y": null, + }, + Object { + "x": 1593413110000, + "y": null, + }, + Object { + "x": 1593413111000, + "y": null, + }, + Object { + "x": 1593413112000, + "y": null, + }, + Object { + "x": 1593413113000, + "y": null, + }, + Object { + "x": 1593413114000, + "y": null, + }, + Object { + "x": 1593413115000, + "y": null, + }, + Object { + "x": 1593413116000, + "y": null, + }, + Object { + "x": 1593413117000, + "y": null, + }, + Object { + "x": 1593413118000, + "y": null, + }, + Object { + "x": 1593413119000, + "y": null, + }, + Object { + "x": 1593413120000, + "y": null, + }, + Object { + "x": 1593413121000, + "y": null, + }, + Object { + "x": 1593413122000, + "y": null, + }, + Object { + "x": 1593413123000, + "y": null, + }, + Object { + "x": 1593413124000, + "y": null, + }, + Object { + "x": 1593413125000, + "y": null, + }, + Object { + "x": 1593413126000, + "y": null, + }, + Object { + "x": 1593413127000, + "y": null, + }, + Object { + "x": 1593413128000, + "y": null, + }, + Object { + "x": 1593413129000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413131000, + "y": null, + }, + Object { + "x": 1593413132000, + "y": null, + }, + Object { + "x": 1593413133000, + "y": null, + }, + Object { + "x": 1593413134000, + "y": null, + }, + Object { + "x": 1593413135000, + "y": null, + }, + Object { + "x": 1593413136000, + "y": null, + }, + Object { + "x": 1593413137000, + "y": null, + }, + Object { + "x": 1593413138000, + "y": null, + }, + Object { + "x": 1593413139000, + "y": null, + }, + Object { + "x": 1593413140000, + "y": null, + }, + Object { + "x": 1593413141000, + "y": null, + }, + Object { + "x": 1593413142000, + "y": null, + }, + Object { + "x": 1593413143000, + "y": null, + }, + Object { + "x": 1593413144000, + "y": null, + }, + Object { + "x": 1593413145000, + "y": null, + }, + Object { + "x": 1593413146000, + "y": null, + }, + Object { + "x": 1593413147000, + "y": null, + }, + Object { + "x": 1593413148000, + "y": null, + }, + Object { + "x": 1593413149000, + "y": null, + }, + Object { + "x": 1593413150000, + "y": null, + }, + Object { + "x": 1593413151000, + "y": null, + }, + Object { + "x": 1593413152000, + "y": null, + }, + Object { + "x": 1593413153000, + "y": null, + }, + Object { + "x": 1593413154000, + "y": null, + }, + Object { + "x": 1593413155000, + "y": null, + }, + Object { + "x": 1593413156000, + "y": null, + }, + Object { + "x": 1593413157000, + "y": null, + }, + Object { + "x": 1593413158000, + "y": null, + }, + Object { + "x": 1593413159000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413161000, + "y": null, + }, + Object { + "x": 1593413162000, + "y": null, + }, + Object { + "x": 1593413163000, + "y": null, + }, + Object { + "x": 1593413164000, + "y": null, + }, + Object { + "x": 1593413165000, + "y": null, + }, + Object { + "x": 1593413166000, + "y": null, + }, + Object { + "x": 1593413167000, + "y": null, + }, + Object { + "x": 1593413168000, + "y": null, + }, + Object { + "x": 1593413169000, + "y": null, + }, + Object { + "x": 1593413170000, + "y": null, + }, + Object { + "x": 1593413171000, + "y": null, + }, + Object { + "x": 1593413172000, + "y": null, + }, + Object { + "x": 1593413173000, + "y": null, + }, + Object { + "x": 1593413174000, + "y": null, + }, + Object { + "x": 1593413175000, + "y": null, + }, + Object { + "x": 1593413176000, + "y": null, + }, + Object { + "x": 1593413177000, + "y": null, + }, + Object { + "x": 1593413178000, + "y": null, + }, + Object { + "x": 1593413179000, + "y": null, + }, + Object { + "x": 1593413180000, + "y": null, + }, + Object { + "x": 1593413181000, + "y": null, + }, + Object { + "x": 1593413182000, + "y": null, + }, + Object { + "x": 1593413183000, + "y": null, + }, + Object { + "x": 1593413184000, + "y": null, + }, + Object { + "x": 1593413185000, + "y": null, + }, + Object { + "x": 1593413186000, + "y": null, + }, + Object { + "x": 1593413187000, + "y": null, + }, + Object { + "x": 1593413188000, + "y": null, + }, + Object { + "x": 1593413189000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413191000, + "y": null, + }, + Object { + "x": 1593413192000, + "y": null, + }, + Object { + "x": 1593413193000, + "y": null, + }, + Object { + "x": 1593413194000, + "y": null, + }, + Object { + "x": 1593413195000, + "y": null, + }, + Object { + "x": 1593413196000, + "y": null, + }, + Object { + "x": 1593413197000, + "y": null, + }, + Object { + "x": 1593413198000, + "y": null, + }, + Object { + "x": 1593413199000, + "y": null, + }, + Object { + "x": 1593413200000, + "y": null, + }, + Object { + "x": 1593413201000, + "y": null, + }, + Object { + "x": 1593413202000, + "y": null, + }, + Object { + "x": 1593413203000, + "y": null, + }, + Object { + "x": 1593413204000, + "y": null, + }, + Object { + "x": 1593413205000, + "y": null, + }, + Object { + "x": 1593413206000, + "y": null, + }, + Object { + "x": 1593413207000, + "y": null, + }, + Object { + "x": 1593413208000, + "y": null, + }, + Object { + "x": 1593413209000, + "y": null, + }, + Object { + "x": 1593413210000, + "y": null, + }, + Object { + "x": 1593413211000, + "y": null, + }, + Object { + "x": 1593413212000, + "y": null, + }, + Object { + "x": 1593413213000, + "y": null, + }, + Object { + "x": 1593413214000, + "y": null, + }, + Object { + "x": 1593413215000, + "y": null, + }, + Object { + "x": 1593413216000, + "y": null, + }, + Object { + "x": 1593413217000, + "y": null, + }, + Object { + "x": 1593413218000, + "y": null, + }, + Object { + "x": 1593413219000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413221000, + "y": null, + }, + Object { + "x": 1593413222000, + "y": null, + }, + Object { + "x": 1593413223000, + "y": null, + }, + Object { + "x": 1593413224000, + "y": null, + }, + Object { + "x": 1593413225000, + "y": null, + }, + Object { + "x": 1593413226000, + "y": null, + }, + Object { + "x": 1593413227000, + "y": null, + }, + Object { + "x": 1593413228000, + "y": null, + }, + Object { + "x": 1593413229000, + "y": null, + }, + Object { + "x": 1593413230000, + "y": null, + }, + Object { + "x": 1593413231000, + "y": null, + }, + Object { + "x": 1593413232000, + "y": null, + }, + Object { + "x": 1593413233000, + "y": null, + }, + Object { + "x": 1593413234000, + "y": null, + }, + Object { + "x": 1593413235000, + "y": null, + }, + Object { + "x": 1593413236000, + "y": null, + }, + Object { + "x": 1593413237000, + "y": null, + }, + Object { + "x": 1593413238000, + "y": null, + }, + Object { + "x": 1593413239000, + "y": null, + }, + Object { + "x": 1593413240000, + "y": null, + }, + Object { + "x": 1593413241000, + "y": null, + }, + Object { + "x": 1593413242000, + "y": null, + }, + Object { + "x": 1593413243000, + "y": null, + }, + Object { + "x": 1593413244000, + "y": null, + }, + Object { + "x": 1593413245000, + "y": null, + }, + Object { + "x": 1593413246000, + "y": null, + }, + Object { + "x": 1593413247000, + "y": null, + }, + Object { + "x": 1593413248000, + "y": null, + }, + Object { + "x": 1593413249000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413251000, + "y": null, + }, + Object { + "x": 1593413252000, + "y": null, + }, + Object { + "x": 1593413253000, + "y": null, + }, + Object { + "x": 1593413254000, + "y": null, + }, + Object { + "x": 1593413255000, + "y": null, + }, + Object { + "x": 1593413256000, + "y": null, + }, + Object { + "x": 1593413257000, + "y": null, + }, + Object { + "x": 1593413258000, + "y": null, + }, + Object { + "x": 1593413259000, + "y": null, + }, + Object { + "x": 1593413260000, + "y": null, + }, + Object { + "x": 1593413261000, + "y": null, + }, + Object { + "x": 1593413262000, + "y": null, + }, + Object { + "x": 1593413263000, + "y": null, + }, + Object { + "x": 1593413264000, + "y": null, + }, + Object { + "x": 1593413265000, + "y": null, + }, + Object { + "x": 1593413266000, + "y": null, + }, + Object { + "x": 1593413267000, + "y": null, + }, + Object { + "x": 1593413268000, + "y": null, + }, + Object { + "x": 1593413269000, + "y": null, + }, + Object { + "x": 1593413270000, + "y": null, + }, + Object { + "x": 1593413271000, + "y": null, + }, + Object { + "x": 1593413272000, + "y": 45056, + }, + Object { + "x": 1593413273000, + "y": 10080, + }, + Object { + "x": 1593413274000, + "y": null, + }, + Object { + "x": 1593413275000, + "y": null, + }, + Object { + "x": 1593413276000, + "y": null, + }, + Object { + "x": 1593413277000, + "y": 37632, + }, + Object { + "x": 1593413278000, + "y": null, + }, + Object { + "x": 1593413279000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413281000, + "y": 33024, + }, + Object { + "x": 1593413282000, + "y": null, + }, + Object { + "x": 1593413283000, + "y": null, + }, + Object { + "x": 1593413284000, + "y": 761728, + }, + Object { + "x": 1593413285000, + "y": 81904, + }, + Object { + "x": 1593413286000, + "y": 358384, + }, + Object { + "x": 1593413287000, + "y": 36088, + }, + Object { + "x": 1593413288000, + "y": 44536, + }, + Object { + "x": 1593413289000, + "y": 11648, + }, + Object { + "x": 1593413290000, + "y": 31984, + }, + Object { + "x": 1593413291000, + "y": 2920, + }, + Object { + "x": 1593413292000, + "y": 9312, + }, + Object { + "x": 1593413293000, + "y": 10912, + }, + Object { + "x": 1593413294000, + "y": 6392, + }, + Object { + "x": 1593413295000, + "y": 11704, + }, + Object { + "x": 1593413296000, + "y": 10816, + }, + Object { + "x": 1593413297000, + "y": 12000, + }, + Object { + "x": 1593413298000, + "y": 15164, + }, + Object { + "x": 1593413299000, + "y": 3216, + }, + Object { + "x": 1593413300000, + "y": 9584, + }, + Object { + "x": 1593413301000, + "y": 21240, + }, + Object { + "x": 1593413302000, + "y": 5624, + }, + Object { + "x": 1593413303000, + "y": 11360, + }, + Object { + "x": 1593413304000, + "y": 12320, + }, + Object { + "x": 1593413305000, + "y": 38640, + }, + Object { + "x": 1593413306000, + "y": 9728, + }, + Object { + "x": 1593413307000, + "y": 17016, + }, + Object { + "x": 1593413308000, + "y": 26848, + }, + Object { + "x": 1593413309000, + "y": 1753072, + }, + Object { + "x": 1593413310000, + "y": 16992, + }, + Object { + "x": 1593413311000, + "y": 26560, + }, + Object { + "x": 1593413312000, + "y": 11232, + }, + Object { + "x": 1593413313000, + "y": 11424, + }, + Object { + "x": 1593413314000, + "y": 16096, + }, + Object { + "x": 1593413315000, + "y": 18800, + }, + Object { + "x": 1593413316000, + "y": 12672, + }, + Object { + "x": 1593413317000, + "y": 24316, + }, + Object { + "x": 1593413318000, + "y": 8944, + }, + Object { + "x": 1593413319000, + "y": 272352, + }, + Object { + "x": 1593413320000, + "y": 7992, + }, + Object { + "x": 1593413321000, + "y": 8368, + }, + Object { + "x": 1593413322000, + "y": 1928, + }, + Object { + "x": 1593413323000, + "y": null, + }, + Object { + "x": 1593413324000, + "y": null, + }, + Object { + "x": 1593413325000, + "y": null, + }, + Object { + "x": 1593413326000, + "y": null, + }, + Object { + "x": 1593413327000, + "y": null, + }, + Object { + "x": 1593413328000, + "y": null, + }, + Object { + "x": 1593413329000, + "y": null, + }, + Object { + "x": 1593413330000, + "y": null, + }, + Object { + "x": 1593413331000, + "y": null, + }, + Object { + "x": 1593413332000, + "y": null, + }, + Object { + "x": 1593413333000, + "y": null, + }, + Object { + "x": 1593413334000, + "y": null, + }, + Object { + "x": 1593413335000, + "y": null, + }, + Object { + "x": 1593413336000, + "y": null, + }, + Object { + "x": 1593413337000, + "y": null, + }, + Object { + "x": 1593413338000, + "y": null, + }, + Object { + "x": 1593413339000, + "y": null, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + "p99": Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413101000, + "y": null, + }, + Object { + "x": 1593413102000, + "y": null, + }, + Object { + "x": 1593413103000, + "y": null, + }, + Object { + "x": 1593413104000, + "y": null, + }, + Object { + "x": 1593413105000, + "y": null, + }, + Object { + "x": 1593413106000, + "y": null, + }, + Object { + "x": 1593413107000, + "y": null, + }, + Object { + "x": 1593413108000, + "y": null, + }, + Object { + "x": 1593413109000, + "y": null, + }, + Object { + "x": 1593413110000, + "y": null, + }, + Object { + "x": 1593413111000, + "y": null, + }, + Object { + "x": 1593413112000, + "y": null, + }, + Object { + "x": 1593413113000, + "y": null, + }, + Object { + "x": 1593413114000, + "y": null, + }, + Object { + "x": 1593413115000, + "y": null, + }, + Object { + "x": 1593413116000, + "y": null, + }, + Object { + "x": 1593413117000, + "y": null, + }, + Object { + "x": 1593413118000, + "y": null, + }, + Object { + "x": 1593413119000, + "y": null, + }, + Object { + "x": 1593413120000, + "y": null, + }, + Object { + "x": 1593413121000, + "y": null, + }, + Object { + "x": 1593413122000, + "y": null, + }, + Object { + "x": 1593413123000, + "y": null, + }, + Object { + "x": 1593413124000, + "y": null, + }, + Object { + "x": 1593413125000, + "y": null, + }, + Object { + "x": 1593413126000, + "y": null, + }, + Object { + "x": 1593413127000, + "y": null, + }, + Object { + "x": 1593413128000, + "y": null, + }, + Object { + "x": 1593413129000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413131000, + "y": null, + }, + Object { + "x": 1593413132000, + "y": null, + }, + Object { + "x": 1593413133000, + "y": null, + }, + Object { + "x": 1593413134000, + "y": null, + }, + Object { + "x": 1593413135000, + "y": null, + }, + Object { + "x": 1593413136000, + "y": null, + }, + Object { + "x": 1593413137000, + "y": null, + }, + Object { + "x": 1593413138000, + "y": null, + }, + Object { + "x": 1593413139000, + "y": null, + }, + Object { + "x": 1593413140000, + "y": null, + }, + Object { + "x": 1593413141000, + "y": null, + }, + Object { + "x": 1593413142000, + "y": null, + }, + Object { + "x": 1593413143000, + "y": null, + }, + Object { + "x": 1593413144000, + "y": null, + }, + Object { + "x": 1593413145000, + "y": null, + }, + Object { + "x": 1593413146000, + "y": null, + }, + Object { + "x": 1593413147000, + "y": null, + }, + Object { + "x": 1593413148000, + "y": null, + }, + Object { + "x": 1593413149000, + "y": null, + }, + Object { + "x": 1593413150000, + "y": null, + }, + Object { + "x": 1593413151000, + "y": null, + }, + Object { + "x": 1593413152000, + "y": null, + }, + Object { + "x": 1593413153000, + "y": null, + }, + Object { + "x": 1593413154000, + "y": null, + }, + Object { + "x": 1593413155000, + "y": null, + }, + Object { + "x": 1593413156000, + "y": null, + }, + Object { + "x": 1593413157000, + "y": null, + }, + Object { + "x": 1593413158000, + "y": null, + }, + Object { + "x": 1593413159000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413161000, + "y": null, + }, + Object { + "x": 1593413162000, + "y": null, + }, + Object { + "x": 1593413163000, + "y": null, + }, + Object { + "x": 1593413164000, + "y": null, + }, + Object { + "x": 1593413165000, + "y": null, + }, + Object { + "x": 1593413166000, + "y": null, + }, + Object { + "x": 1593413167000, + "y": null, + }, + Object { + "x": 1593413168000, + "y": null, + }, + Object { + "x": 1593413169000, + "y": null, + }, + Object { + "x": 1593413170000, + "y": null, + }, + Object { + "x": 1593413171000, + "y": null, + }, + Object { + "x": 1593413172000, + "y": null, + }, + Object { + "x": 1593413173000, + "y": null, + }, + Object { + "x": 1593413174000, + "y": null, + }, + Object { + "x": 1593413175000, + "y": null, + }, + Object { + "x": 1593413176000, + "y": null, + }, + Object { + "x": 1593413177000, + "y": null, + }, + Object { + "x": 1593413178000, + "y": null, + }, + Object { + "x": 1593413179000, + "y": null, + }, + Object { + "x": 1593413180000, + "y": null, + }, + Object { + "x": 1593413181000, + "y": null, + }, + Object { + "x": 1593413182000, + "y": null, + }, + Object { + "x": 1593413183000, + "y": null, + }, + Object { + "x": 1593413184000, + "y": null, + }, + Object { + "x": 1593413185000, + "y": null, + }, + Object { + "x": 1593413186000, + "y": null, + }, + Object { + "x": 1593413187000, + "y": null, + }, + Object { + "x": 1593413188000, + "y": null, + }, + Object { + "x": 1593413189000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413191000, + "y": null, + }, + Object { + "x": 1593413192000, + "y": null, + }, + Object { + "x": 1593413193000, + "y": null, + }, + Object { + "x": 1593413194000, + "y": null, + }, + Object { + "x": 1593413195000, + "y": null, + }, + Object { + "x": 1593413196000, + "y": null, + }, + Object { + "x": 1593413197000, + "y": null, + }, + Object { + "x": 1593413198000, + "y": null, + }, + Object { + "x": 1593413199000, + "y": null, + }, + Object { + "x": 1593413200000, + "y": null, + }, + Object { + "x": 1593413201000, + "y": null, + }, + Object { + "x": 1593413202000, + "y": null, + }, + Object { + "x": 1593413203000, + "y": null, + }, + Object { + "x": 1593413204000, + "y": null, + }, + Object { + "x": 1593413205000, + "y": null, + }, + Object { + "x": 1593413206000, + "y": null, + }, + Object { + "x": 1593413207000, + "y": null, + }, + Object { + "x": 1593413208000, + "y": null, + }, + Object { + "x": 1593413209000, + "y": null, + }, + Object { + "x": 1593413210000, + "y": null, + }, + Object { + "x": 1593413211000, + "y": null, + }, + Object { + "x": 1593413212000, + "y": null, + }, + Object { + "x": 1593413213000, + "y": null, + }, + Object { + "x": 1593413214000, + "y": null, + }, + Object { + "x": 1593413215000, + "y": null, + }, + Object { + "x": 1593413216000, + "y": null, + }, + Object { + "x": 1593413217000, + "y": null, + }, + Object { + "x": 1593413218000, + "y": null, + }, + Object { + "x": 1593413219000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413221000, + "y": null, + }, + Object { + "x": 1593413222000, + "y": null, + }, + Object { + "x": 1593413223000, + "y": null, + }, + Object { + "x": 1593413224000, + "y": null, + }, + Object { + "x": 1593413225000, + "y": null, + }, + Object { + "x": 1593413226000, + "y": null, + }, + Object { + "x": 1593413227000, + "y": null, + }, + Object { + "x": 1593413228000, + "y": null, + }, + Object { + "x": 1593413229000, + "y": null, + }, + Object { + "x": 1593413230000, + "y": null, + }, + Object { + "x": 1593413231000, + "y": null, + }, + Object { + "x": 1593413232000, + "y": null, + }, + Object { + "x": 1593413233000, + "y": null, + }, + Object { + "x": 1593413234000, + "y": null, + }, + Object { + "x": 1593413235000, + "y": null, + }, + Object { + "x": 1593413236000, + "y": null, + }, + Object { + "x": 1593413237000, + "y": null, + }, + Object { + "x": 1593413238000, + "y": null, + }, + Object { + "x": 1593413239000, + "y": null, + }, + Object { + "x": 1593413240000, + "y": null, + }, + Object { + "x": 1593413241000, + "y": null, + }, + Object { + "x": 1593413242000, + "y": null, + }, + Object { + "x": 1593413243000, + "y": null, + }, + Object { + "x": 1593413244000, + "y": null, + }, + Object { + "x": 1593413245000, + "y": null, + }, + Object { + "x": 1593413246000, + "y": null, + }, + Object { + "x": 1593413247000, + "y": null, + }, + Object { + "x": 1593413248000, + "y": null, + }, + Object { + "x": 1593413249000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413251000, + "y": null, + }, + Object { + "x": 1593413252000, + "y": null, + }, + Object { + "x": 1593413253000, + "y": null, + }, + Object { + "x": 1593413254000, + "y": null, + }, + Object { + "x": 1593413255000, + "y": null, + }, + Object { + "x": 1593413256000, + "y": null, + }, + Object { + "x": 1593413257000, + "y": null, + }, + Object { + "x": 1593413258000, + "y": null, + }, + Object { + "x": 1593413259000, + "y": null, + }, + Object { + "x": 1593413260000, + "y": null, + }, + Object { + "x": 1593413261000, + "y": null, + }, + Object { + "x": 1593413262000, + "y": null, + }, + Object { + "x": 1593413263000, + "y": null, + }, + Object { + "x": 1593413264000, + "y": null, + }, + Object { + "x": 1593413265000, + "y": null, + }, + Object { + "x": 1593413266000, + "y": null, + }, + Object { + "x": 1593413267000, + "y": null, + }, + Object { + "x": 1593413268000, + "y": null, + }, + Object { + "x": 1593413269000, + "y": null, + }, + Object { + "x": 1593413270000, + "y": null, + }, + Object { + "x": 1593413271000, + "y": null, + }, + Object { + "x": 1593413272000, + "y": 45056, + }, + Object { + "x": 1593413273000, + "y": 10080, + }, + Object { + "x": 1593413274000, + "y": null, + }, + Object { + "x": 1593413275000, + "y": null, + }, + Object { + "x": 1593413276000, + "y": null, + }, + Object { + "x": 1593413277000, + "y": 37632, + }, + Object { + "x": 1593413278000, + "y": null, + }, + Object { + "x": 1593413279000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413281000, + "y": 33024, + }, + Object { + "x": 1593413282000, + "y": null, + }, + Object { + "x": 1593413283000, + "y": null, + }, + Object { + "x": 1593413284000, + "y": 761728, + }, + Object { + "x": 1593413285000, + "y": 81904, + }, + Object { + "x": 1593413286000, + "y": 358384, + }, + Object { + "x": 1593413287000, + "y": 36088, + }, + Object { + "x": 1593413288000, + "y": 44536, + }, + Object { + "x": 1593413289000, + "y": 11648, + }, + Object { + "x": 1593413290000, + "y": 31984, + }, + Object { + "x": 1593413291000, + "y": 2920, + }, + Object { + "x": 1593413292000, + "y": 9312, + }, + Object { + "x": 1593413293000, + "y": 10912, + }, + Object { + "x": 1593413294000, + "y": 6392, + }, + Object { + "x": 1593413295000, + "y": 11704, + }, + Object { + "x": 1593413296000, + "y": 10816, + }, + Object { + "x": 1593413297000, + "y": 12000, + }, + Object { + "x": 1593413298000, + "y": 15164, + }, + Object { + "x": 1593413299000, + "y": 3216, + }, + Object { + "x": 1593413300000, + "y": 9584, + }, + Object { + "x": 1593413301000, + "y": 21240, + }, + Object { + "x": 1593413302000, + "y": 5624, + }, + Object { + "x": 1593413303000, + "y": 11360, + }, + Object { + "x": 1593413304000, + "y": 12320, + }, + Object { + "x": 1593413305000, + "y": 38640, + }, + Object { + "x": 1593413306000, + "y": 9728, + }, + Object { + "x": 1593413307000, + "y": 17016, + }, + Object { + "x": 1593413308000, + "y": 26848, + }, + Object { + "x": 1593413309000, + "y": 1753072, + }, + Object { + "x": 1593413310000, + "y": 16992, + }, + Object { + "x": 1593413311000, + "y": 26560, + }, + Object { + "x": 1593413312000, + "y": 11232, + }, + Object { + "x": 1593413313000, + "y": 11424, + }, + Object { + "x": 1593413314000, + "y": 16096, + }, + Object { + "x": 1593413315000, + "y": 18800, + }, + Object { + "x": 1593413316000, + "y": 12672, + }, + Object { + "x": 1593413317000, + "y": 24316, + }, + Object { + "x": 1593413318000, + "y": 8944, + }, + Object { + "x": 1593413319000, + "y": 272352, + }, + Object { + "x": 1593413320000, + "y": 7992, + }, + Object { + "x": 1593413321000, + "y": 8368, + }, + Object { + "x": 1593413322000, + "y": 1928, + }, + Object { + "x": 1593413323000, + "y": null, + }, + Object { + "x": 1593413324000, + "y": null, + }, + Object { + "x": 1593413325000, + "y": null, + }, + Object { + "x": 1593413326000, + "y": null, + }, + Object { + "x": 1593413327000, + "y": null, + }, + Object { + "x": 1593413328000, + "y": null, + }, + Object { + "x": 1593413329000, + "y": null, + }, + Object { + "x": 1593413330000, + "y": null, + }, + Object { + "x": 1593413331000, + "y": null, + }, + Object { + "x": 1593413332000, + "y": null, + }, + Object { + "x": 1593413333000, + "y": null, + }, + Object { + "x": 1593413334000, + "y": null, + }, + Object { + "x": 1593413335000, + "y": null, + }, + Object { + "x": 1593413336000, + "y": null, + }, + Object { + "x": 1593413337000, + "y": null, + }, + Object { + "x": 1593413338000, + "y": null, + }, + Object { + "x": 1593413339000, + "y": null, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ], + }, + "tpmBuckets": Array [ + Object { + "avg": 24.75, + "dataPoints": Array [ + Object { + "x": 1593413100000, + "y": 0, + }, + Object { + "x": 1593413101000, + "y": 0, + }, + Object { + "x": 1593413102000, + "y": 0, + }, + Object { + "x": 1593413103000, + "y": 0, + }, + Object { + "x": 1593413104000, + "y": 0, + }, + Object { + "x": 1593413105000, + "y": 0, + }, + Object { + "x": 1593413106000, + "y": 0, + }, + Object { + "x": 1593413107000, + "y": 0, + }, + Object { + "x": 1593413108000, + "y": 0, + }, + Object { + "x": 1593413109000, + "y": 0, + }, + Object { + "x": 1593413110000, + "y": 0, + }, + Object { + "x": 1593413111000, + "y": 0, + }, + Object { + "x": 1593413112000, + "y": 0, + }, + Object { + "x": 1593413113000, + "y": 0, + }, + Object { + "x": 1593413114000, + "y": 0, + }, + Object { + "x": 1593413115000, + "y": 0, + }, + Object { + "x": 1593413116000, + "y": 0, + }, + Object { + "x": 1593413117000, + "y": 0, + }, + Object { + "x": 1593413118000, + "y": 0, + }, + Object { + "x": 1593413119000, + "y": 0, + }, + Object { + "x": 1593413120000, + "y": 0, + }, + Object { + "x": 1593413121000, + "y": 0, + }, + Object { + "x": 1593413122000, + "y": 0, + }, + Object { + "x": 1593413123000, + "y": 0, + }, + Object { + "x": 1593413124000, + "y": 0, + }, + Object { + "x": 1593413125000, + "y": 0, + }, + Object { + "x": 1593413126000, + "y": 0, + }, + Object { + "x": 1593413127000, + "y": 0, + }, + Object { + "x": 1593413128000, + "y": 0, + }, + Object { + "x": 1593413129000, + "y": 0, + }, + Object { + "x": 1593413130000, + "y": 0, + }, + Object { + "x": 1593413131000, + "y": 0, + }, + Object { + "x": 1593413132000, + "y": 0, + }, + Object { + "x": 1593413133000, + "y": 0, + }, + Object { + "x": 1593413134000, + "y": 0, + }, + Object { + "x": 1593413135000, + "y": 0, + }, + Object { + "x": 1593413136000, + "y": 0, + }, + Object { + "x": 1593413137000, + "y": 0, + }, + Object { + "x": 1593413138000, + "y": 0, + }, + Object { + "x": 1593413139000, + "y": 0, + }, + Object { + "x": 1593413140000, + "y": 0, + }, + Object { + "x": 1593413141000, + "y": 0, + }, + Object { + "x": 1593413142000, + "y": 0, + }, + Object { + "x": 1593413143000, + "y": 0, + }, + Object { + "x": 1593413144000, + "y": 0, + }, + Object { + "x": 1593413145000, + "y": 0, + }, + Object { + "x": 1593413146000, + "y": 0, + }, + Object { + "x": 1593413147000, + "y": 0, + }, + Object { + "x": 1593413148000, + "y": 0, + }, + Object { + "x": 1593413149000, + "y": 0, + }, + Object { + "x": 1593413150000, + "y": 0, + }, + Object { + "x": 1593413151000, + "y": 0, + }, + Object { + "x": 1593413152000, + "y": 0, + }, + Object { + "x": 1593413153000, + "y": 0, + }, + Object { + "x": 1593413154000, + "y": 0, + }, + Object { + "x": 1593413155000, + "y": 0, + }, + Object { + "x": 1593413156000, + "y": 0, + }, + Object { + "x": 1593413157000, + "y": 0, + }, + Object { + "x": 1593413158000, + "y": 0, + }, + Object { + "x": 1593413159000, + "y": 0, + }, + Object { + "x": 1593413160000, + "y": 0, + }, + Object { + "x": 1593413161000, + "y": 0, + }, + Object { + "x": 1593413162000, + "y": 0, + }, + Object { + "x": 1593413163000, + "y": 0, + }, + Object { + "x": 1593413164000, + "y": 0, + }, + Object { + "x": 1593413165000, + "y": 0, + }, + Object { + "x": 1593413166000, + "y": 0, + }, + Object { + "x": 1593413167000, + "y": 0, + }, + Object { + "x": 1593413168000, + "y": 0, + }, + Object { + "x": 1593413169000, + "y": 0, + }, + Object { + "x": 1593413170000, + "y": 0, + }, + Object { + "x": 1593413171000, + "y": 0, + }, + Object { + "x": 1593413172000, + "y": 0, + }, + Object { + "x": 1593413173000, + "y": 0, + }, + Object { + "x": 1593413174000, + "y": 0, + }, + Object { + "x": 1593413175000, + "y": 0, + }, + Object { + "x": 1593413176000, + "y": 0, + }, + Object { + "x": 1593413177000, + "y": 0, + }, + Object { + "x": 1593413178000, + "y": 0, + }, + Object { + "x": 1593413179000, + "y": 0, + }, + Object { + "x": 1593413180000, + "y": 0, + }, + Object { + "x": 1593413181000, + "y": 0, + }, + Object { + "x": 1593413182000, + "y": 0, + }, + Object { + "x": 1593413183000, + "y": 0, + }, + Object { + "x": 1593413184000, + "y": 0, + }, + Object { + "x": 1593413185000, + "y": 0, + }, + Object { + "x": 1593413186000, + "y": 0, + }, + Object { + "x": 1593413187000, + "y": 0, + }, + Object { + "x": 1593413188000, + "y": 0, + }, + Object { + "x": 1593413189000, + "y": 0, + }, + Object { + "x": 1593413190000, + "y": 0, + }, + Object { + "x": 1593413191000, + "y": 0, + }, + Object { + "x": 1593413192000, + "y": 0, + }, + Object { + "x": 1593413193000, + "y": 0, + }, + Object { + "x": 1593413194000, + "y": 0, + }, + Object { + "x": 1593413195000, + "y": 0, + }, + Object { + "x": 1593413196000, + "y": 0, + }, + Object { + "x": 1593413197000, + "y": 0, + }, + Object { + "x": 1593413198000, + "y": 0, + }, + Object { + "x": 1593413199000, + "y": 0, + }, + Object { + "x": 1593413200000, + "y": 0, + }, + Object { + "x": 1593413201000, + "y": 0, + }, + Object { + "x": 1593413202000, + "y": 0, + }, + Object { + "x": 1593413203000, + "y": 0, + }, + Object { + "x": 1593413204000, + "y": 0, + }, + Object { + "x": 1593413205000, + "y": 0, + }, + Object { + "x": 1593413206000, + "y": 0, + }, + Object { + "x": 1593413207000, + "y": 0, + }, + Object { + "x": 1593413208000, + "y": 0, + }, + Object { + "x": 1593413209000, + "y": 0, + }, + Object { + "x": 1593413210000, + "y": 0, + }, + Object { + "x": 1593413211000, + "y": 0, + }, + Object { + "x": 1593413212000, + "y": 0, + }, + Object { + "x": 1593413213000, + "y": 0, + }, + Object { + "x": 1593413214000, + "y": 0, + }, + Object { + "x": 1593413215000, + "y": 0, + }, + Object { + "x": 1593413216000, + "y": 0, + }, + Object { + "x": 1593413217000, + "y": 0, + }, + Object { + "x": 1593413218000, + "y": 0, + }, + Object { + "x": 1593413219000, + "y": 0, + }, + Object { + "x": 1593413220000, + "y": 0, + }, + Object { + "x": 1593413221000, + "y": 0, + }, + Object { + "x": 1593413222000, + "y": 0, + }, + Object { + "x": 1593413223000, + "y": 0, + }, + Object { + "x": 1593413224000, + "y": 0, + }, + Object { + "x": 1593413225000, + "y": 0, + }, + Object { + "x": 1593413226000, + "y": 0, + }, + Object { + "x": 1593413227000, + "y": 0, + }, + Object { + "x": 1593413228000, + "y": 0, + }, + Object { + "x": 1593413229000, + "y": 0, + }, + Object { + "x": 1593413230000, + "y": 0, + }, + Object { + "x": 1593413231000, + "y": 0, + }, + Object { + "x": 1593413232000, + "y": 0, + }, + Object { + "x": 1593413233000, + "y": 0, + }, + Object { + "x": 1593413234000, + "y": 0, + }, + Object { + "x": 1593413235000, + "y": 0, + }, + Object { + "x": 1593413236000, + "y": 0, + }, + Object { + "x": 1593413237000, + "y": 0, + }, + Object { + "x": 1593413238000, + "y": 0, + }, + Object { + "x": 1593413239000, + "y": 0, + }, + Object { + "x": 1593413240000, + "y": 0, + }, + Object { + "x": 1593413241000, + "y": 0, + }, + Object { + "x": 1593413242000, + "y": 0, + }, + Object { + "x": 1593413243000, + "y": 0, + }, + Object { + "x": 1593413244000, + "y": 0, + }, + Object { + "x": 1593413245000, + "y": 0, + }, + Object { + "x": 1593413246000, + "y": 0, + }, + Object { + "x": 1593413247000, + "y": 0, + }, + Object { + "x": 1593413248000, + "y": 0, + }, + Object { + "x": 1593413249000, + "y": 0, + }, + Object { + "x": 1593413250000, + "y": 0, + }, + Object { + "x": 1593413251000, + "y": 0, + }, + Object { + "x": 1593413252000, + "y": 0, + }, + Object { + "x": 1593413253000, + "y": 0, + }, + Object { + "x": 1593413254000, + "y": 0, + }, + Object { + "x": 1593413255000, + "y": 0, + }, + Object { + "x": 1593413256000, + "y": 0, + }, + Object { + "x": 1593413257000, + "y": 0, + }, + Object { + "x": 1593413258000, + "y": 0, + }, + Object { + "x": 1593413259000, + "y": 0, + }, + Object { + "x": 1593413260000, + "y": 0, + }, + Object { + "x": 1593413261000, + "y": 0, + }, + Object { + "x": 1593413262000, + "y": 0, + }, + Object { + "x": 1593413263000, + "y": 0, + }, + Object { + "x": 1593413264000, + "y": 0, + }, + Object { + "x": 1593413265000, + "y": 0, + }, + Object { + "x": 1593413266000, + "y": 0, + }, + Object { + "x": 1593413267000, + "y": 0, + }, + Object { + "x": 1593413268000, + "y": 0, + }, + Object { + "x": 1593413269000, + "y": 0, + }, + Object { + "x": 1593413270000, + "y": 0, + }, + Object { + "x": 1593413271000, + "y": 0, + }, + Object { + "x": 1593413272000, + "y": 1, + }, + Object { + "x": 1593413273000, + "y": 2, + }, + Object { + "x": 1593413274000, + "y": 0, + }, + Object { + "x": 1593413275000, + "y": 0, + }, + Object { + "x": 1593413276000, + "y": 0, + }, + Object { + "x": 1593413277000, + "y": 1, + }, + Object { + "x": 1593413278000, + "y": 0, + }, + Object { + "x": 1593413279000, + "y": 0, + }, + Object { + "x": 1593413280000, + "y": 0, + }, + Object { + "x": 1593413281000, + "y": 1, + }, + Object { + "x": 1593413282000, + "y": 0, + }, + Object { + "x": 1593413283000, + "y": 0, + }, + Object { + "x": 1593413284000, + "y": 2, + }, + Object { + "x": 1593413285000, + "y": 2, + }, + Object { + "x": 1593413286000, + "y": 7, + }, + Object { + "x": 1593413287000, + "y": 1, + }, + Object { + "x": 1593413288000, + "y": 2, + }, + Object { + "x": 1593413289000, + "y": 1, + }, + Object { + "x": 1593413290000, + "y": 4, + }, + Object { + "x": 1593413291000, + "y": 2, + }, + Object { + "x": 1593413292000, + "y": 1, + }, + Object { + "x": 1593413293000, + "y": 2, + }, + Object { + "x": 1593413294000, + "y": 3, + }, + Object { + "x": 1593413295000, + "y": 2, + }, + Object { + "x": 1593413296000, + "y": 2, + }, + Object { + "x": 1593413297000, + "y": 2, + }, + Object { + "x": 1593413298000, + "y": 6, + }, + Object { + "x": 1593413299000, + "y": 1, + }, + Object { + "x": 1593413300000, + "y": 2, + }, + Object { + "x": 1593413301000, + "y": 3, + }, + Object { + "x": 1593413302000, + "y": 2, + }, + Object { + "x": 1593413303000, + "y": 2, + }, + Object { + "x": 1593413304000, + "y": 2, + }, + Object { + "x": 1593413305000, + "y": 1, + }, + Object { + "x": 1593413306000, + "y": 2, + }, + Object { + "x": 1593413307000, + "y": 3, + }, + Object { + "x": 1593413308000, + "y": 2, + }, + Object { + "x": 1593413309000, + "y": 2, + }, + Object { + "x": 1593413310000, + "y": 2, + }, + Object { + "x": 1593413311000, + "y": 1, + }, + Object { + "x": 1593413312000, + "y": 3, + }, + Object { + "x": 1593413313000, + "y": 3, + }, + Object { + "x": 1593413314000, + "y": 5, + }, + Object { + "x": 1593413315000, + "y": 2, + }, + Object { + "x": 1593413316000, + "y": 2, + }, + Object { + "x": 1593413317000, + "y": 6, + }, + Object { + "x": 1593413318000, + "y": 2, + }, + Object { + "x": 1593413319000, + "y": 2, + }, + Object { + "x": 1593413320000, + "y": 2, + }, + Object { + "x": 1593413321000, + "y": 2, + }, + Object { + "x": 1593413322000, + "y": 1, + }, + Object { + "x": 1593413323000, + "y": 0, + }, + Object { + "x": 1593413324000, + "y": 0, + }, + Object { + "x": 1593413325000, + "y": 0, + }, + Object { + "x": 1593413326000, + "y": 0, + }, + Object { + "x": 1593413327000, + "y": 0, + }, + Object { + "x": 1593413328000, + "y": 0, + }, + Object { + "x": 1593413329000, + "y": 0, + }, + Object { + "x": 1593413330000, + "y": 0, + }, + Object { + "x": 1593413331000, + "y": 0, + }, + Object { + "x": 1593413332000, + "y": 0, + }, + Object { + "x": 1593413333000, + "y": 0, + }, + Object { + "x": 1593413334000, + "y": 0, + }, + Object { + "x": 1593413335000, + "y": 0, + }, + Object { + "x": 1593413336000, + "y": 0, + }, + Object { + "x": 1593413337000, + "y": 0, + }, + Object { + "x": 1593413338000, + "y": 0, + }, + Object { + "x": 1593413339000, + "y": 0, + }, + Object { + "x": 1593413340000, + "y": 0, + }, + ], + "key": "HTTP 2xx", + }, + Object { + "avg": 1.75, + "dataPoints": Array [ + Object { + "x": 1593413100000, + "y": 0, + }, + Object { + "x": 1593413101000, + "y": 0, + }, + Object { + "x": 1593413102000, + "y": 0, + }, + Object { + "x": 1593413103000, + "y": 0, + }, + Object { + "x": 1593413104000, + "y": 0, + }, + Object { + "x": 1593413105000, + "y": 0, + }, + Object { + "x": 1593413106000, + "y": 0, + }, + Object { + "x": 1593413107000, + "y": 0, + }, + Object { + "x": 1593413108000, + "y": 0, + }, + Object { + "x": 1593413109000, + "y": 0, + }, + Object { + "x": 1593413110000, + "y": 0, + }, + Object { + "x": 1593413111000, + "y": 0, + }, + Object { + "x": 1593413112000, + "y": 0, + }, + Object { + "x": 1593413113000, + "y": 0, + }, + Object { + "x": 1593413114000, + "y": 0, + }, + Object { + "x": 1593413115000, + "y": 0, + }, + Object { + "x": 1593413116000, + "y": 0, + }, + Object { + "x": 1593413117000, + "y": 0, + }, + Object { + "x": 1593413118000, + "y": 0, + }, + Object { + "x": 1593413119000, + "y": 0, + }, + Object { + "x": 1593413120000, + "y": 0, + }, + Object { + "x": 1593413121000, + "y": 0, + }, + Object { + "x": 1593413122000, + "y": 0, + }, + Object { + "x": 1593413123000, + "y": 0, + }, + Object { + "x": 1593413124000, + "y": 0, + }, + Object { + "x": 1593413125000, + "y": 0, + }, + Object { + "x": 1593413126000, + "y": 0, + }, + Object { + "x": 1593413127000, + "y": 0, + }, + Object { + "x": 1593413128000, + "y": 0, + }, + Object { + "x": 1593413129000, + "y": 0, + }, + Object { + "x": 1593413130000, + "y": 0, + }, + Object { + "x": 1593413131000, + "y": 0, + }, + Object { + "x": 1593413132000, + "y": 0, + }, + Object { + "x": 1593413133000, + "y": 0, + }, + Object { + "x": 1593413134000, + "y": 0, + }, + Object { + "x": 1593413135000, + "y": 0, + }, + Object { + "x": 1593413136000, + "y": 0, + }, + Object { + "x": 1593413137000, + "y": 0, + }, + Object { + "x": 1593413138000, + "y": 0, + }, + Object { + "x": 1593413139000, + "y": 0, + }, + Object { + "x": 1593413140000, + "y": 0, + }, + Object { + "x": 1593413141000, + "y": 0, + }, + Object { + "x": 1593413142000, + "y": 0, + }, + Object { + "x": 1593413143000, + "y": 0, + }, + Object { + "x": 1593413144000, + "y": 0, + }, + Object { + "x": 1593413145000, + "y": 0, + }, + Object { + "x": 1593413146000, + "y": 0, + }, + Object { + "x": 1593413147000, + "y": 0, + }, + Object { + "x": 1593413148000, + "y": 0, + }, + Object { + "x": 1593413149000, + "y": 0, + }, + Object { + "x": 1593413150000, + "y": 0, + }, + Object { + "x": 1593413151000, + "y": 0, + }, + Object { + "x": 1593413152000, + "y": 0, + }, + Object { + "x": 1593413153000, + "y": 0, + }, + Object { + "x": 1593413154000, + "y": 0, + }, + Object { + "x": 1593413155000, + "y": 0, + }, + Object { + "x": 1593413156000, + "y": 0, + }, + Object { + "x": 1593413157000, + "y": 0, + }, + Object { + "x": 1593413158000, + "y": 0, + }, + Object { + "x": 1593413159000, + "y": 0, + }, + Object { + "x": 1593413160000, + "y": 0, + }, + Object { + "x": 1593413161000, + "y": 0, + }, + Object { + "x": 1593413162000, + "y": 0, + }, + Object { + "x": 1593413163000, + "y": 0, + }, + Object { + "x": 1593413164000, + "y": 0, + }, + Object { + "x": 1593413165000, + "y": 0, + }, + Object { + "x": 1593413166000, + "y": 0, + }, + Object { + "x": 1593413167000, + "y": 0, + }, + Object { + "x": 1593413168000, + "y": 0, + }, + Object { + "x": 1593413169000, + "y": 0, + }, + Object { + "x": 1593413170000, + "y": 0, + }, + Object { + "x": 1593413171000, + "y": 0, + }, + Object { + "x": 1593413172000, + "y": 0, + }, + Object { + "x": 1593413173000, + "y": 0, + }, + Object { + "x": 1593413174000, + "y": 0, + }, + Object { + "x": 1593413175000, + "y": 0, + }, + Object { + "x": 1593413176000, + "y": 0, + }, + Object { + "x": 1593413177000, + "y": 0, + }, + Object { + "x": 1593413178000, + "y": 0, + }, + Object { + "x": 1593413179000, + "y": 0, + }, + Object { + "x": 1593413180000, + "y": 0, + }, + Object { + "x": 1593413181000, + "y": 0, + }, + Object { + "x": 1593413182000, + "y": 0, + }, + Object { + "x": 1593413183000, + "y": 0, + }, + Object { + "x": 1593413184000, + "y": 0, + }, + Object { + "x": 1593413185000, + "y": 0, + }, + Object { + "x": 1593413186000, + "y": 0, + }, + Object { + "x": 1593413187000, + "y": 0, + }, + Object { + "x": 1593413188000, + "y": 0, + }, + Object { + "x": 1593413189000, + "y": 0, + }, + Object { + "x": 1593413190000, + "y": 0, + }, + Object { + "x": 1593413191000, + "y": 0, + }, + Object { + "x": 1593413192000, + "y": 0, + }, + Object { + "x": 1593413193000, + "y": 0, + }, + Object { + "x": 1593413194000, + "y": 0, + }, + Object { + "x": 1593413195000, + "y": 0, + }, + Object { + "x": 1593413196000, + "y": 0, + }, + Object { + "x": 1593413197000, + "y": 0, + }, + Object { + "x": 1593413198000, + "y": 0, + }, + Object { + "x": 1593413199000, + "y": 0, + }, + Object { + "x": 1593413200000, + "y": 0, + }, + Object { + "x": 1593413201000, + "y": 0, + }, + Object { + "x": 1593413202000, + "y": 0, + }, + Object { + "x": 1593413203000, + "y": 0, + }, + Object { + "x": 1593413204000, + "y": 0, + }, + Object { + "x": 1593413205000, + "y": 0, + }, + Object { + "x": 1593413206000, + "y": 0, + }, + Object { + "x": 1593413207000, + "y": 0, + }, + Object { + "x": 1593413208000, + "y": 0, + }, + Object { + "x": 1593413209000, + "y": 0, + }, + Object { + "x": 1593413210000, + "y": 0, + }, + Object { + "x": 1593413211000, + "y": 0, + }, + Object { + "x": 1593413212000, + "y": 0, + }, + Object { + "x": 1593413213000, + "y": 0, + }, + Object { + "x": 1593413214000, + "y": 0, + }, + Object { + "x": 1593413215000, + "y": 0, + }, + Object { + "x": 1593413216000, + "y": 0, + }, + Object { + "x": 1593413217000, + "y": 0, + }, + Object { + "x": 1593413218000, + "y": 0, + }, + Object { + "x": 1593413219000, + "y": 0, + }, + Object { + "x": 1593413220000, + "y": 0, + }, + Object { + "x": 1593413221000, + "y": 0, + }, + Object { + "x": 1593413222000, + "y": 0, + }, + Object { + "x": 1593413223000, + "y": 0, + }, + Object { + "x": 1593413224000, + "y": 0, + }, + Object { + "x": 1593413225000, + "y": 0, + }, + Object { + "x": 1593413226000, + "y": 0, + }, + Object { + "x": 1593413227000, + "y": 0, + }, + Object { + "x": 1593413228000, + "y": 0, + }, + Object { + "x": 1593413229000, + "y": 0, + }, + Object { + "x": 1593413230000, + "y": 0, + }, + Object { + "x": 1593413231000, + "y": 0, + }, + Object { + "x": 1593413232000, + "y": 0, + }, + Object { + "x": 1593413233000, + "y": 0, + }, + Object { + "x": 1593413234000, + "y": 0, + }, + Object { + "x": 1593413235000, + "y": 0, + }, + Object { + "x": 1593413236000, + "y": 0, + }, + Object { + "x": 1593413237000, + "y": 0, + }, + Object { + "x": 1593413238000, + "y": 0, + }, + Object { + "x": 1593413239000, + "y": 0, + }, + Object { + "x": 1593413240000, + "y": 0, + }, + Object { + "x": 1593413241000, + "y": 0, + }, + Object { + "x": 1593413242000, + "y": 0, + }, + Object { + "x": 1593413243000, + "y": 0, + }, + Object { + "x": 1593413244000, + "y": 0, + }, + Object { + "x": 1593413245000, + "y": 0, + }, + Object { + "x": 1593413246000, + "y": 0, + }, + Object { + "x": 1593413247000, + "y": 0, + }, + Object { + "x": 1593413248000, + "y": 0, + }, + Object { + "x": 1593413249000, + "y": 0, + }, + Object { + "x": 1593413250000, + "y": 0, + }, + Object { + "x": 1593413251000, + "y": 0, + }, + Object { + "x": 1593413252000, + "y": 0, + }, + Object { + "x": 1593413253000, + "y": 0, + }, + Object { + "x": 1593413254000, + "y": 0, + }, + Object { + "x": 1593413255000, + "y": 0, + }, + Object { + "x": 1593413256000, + "y": 0, + }, + Object { + "x": 1593413257000, + "y": 0, + }, + Object { + "x": 1593413258000, + "y": 0, + }, + Object { + "x": 1593413259000, + "y": 0, + }, + Object { + "x": 1593413260000, + "y": 0, + }, + Object { + "x": 1593413261000, + "y": 0, + }, + Object { + "x": 1593413262000, + "y": 0, + }, + Object { + "x": 1593413263000, + "y": 0, + }, + Object { + "x": 1593413264000, + "y": 0, + }, + Object { + "x": 1593413265000, + "y": 0, + }, + Object { + "x": 1593413266000, + "y": 0, + }, + Object { + "x": 1593413267000, + "y": 0, + }, + Object { + "x": 1593413268000, + "y": 0, + }, + Object { + "x": 1593413269000, + "y": 0, + }, + Object { + "x": 1593413270000, + "y": 0, + }, + Object { + "x": 1593413271000, + "y": 0, + }, + Object { + "x": 1593413272000, + "y": 0, + }, + Object { + "x": 1593413273000, + "y": 0, + }, + Object { + "x": 1593413274000, + "y": 0, + }, + Object { + "x": 1593413275000, + "y": 0, + }, + Object { + "x": 1593413276000, + "y": 0, + }, + Object { + "x": 1593413277000, + "y": 0, + }, + Object { + "x": 1593413278000, + "y": 0, + }, + Object { + "x": 1593413279000, + "y": 0, + }, + Object { + "x": 1593413280000, + "y": 0, + }, + Object { + "x": 1593413281000, + "y": 0, + }, + Object { + "x": 1593413282000, + "y": 0, + }, + Object { + "x": 1593413283000, + "y": 0, + }, + Object { + "x": 1593413284000, + "y": 0, + }, + Object { + "x": 1593413285000, + "y": 0, + }, + Object { + "x": 1593413286000, + "y": 0, + }, + Object { + "x": 1593413287000, + "y": 0, + }, + Object { + "x": 1593413288000, + "y": 0, + }, + Object { + "x": 1593413289000, + "y": 0, + }, + Object { + "x": 1593413290000, + "y": 0, + }, + Object { + "x": 1593413291000, + "y": 0, + }, + Object { + "x": 1593413292000, + "y": 0, + }, + Object { + "x": 1593413293000, + "y": 0, + }, + Object { + "x": 1593413294000, + "y": 0, + }, + Object { + "x": 1593413295000, + "y": 0, + }, + Object { + "x": 1593413296000, + "y": 0, + }, + Object { + "x": 1593413297000, + "y": 0, + }, + Object { + "x": 1593413298000, + "y": 2, + }, + Object { + "x": 1593413299000, + "y": 0, + }, + Object { + "x": 1593413300000, + "y": 0, + }, + Object { + "x": 1593413301000, + "y": 3, + }, + Object { + "x": 1593413302000, + "y": 0, + }, + Object { + "x": 1593413303000, + "y": 0, + }, + Object { + "x": 1593413304000, + "y": 0, + }, + Object { + "x": 1593413305000, + "y": 0, + }, + Object { + "x": 1593413306000, + "y": 0, + }, + Object { + "x": 1593413307000, + "y": 0, + }, + Object { + "x": 1593413308000, + "y": 0, + }, + Object { + "x": 1593413309000, + "y": 0, + }, + Object { + "x": 1593413310000, + "y": 0, + }, + Object { + "x": 1593413311000, + "y": 0, + }, + Object { + "x": 1593413312000, + "y": 0, + }, + Object { + "x": 1593413313000, + "y": 0, + }, + Object { + "x": 1593413314000, + "y": 0, + }, + Object { + "x": 1593413315000, + "y": 0, + }, + Object { + "x": 1593413316000, + "y": 0, + }, + Object { + "x": 1593413317000, + "y": 2, + }, + Object { + "x": 1593413318000, + "y": 0, + }, + Object { + "x": 1593413319000, + "y": 0, + }, + Object { + "x": 1593413320000, + "y": 0, + }, + Object { + "x": 1593413321000, + "y": 0, + }, + Object { + "x": 1593413322000, + "y": 0, + }, + Object { + "x": 1593413323000, + "y": 0, + }, + Object { + "x": 1593413324000, + "y": 0, + }, + Object { + "x": 1593413325000, + "y": 0, + }, + Object { + "x": 1593413326000, + "y": 0, + }, + Object { + "x": 1593413327000, + "y": 0, + }, + Object { + "x": 1593413328000, + "y": 0, + }, + Object { + "x": 1593413329000, + "y": 0, + }, + Object { + "x": 1593413330000, + "y": 0, + }, + Object { + "x": 1593413331000, + "y": 0, + }, + Object { + "x": 1593413332000, + "y": 0, + }, + Object { + "x": 1593413333000, + "y": 0, + }, + Object { + "x": 1593413334000, + "y": 0, + }, + Object { + "x": 1593413335000, + "y": 0, + }, + Object { + "x": 1593413336000, + "y": 0, + }, + Object { + "x": 1593413337000, + "y": 0, + }, + Object { + "x": 1593413338000, + "y": 0, + }, + Object { + "x": 1593413339000, + "y": 0, + }, + Object { + "x": 1593413340000, + "y": 0, + }, + ], + "key": "HTTP 3xx", + }, + Object { + "avg": 2, + "dataPoints": Array [ + Object { + "x": 1593413100000, + "y": 0, + }, + Object { + "x": 1593413101000, + "y": 0, + }, + Object { + "x": 1593413102000, + "y": 0, + }, + Object { + "x": 1593413103000, + "y": 0, + }, + Object { + "x": 1593413104000, + "y": 0, + }, + Object { + "x": 1593413105000, + "y": 0, + }, + Object { + "x": 1593413106000, + "y": 0, + }, + Object { + "x": 1593413107000, + "y": 0, + }, + Object { + "x": 1593413108000, + "y": 0, + }, + Object { + "x": 1593413109000, + "y": 0, + }, + Object { + "x": 1593413110000, + "y": 0, + }, + Object { + "x": 1593413111000, + "y": 0, + }, + Object { + "x": 1593413112000, + "y": 0, + }, + Object { + "x": 1593413113000, + "y": 0, + }, + Object { + "x": 1593413114000, + "y": 0, + }, + Object { + "x": 1593413115000, + "y": 0, + }, + Object { + "x": 1593413116000, + "y": 0, + }, + Object { + "x": 1593413117000, + "y": 0, + }, + Object { + "x": 1593413118000, + "y": 0, + }, + Object { + "x": 1593413119000, + "y": 0, + }, + Object { + "x": 1593413120000, + "y": 0, + }, + Object { + "x": 1593413121000, + "y": 0, + }, + Object { + "x": 1593413122000, + "y": 0, + }, + Object { + "x": 1593413123000, + "y": 0, + }, + Object { + "x": 1593413124000, + "y": 0, + }, + Object { + "x": 1593413125000, + "y": 0, + }, + Object { + "x": 1593413126000, + "y": 0, + }, + Object { + "x": 1593413127000, + "y": 0, + }, + Object { + "x": 1593413128000, + "y": 0, + }, + Object { + "x": 1593413129000, + "y": 0, + }, + Object { + "x": 1593413130000, + "y": 0, + }, + Object { + "x": 1593413131000, + "y": 0, + }, + Object { + "x": 1593413132000, + "y": 0, + }, + Object { + "x": 1593413133000, + "y": 0, + }, + Object { + "x": 1593413134000, + "y": 0, + }, + Object { + "x": 1593413135000, + "y": 0, + }, + Object { + "x": 1593413136000, + "y": 0, + }, + Object { + "x": 1593413137000, + "y": 0, + }, + Object { + "x": 1593413138000, + "y": 0, + }, + Object { + "x": 1593413139000, + "y": 0, + }, + Object { + "x": 1593413140000, + "y": 0, + }, + Object { + "x": 1593413141000, + "y": 0, + }, + Object { + "x": 1593413142000, + "y": 0, + }, + Object { + "x": 1593413143000, + "y": 0, + }, + Object { + "x": 1593413144000, + "y": 0, + }, + Object { + "x": 1593413145000, + "y": 0, + }, + Object { + "x": 1593413146000, + "y": 0, + }, + Object { + "x": 1593413147000, + "y": 0, + }, + Object { + "x": 1593413148000, + "y": 0, + }, + Object { + "x": 1593413149000, + "y": 0, + }, + Object { + "x": 1593413150000, + "y": 0, + }, + Object { + "x": 1593413151000, + "y": 0, + }, + Object { + "x": 1593413152000, + "y": 0, + }, + Object { + "x": 1593413153000, + "y": 0, + }, + Object { + "x": 1593413154000, + "y": 0, + }, + Object { + "x": 1593413155000, + "y": 0, + }, + Object { + "x": 1593413156000, + "y": 0, + }, + Object { + "x": 1593413157000, + "y": 0, + }, + Object { + "x": 1593413158000, + "y": 0, + }, + Object { + "x": 1593413159000, + "y": 0, + }, + Object { + "x": 1593413160000, + "y": 0, + }, + Object { + "x": 1593413161000, + "y": 0, + }, + Object { + "x": 1593413162000, + "y": 0, + }, + Object { + "x": 1593413163000, + "y": 0, + }, + Object { + "x": 1593413164000, + "y": 0, + }, + Object { + "x": 1593413165000, + "y": 0, + }, + Object { + "x": 1593413166000, + "y": 0, + }, + Object { + "x": 1593413167000, + "y": 0, + }, + Object { + "x": 1593413168000, + "y": 0, + }, + Object { + "x": 1593413169000, + "y": 0, + }, + Object { + "x": 1593413170000, + "y": 0, + }, + Object { + "x": 1593413171000, + "y": 0, + }, + Object { + "x": 1593413172000, + "y": 0, + }, + Object { + "x": 1593413173000, + "y": 0, + }, + Object { + "x": 1593413174000, + "y": 0, + }, + Object { + "x": 1593413175000, + "y": 0, + }, + Object { + "x": 1593413176000, + "y": 0, + }, + Object { + "x": 1593413177000, + "y": 0, + }, + Object { + "x": 1593413178000, + "y": 0, + }, + Object { + "x": 1593413179000, + "y": 0, + }, + Object { + "x": 1593413180000, + "y": 0, + }, + Object { + "x": 1593413181000, + "y": 0, + }, + Object { + "x": 1593413182000, + "y": 0, + }, + Object { + "x": 1593413183000, + "y": 0, + }, + Object { + "x": 1593413184000, + "y": 0, + }, + Object { + "x": 1593413185000, + "y": 0, + }, + Object { + "x": 1593413186000, + "y": 0, + }, + Object { + "x": 1593413187000, + "y": 0, + }, + Object { + "x": 1593413188000, + "y": 0, + }, + Object { + "x": 1593413189000, + "y": 0, + }, + Object { + "x": 1593413190000, + "y": 0, + }, + Object { + "x": 1593413191000, + "y": 0, + }, + Object { + "x": 1593413192000, + "y": 0, + }, + Object { + "x": 1593413193000, + "y": 0, + }, + Object { + "x": 1593413194000, + "y": 0, + }, + Object { + "x": 1593413195000, + "y": 0, + }, + Object { + "x": 1593413196000, + "y": 0, + }, + Object { + "x": 1593413197000, + "y": 0, + }, + Object { + "x": 1593413198000, + "y": 0, + }, + Object { + "x": 1593413199000, + "y": 0, + }, + Object { + "x": 1593413200000, + "y": 0, + }, + Object { + "x": 1593413201000, + "y": 0, + }, + Object { + "x": 1593413202000, + "y": 0, + }, + Object { + "x": 1593413203000, + "y": 0, + }, + Object { + "x": 1593413204000, + "y": 0, + }, + Object { + "x": 1593413205000, + "y": 0, + }, + Object { + "x": 1593413206000, + "y": 0, + }, + Object { + "x": 1593413207000, + "y": 0, + }, + Object { + "x": 1593413208000, + "y": 0, + }, + Object { + "x": 1593413209000, + "y": 0, + }, + Object { + "x": 1593413210000, + "y": 0, + }, + Object { + "x": 1593413211000, + "y": 0, + }, + Object { + "x": 1593413212000, + "y": 0, + }, + Object { + "x": 1593413213000, + "y": 0, + }, + Object { + "x": 1593413214000, + "y": 0, + }, + Object { + "x": 1593413215000, + "y": 0, + }, + Object { + "x": 1593413216000, + "y": 0, + }, + Object { + "x": 1593413217000, + "y": 0, + }, + Object { + "x": 1593413218000, + "y": 0, + }, + Object { + "x": 1593413219000, + "y": 0, + }, + Object { + "x": 1593413220000, + "y": 0, + }, + Object { + "x": 1593413221000, + "y": 0, + }, + Object { + "x": 1593413222000, + "y": 0, + }, + Object { + "x": 1593413223000, + "y": 0, + }, + Object { + "x": 1593413224000, + "y": 0, + }, + Object { + "x": 1593413225000, + "y": 0, + }, + Object { + "x": 1593413226000, + "y": 0, + }, + Object { + "x": 1593413227000, + "y": 0, + }, + Object { + "x": 1593413228000, + "y": 0, + }, + Object { + "x": 1593413229000, + "y": 0, + }, + Object { + "x": 1593413230000, + "y": 0, + }, + Object { + "x": 1593413231000, + "y": 0, + }, + Object { + "x": 1593413232000, + "y": 0, + }, + Object { + "x": 1593413233000, + "y": 0, + }, + Object { + "x": 1593413234000, + "y": 0, + }, + Object { + "x": 1593413235000, + "y": 0, + }, + Object { + "x": 1593413236000, + "y": 0, + }, + Object { + "x": 1593413237000, + "y": 0, + }, + Object { + "x": 1593413238000, + "y": 0, + }, + Object { + "x": 1593413239000, + "y": 0, + }, + Object { + "x": 1593413240000, + "y": 0, + }, + Object { + "x": 1593413241000, + "y": 0, + }, + Object { + "x": 1593413242000, + "y": 0, + }, + Object { + "x": 1593413243000, + "y": 0, + }, + Object { + "x": 1593413244000, + "y": 0, + }, + Object { + "x": 1593413245000, + "y": 0, + }, + Object { + "x": 1593413246000, + "y": 0, + }, + Object { + "x": 1593413247000, + "y": 0, + }, + Object { + "x": 1593413248000, + "y": 0, + }, + Object { + "x": 1593413249000, + "y": 0, + }, + Object { + "x": 1593413250000, + "y": 0, + }, + Object { + "x": 1593413251000, + "y": 0, + }, + Object { + "x": 1593413252000, + "y": 0, + }, + Object { + "x": 1593413253000, + "y": 0, + }, + Object { + "x": 1593413254000, + "y": 0, + }, + Object { + "x": 1593413255000, + "y": 0, + }, + Object { + "x": 1593413256000, + "y": 0, + }, + Object { + "x": 1593413257000, + "y": 0, + }, + Object { + "x": 1593413258000, + "y": 0, + }, + Object { + "x": 1593413259000, + "y": 0, + }, + Object { + "x": 1593413260000, + "y": 0, + }, + Object { + "x": 1593413261000, + "y": 0, + }, + Object { + "x": 1593413262000, + "y": 0, + }, + Object { + "x": 1593413263000, + "y": 0, + }, + Object { + "x": 1593413264000, + "y": 0, + }, + Object { + "x": 1593413265000, + "y": 0, + }, + Object { + "x": 1593413266000, + "y": 0, + }, + Object { + "x": 1593413267000, + "y": 0, + }, + Object { + "x": 1593413268000, + "y": 0, + }, + Object { + "x": 1593413269000, + "y": 0, + }, + Object { + "x": 1593413270000, + "y": 0, + }, + Object { + "x": 1593413271000, + "y": 0, + }, + Object { + "x": 1593413272000, + "y": 0, + }, + Object { + "x": 1593413273000, + "y": 0, + }, + Object { + "x": 1593413274000, + "y": 0, + }, + Object { + "x": 1593413275000, + "y": 0, + }, + Object { + "x": 1593413276000, + "y": 0, + }, + Object { + "x": 1593413277000, + "y": 0, + }, + Object { + "x": 1593413278000, + "y": 0, + }, + Object { + "x": 1593413279000, + "y": 0, + }, + Object { + "x": 1593413280000, + "y": 0, + }, + Object { + "x": 1593413281000, + "y": 0, + }, + Object { + "x": 1593413282000, + "y": 0, + }, + Object { + "x": 1593413283000, + "y": 0, + }, + Object { + "x": 1593413284000, + "y": 0, + }, + Object { + "x": 1593413285000, + "y": 0, + }, + Object { + "x": 1593413286000, + "y": 0, + }, + Object { + "x": 1593413287000, + "y": 0, + }, + Object { + "x": 1593413288000, + "y": 0, + }, + Object { + "x": 1593413289000, + "y": 1, + }, + Object { + "x": 1593413290000, + "y": 0, + }, + Object { + "x": 1593413291000, + "y": 0, + }, + Object { + "x": 1593413292000, + "y": 1, + }, + Object { + "x": 1593413293000, + "y": 0, + }, + Object { + "x": 1593413294000, + "y": 0, + }, + Object { + "x": 1593413295000, + "y": 0, + }, + Object { + "x": 1593413296000, + "y": 0, + }, + Object { + "x": 1593413297000, + "y": 0, + }, + Object { + "x": 1593413298000, + "y": 0, + }, + Object { + "x": 1593413299000, + "y": 0, + }, + Object { + "x": 1593413300000, + "y": 1, + }, + Object { + "x": 1593413301000, + "y": 0, + }, + Object { + "x": 1593413302000, + "y": 0, + }, + Object { + "x": 1593413303000, + "y": 0, + }, + Object { + "x": 1593413304000, + "y": 0, + }, + Object { + "x": 1593413305000, + "y": 1, + }, + Object { + "x": 1593413306000, + "y": 0, + }, + Object { + "x": 1593413307000, + "y": 0, + }, + Object { + "x": 1593413308000, + "y": 0, + }, + Object { + "x": 1593413309000, + "y": 1, + }, + Object { + "x": 1593413310000, + "y": 1, + }, + Object { + "x": 1593413311000, + "y": 0, + }, + Object { + "x": 1593413312000, + "y": 0, + }, + Object { + "x": 1593413313000, + "y": 0, + }, + Object { + "x": 1593413314000, + "y": 0, + }, + Object { + "x": 1593413315000, + "y": 1, + }, + Object { + "x": 1593413316000, + "y": 0, + }, + Object { + "x": 1593413317000, + "y": 0, + }, + Object { + "x": 1593413318000, + "y": 0, + }, + Object { + "x": 1593413319000, + "y": 0, + }, + Object { + "x": 1593413320000, + "y": 1, + }, + Object { + "x": 1593413321000, + "y": 0, + }, + Object { + "x": 1593413322000, + "y": 0, + }, + Object { + "x": 1593413323000, + "y": 0, + }, + Object { + "x": 1593413324000, + "y": 0, + }, + Object { + "x": 1593413325000, + "y": 0, + }, + Object { + "x": 1593413326000, + "y": 0, + }, + Object { + "x": 1593413327000, + "y": 0, + }, + Object { + "x": 1593413328000, + "y": 0, + }, + Object { + "x": 1593413329000, + "y": 0, + }, + Object { + "x": 1593413330000, + "y": 0, + }, + Object { + "x": 1593413331000, + "y": 0, + }, + Object { + "x": 1593413332000, + "y": 0, + }, + Object { + "x": 1593413333000, + "y": 0, + }, + Object { + "x": 1593413334000, + "y": 0, + }, + Object { + "x": 1593413335000, + "y": 0, + }, + Object { + "x": 1593413336000, + "y": 0, + }, + Object { + "x": 1593413337000, + "y": 0, + }, + Object { + "x": 1593413338000, + "y": 0, + }, + Object { + "x": 1593413339000, + "y": 0, + }, + Object { + "x": 1593413340000, + "y": 0, + }, + ], + "key": "HTTP 4xx", + }, + Object { + "avg": 2.25, + "dataPoints": Array [ + Object { + "x": 1593413100000, + "y": 0, + }, + Object { + "x": 1593413101000, + "y": 0, + }, + Object { + "x": 1593413102000, + "y": 0, + }, + Object { + "x": 1593413103000, + "y": 0, + }, + Object { + "x": 1593413104000, + "y": 0, + }, + Object { + "x": 1593413105000, + "y": 0, + }, + Object { + "x": 1593413106000, + "y": 0, + }, + Object { + "x": 1593413107000, + "y": 0, + }, + Object { + "x": 1593413108000, + "y": 0, + }, + Object { + "x": 1593413109000, + "y": 0, + }, + Object { + "x": 1593413110000, + "y": 0, + }, + Object { + "x": 1593413111000, + "y": 0, + }, + Object { + "x": 1593413112000, + "y": 0, + }, + Object { + "x": 1593413113000, + "y": 0, + }, + Object { + "x": 1593413114000, + "y": 0, + }, + Object { + "x": 1593413115000, + "y": 0, + }, + Object { + "x": 1593413116000, + "y": 0, + }, + Object { + "x": 1593413117000, + "y": 0, + }, + Object { + "x": 1593413118000, + "y": 0, + }, + Object { + "x": 1593413119000, + "y": 0, + }, + Object { + "x": 1593413120000, + "y": 0, + }, + Object { + "x": 1593413121000, + "y": 0, + }, + Object { + "x": 1593413122000, + "y": 0, + }, + Object { + "x": 1593413123000, + "y": 0, + }, + Object { + "x": 1593413124000, + "y": 0, + }, + Object { + "x": 1593413125000, + "y": 0, + }, + Object { + "x": 1593413126000, + "y": 0, + }, + Object { + "x": 1593413127000, + "y": 0, + }, + Object { + "x": 1593413128000, + "y": 0, + }, + Object { + "x": 1593413129000, + "y": 0, + }, + Object { + "x": 1593413130000, + "y": 0, + }, + Object { + "x": 1593413131000, + "y": 0, + }, + Object { + "x": 1593413132000, + "y": 0, + }, + Object { + "x": 1593413133000, + "y": 0, + }, + Object { + "x": 1593413134000, + "y": 0, + }, + Object { + "x": 1593413135000, + "y": 0, + }, + Object { + "x": 1593413136000, + "y": 0, + }, + Object { + "x": 1593413137000, + "y": 0, + }, + Object { + "x": 1593413138000, + "y": 0, + }, + Object { + "x": 1593413139000, + "y": 0, + }, + Object { + "x": 1593413140000, + "y": 0, + }, + Object { + "x": 1593413141000, + "y": 0, + }, + Object { + "x": 1593413142000, + "y": 0, + }, + Object { + "x": 1593413143000, + "y": 0, + }, + Object { + "x": 1593413144000, + "y": 0, + }, + Object { + "x": 1593413145000, + "y": 0, + }, + Object { + "x": 1593413146000, + "y": 0, + }, + Object { + "x": 1593413147000, + "y": 0, + }, + Object { + "x": 1593413148000, + "y": 0, + }, + Object { + "x": 1593413149000, + "y": 0, + }, + Object { + "x": 1593413150000, + "y": 0, + }, + Object { + "x": 1593413151000, + "y": 0, + }, + Object { + "x": 1593413152000, + "y": 0, + }, + Object { + "x": 1593413153000, + "y": 0, + }, + Object { + "x": 1593413154000, + "y": 0, + }, + Object { + "x": 1593413155000, + "y": 0, + }, + Object { + "x": 1593413156000, + "y": 0, + }, + Object { + "x": 1593413157000, + "y": 0, + }, + Object { + "x": 1593413158000, + "y": 0, + }, + Object { + "x": 1593413159000, + "y": 0, + }, + Object { + "x": 1593413160000, + "y": 0, + }, + Object { + "x": 1593413161000, + "y": 0, + }, + Object { + "x": 1593413162000, + "y": 0, + }, + Object { + "x": 1593413163000, + "y": 0, + }, + Object { + "x": 1593413164000, + "y": 0, + }, + Object { + "x": 1593413165000, + "y": 0, + }, + Object { + "x": 1593413166000, + "y": 0, + }, + Object { + "x": 1593413167000, + "y": 0, + }, + Object { + "x": 1593413168000, + "y": 0, + }, + Object { + "x": 1593413169000, + "y": 0, + }, + Object { + "x": 1593413170000, + "y": 0, + }, + Object { + "x": 1593413171000, + "y": 0, + }, + Object { + "x": 1593413172000, + "y": 0, + }, + Object { + "x": 1593413173000, + "y": 0, + }, + Object { + "x": 1593413174000, + "y": 0, + }, + Object { + "x": 1593413175000, + "y": 0, + }, + Object { + "x": 1593413176000, + "y": 0, + }, + Object { + "x": 1593413177000, + "y": 0, + }, + Object { + "x": 1593413178000, + "y": 0, + }, + Object { + "x": 1593413179000, + "y": 0, + }, + Object { + "x": 1593413180000, + "y": 0, + }, + Object { + "x": 1593413181000, + "y": 0, + }, + Object { + "x": 1593413182000, + "y": 0, + }, + Object { + "x": 1593413183000, + "y": 0, + }, + Object { + "x": 1593413184000, + "y": 0, + }, + Object { + "x": 1593413185000, + "y": 0, + }, + Object { + "x": 1593413186000, + "y": 0, + }, + Object { + "x": 1593413187000, + "y": 0, + }, + Object { + "x": 1593413188000, + "y": 0, + }, + Object { + "x": 1593413189000, + "y": 0, + }, + Object { + "x": 1593413190000, + "y": 0, + }, + Object { + "x": 1593413191000, + "y": 0, + }, + Object { + "x": 1593413192000, + "y": 0, + }, + Object { + "x": 1593413193000, + "y": 0, + }, + Object { + "x": 1593413194000, + "y": 0, + }, + Object { + "x": 1593413195000, + "y": 0, + }, + Object { + "x": 1593413196000, + "y": 0, + }, + Object { + "x": 1593413197000, + "y": 0, + }, + Object { + "x": 1593413198000, + "y": 0, + }, + Object { + "x": 1593413199000, + "y": 0, + }, + Object { + "x": 1593413200000, + "y": 0, + }, + Object { + "x": 1593413201000, + "y": 0, + }, + Object { + "x": 1593413202000, + "y": 0, + }, + Object { + "x": 1593413203000, + "y": 0, + }, + Object { + "x": 1593413204000, + "y": 0, + }, + Object { + "x": 1593413205000, + "y": 0, + }, + Object { + "x": 1593413206000, + "y": 0, + }, + Object { + "x": 1593413207000, + "y": 0, + }, + Object { + "x": 1593413208000, + "y": 0, + }, + Object { + "x": 1593413209000, + "y": 0, + }, + Object { + "x": 1593413210000, + "y": 0, + }, + Object { + "x": 1593413211000, + "y": 0, + }, + Object { + "x": 1593413212000, + "y": 0, + }, + Object { + "x": 1593413213000, + "y": 0, + }, + Object { + "x": 1593413214000, + "y": 0, + }, + Object { + "x": 1593413215000, + "y": 0, + }, + Object { + "x": 1593413216000, + "y": 0, + }, + Object { + "x": 1593413217000, + "y": 0, + }, + Object { + "x": 1593413218000, + "y": 0, + }, + Object { + "x": 1593413219000, + "y": 0, + }, + Object { + "x": 1593413220000, + "y": 0, + }, + Object { + "x": 1593413221000, + "y": 0, + }, + Object { + "x": 1593413222000, + "y": 0, + }, + Object { + "x": 1593413223000, + "y": 0, + }, + Object { + "x": 1593413224000, + "y": 0, + }, + Object { + "x": 1593413225000, + "y": 0, + }, + Object { + "x": 1593413226000, + "y": 0, + }, + Object { + "x": 1593413227000, + "y": 0, + }, + Object { + "x": 1593413228000, + "y": 0, + }, + Object { + "x": 1593413229000, + "y": 0, + }, + Object { + "x": 1593413230000, + "y": 0, + }, + Object { + "x": 1593413231000, + "y": 0, + }, + Object { + "x": 1593413232000, + "y": 0, + }, + Object { + "x": 1593413233000, + "y": 0, + }, + Object { + "x": 1593413234000, + "y": 0, + }, + Object { + "x": 1593413235000, + "y": 0, + }, + Object { + "x": 1593413236000, + "y": 0, + }, + Object { + "x": 1593413237000, + "y": 0, + }, + Object { + "x": 1593413238000, + "y": 0, + }, + Object { + "x": 1593413239000, + "y": 0, + }, + Object { + "x": 1593413240000, + "y": 0, + }, + Object { + "x": 1593413241000, + "y": 0, + }, + Object { + "x": 1593413242000, + "y": 0, + }, + Object { + "x": 1593413243000, + "y": 0, + }, + Object { + "x": 1593413244000, + "y": 0, + }, + Object { + "x": 1593413245000, + "y": 0, + }, + Object { + "x": 1593413246000, + "y": 0, + }, + Object { + "x": 1593413247000, + "y": 0, + }, + Object { + "x": 1593413248000, + "y": 0, + }, + Object { + "x": 1593413249000, + "y": 0, + }, + Object { + "x": 1593413250000, + "y": 0, + }, + Object { + "x": 1593413251000, + "y": 0, + }, + Object { + "x": 1593413252000, + "y": 0, + }, + Object { + "x": 1593413253000, + "y": 0, + }, + Object { + "x": 1593413254000, + "y": 0, + }, + Object { + "x": 1593413255000, + "y": 0, + }, + Object { + "x": 1593413256000, + "y": 0, + }, + Object { + "x": 1593413257000, + "y": 0, + }, + Object { + "x": 1593413258000, + "y": 0, + }, + Object { + "x": 1593413259000, + "y": 0, + }, + Object { + "x": 1593413260000, + "y": 0, + }, + Object { + "x": 1593413261000, + "y": 0, + }, + Object { + "x": 1593413262000, + "y": 0, + }, + Object { + "x": 1593413263000, + "y": 0, + }, + Object { + "x": 1593413264000, + "y": 0, + }, + Object { + "x": 1593413265000, + "y": 0, + }, + Object { + "x": 1593413266000, + "y": 0, + }, + Object { + "x": 1593413267000, + "y": 0, + }, + Object { + "x": 1593413268000, + "y": 0, + }, + Object { + "x": 1593413269000, + "y": 0, + }, + Object { + "x": 1593413270000, + "y": 0, + }, + Object { + "x": 1593413271000, + "y": 0, + }, + Object { + "x": 1593413272000, + "y": 0, + }, + Object { + "x": 1593413273000, + "y": 0, + }, + Object { + "x": 1593413274000, + "y": 0, + }, + Object { + "x": 1593413275000, + "y": 0, + }, + Object { + "x": 1593413276000, + "y": 0, + }, + Object { + "x": 1593413277000, + "y": 0, + }, + Object { + "x": 1593413278000, + "y": 0, + }, + Object { + "x": 1593413279000, + "y": 0, + }, + Object { + "x": 1593413280000, + "y": 0, + }, + Object { + "x": 1593413281000, + "y": 0, + }, + Object { + "x": 1593413282000, + "y": 0, + }, + Object { + "x": 1593413283000, + "y": 0, + }, + Object { + "x": 1593413284000, + "y": 0, + }, + Object { + "x": 1593413285000, + "y": 0, + }, + Object { + "x": 1593413286000, + "y": 1, + }, + Object { + "x": 1593413287000, + "y": 1, + }, + Object { + "x": 1593413288000, + "y": 0, + }, + Object { + "x": 1593413289000, + "y": 0, + }, + Object { + "x": 1593413290000, + "y": 0, + }, + Object { + "x": 1593413291000, + "y": 0, + }, + Object { + "x": 1593413292000, + "y": 0, + }, + Object { + "x": 1593413293000, + "y": 0, + }, + Object { + "x": 1593413294000, + "y": 0, + }, + Object { + "x": 1593413295000, + "y": 0, + }, + Object { + "x": 1593413296000, + "y": 0, + }, + Object { + "x": 1593413297000, + "y": 0, + }, + Object { + "x": 1593413298000, + "y": 0, + }, + Object { + "x": 1593413299000, + "y": 1, + }, + Object { + "x": 1593413300000, + "y": 0, + }, + Object { + "x": 1593413301000, + "y": 1, + }, + Object { + "x": 1593413302000, + "y": 0, + }, + Object { + "x": 1593413303000, + "y": 0, + }, + Object { + "x": 1593413304000, + "y": 0, + }, + Object { + "x": 1593413305000, + "y": 1, + }, + Object { + "x": 1593413306000, + "y": 0, + }, + Object { + "x": 1593413307000, + "y": 0, + }, + Object { + "x": 1593413308000, + "y": 1, + }, + Object { + "x": 1593413309000, + "y": 0, + }, + Object { + "x": 1593413310000, + "y": 0, + }, + Object { + "x": 1593413311000, + "y": 1, + }, + Object { + "x": 1593413312000, + "y": 0, + }, + Object { + "x": 1593413313000, + "y": 0, + }, + Object { + "x": 1593413314000, + "y": 0, + }, + Object { + "x": 1593413315000, + "y": 1, + }, + Object { + "x": 1593413316000, + "y": 0, + }, + Object { + "x": 1593413317000, + "y": 0, + }, + Object { + "x": 1593413318000, + "y": 0, + }, + Object { + "x": 1593413319000, + "y": 0, + }, + Object { + "x": 1593413320000, + "y": 0, + }, + Object { + "x": 1593413321000, + "y": 0, + }, + Object { + "x": 1593413322000, + "y": 1, + }, + Object { + "x": 1593413323000, + "y": 0, + }, + Object { + "x": 1593413324000, + "y": 0, + }, + Object { + "x": 1593413325000, + "y": 0, + }, + Object { + "x": 1593413326000, + "y": 0, + }, + Object { + "x": 1593413327000, + "y": 0, + }, + Object { + "x": 1593413328000, + "y": 0, + }, + Object { + "x": 1593413329000, + "y": 0, + }, + Object { + "x": 1593413330000, + "y": 0, + }, + Object { + "x": 1593413331000, + "y": 0, + }, + Object { + "x": 1593413332000, + "y": 0, + }, + Object { + "x": 1593413333000, + "y": 0, + }, + Object { + "x": 1593413334000, + "y": 0, + }, + Object { + "x": 1593413335000, + "y": 0, + }, + Object { + "x": 1593413336000, + "y": 0, + }, + Object { + "x": 1593413337000, + "y": 0, + }, + Object { + "x": 1593413338000, + "y": 0, + }, + Object { + "x": 1593413339000, + "y": 0, + }, + Object { + "x": 1593413340000, + "y": 0, + }, + ], + "key": "HTTP 5xx", + }, + Object { + "avg": 0.25, + "dataPoints": Array [ + Object { + "x": 1593413100000, + "y": 0, + }, + Object { + "x": 1593413101000, + "y": 0, + }, + Object { + "x": 1593413102000, + "y": 0, + }, + Object { + "x": 1593413103000, + "y": 0, + }, + Object { + "x": 1593413104000, + "y": 0, + }, + Object { + "x": 1593413105000, + "y": 0, + }, + Object { + "x": 1593413106000, + "y": 0, + }, + Object { + "x": 1593413107000, + "y": 0, + }, + Object { + "x": 1593413108000, + "y": 0, + }, + Object { + "x": 1593413109000, + "y": 0, + }, + Object { + "x": 1593413110000, + "y": 0, + }, + Object { + "x": 1593413111000, + "y": 0, + }, + Object { + "x": 1593413112000, + "y": 0, + }, + Object { + "x": 1593413113000, + "y": 0, + }, + Object { + "x": 1593413114000, + "y": 0, + }, + Object { + "x": 1593413115000, + "y": 0, + }, + Object { + "x": 1593413116000, + "y": 0, + }, + Object { + "x": 1593413117000, + "y": 0, + }, + Object { + "x": 1593413118000, + "y": 0, + }, + Object { + "x": 1593413119000, + "y": 0, + }, + Object { + "x": 1593413120000, + "y": 0, + }, + Object { + "x": 1593413121000, + "y": 0, + }, + Object { + "x": 1593413122000, + "y": 0, + }, + Object { + "x": 1593413123000, + "y": 0, + }, + Object { + "x": 1593413124000, + "y": 0, + }, + Object { + "x": 1593413125000, + "y": 0, + }, + Object { + "x": 1593413126000, + "y": 0, + }, + Object { + "x": 1593413127000, + "y": 0, + }, + Object { + "x": 1593413128000, + "y": 0, + }, + Object { + "x": 1593413129000, + "y": 0, + }, + Object { + "x": 1593413130000, + "y": 0, + }, + Object { + "x": 1593413131000, + "y": 0, + }, + Object { + "x": 1593413132000, + "y": 0, + }, + Object { + "x": 1593413133000, + "y": 0, + }, + Object { + "x": 1593413134000, + "y": 0, + }, + Object { + "x": 1593413135000, + "y": 0, + }, + Object { + "x": 1593413136000, + "y": 0, + }, + Object { + "x": 1593413137000, + "y": 0, + }, + Object { + "x": 1593413138000, + "y": 0, + }, + Object { + "x": 1593413139000, + "y": 0, + }, + Object { + "x": 1593413140000, + "y": 0, + }, + Object { + "x": 1593413141000, + "y": 0, + }, + Object { + "x": 1593413142000, + "y": 0, + }, + Object { + "x": 1593413143000, + "y": 0, + }, + Object { + "x": 1593413144000, + "y": 0, + }, + Object { + "x": 1593413145000, + "y": 0, + }, + Object { + "x": 1593413146000, + "y": 0, + }, + Object { + "x": 1593413147000, + "y": 0, + }, + Object { + "x": 1593413148000, + "y": 0, + }, + Object { + "x": 1593413149000, + "y": 0, + }, + Object { + "x": 1593413150000, + "y": 0, + }, + Object { + "x": 1593413151000, + "y": 0, + }, + Object { + "x": 1593413152000, + "y": 0, + }, + Object { + "x": 1593413153000, + "y": 0, + }, + Object { + "x": 1593413154000, + "y": 0, + }, + Object { + "x": 1593413155000, + "y": 0, + }, + Object { + "x": 1593413156000, + "y": 0, + }, + Object { + "x": 1593413157000, + "y": 0, + }, + Object { + "x": 1593413158000, + "y": 0, + }, + Object { + "x": 1593413159000, + "y": 0, + }, + Object { + "x": 1593413160000, + "y": 0, + }, + Object { + "x": 1593413161000, + "y": 0, + }, + Object { + "x": 1593413162000, + "y": 0, + }, + Object { + "x": 1593413163000, + "y": 0, + }, + Object { + "x": 1593413164000, + "y": 0, + }, + Object { + "x": 1593413165000, + "y": 0, + }, + Object { + "x": 1593413166000, + "y": 0, + }, + Object { + "x": 1593413167000, + "y": 0, + }, + Object { + "x": 1593413168000, + "y": 0, + }, + Object { + "x": 1593413169000, + "y": 0, + }, + Object { + "x": 1593413170000, + "y": 0, + }, + Object { + "x": 1593413171000, + "y": 0, + }, + Object { + "x": 1593413172000, + "y": 0, + }, + Object { + "x": 1593413173000, + "y": 0, + }, + Object { + "x": 1593413174000, + "y": 0, + }, + Object { + "x": 1593413175000, + "y": 0, + }, + Object { + "x": 1593413176000, + "y": 0, + }, + Object { + "x": 1593413177000, + "y": 0, + }, + Object { + "x": 1593413178000, + "y": 0, + }, + Object { + "x": 1593413179000, + "y": 0, + }, + Object { + "x": 1593413180000, + "y": 0, + }, + Object { + "x": 1593413181000, + "y": 0, + }, + Object { + "x": 1593413182000, + "y": 0, + }, + Object { + "x": 1593413183000, + "y": 0, + }, + Object { + "x": 1593413184000, + "y": 0, + }, + Object { + "x": 1593413185000, + "y": 0, + }, + Object { + "x": 1593413186000, + "y": 0, + }, + Object { + "x": 1593413187000, + "y": 0, + }, + Object { + "x": 1593413188000, + "y": 0, + }, + Object { + "x": 1593413189000, + "y": 0, + }, + Object { + "x": 1593413190000, + "y": 0, + }, + Object { + "x": 1593413191000, + "y": 0, + }, + Object { + "x": 1593413192000, + "y": 0, + }, + Object { + "x": 1593413193000, + "y": 0, + }, + Object { + "x": 1593413194000, + "y": 0, + }, + Object { + "x": 1593413195000, + "y": 0, + }, + Object { + "x": 1593413196000, + "y": 0, + }, + Object { + "x": 1593413197000, + "y": 0, + }, + Object { + "x": 1593413198000, + "y": 0, + }, + Object { + "x": 1593413199000, + "y": 0, + }, + Object { + "x": 1593413200000, + "y": 0, + }, + Object { + "x": 1593413201000, + "y": 0, + }, + Object { + "x": 1593413202000, + "y": 0, + }, + Object { + "x": 1593413203000, + "y": 0, + }, + Object { + "x": 1593413204000, + "y": 0, + }, + Object { + "x": 1593413205000, + "y": 0, + }, + Object { + "x": 1593413206000, + "y": 0, + }, + Object { + "x": 1593413207000, + "y": 0, + }, + Object { + "x": 1593413208000, + "y": 0, + }, + Object { + "x": 1593413209000, + "y": 0, + }, + Object { + "x": 1593413210000, + "y": 0, + }, + Object { + "x": 1593413211000, + "y": 0, + }, + Object { + "x": 1593413212000, + "y": 0, + }, + Object { + "x": 1593413213000, + "y": 0, + }, + Object { + "x": 1593413214000, + "y": 0, + }, + Object { + "x": 1593413215000, + "y": 0, + }, + Object { + "x": 1593413216000, + "y": 0, + }, + Object { + "x": 1593413217000, + "y": 0, + }, + Object { + "x": 1593413218000, + "y": 0, + }, + Object { + "x": 1593413219000, + "y": 0, + }, + Object { + "x": 1593413220000, + "y": 0, + }, + Object { + "x": 1593413221000, + "y": 0, + }, + Object { + "x": 1593413222000, + "y": 0, + }, + Object { + "x": 1593413223000, + "y": 0, + }, + Object { + "x": 1593413224000, + "y": 0, + }, + Object { + "x": 1593413225000, + "y": 0, + }, + Object { + "x": 1593413226000, + "y": 0, + }, + Object { + "x": 1593413227000, + "y": 0, + }, + Object { + "x": 1593413228000, + "y": 0, + }, + Object { + "x": 1593413229000, + "y": 0, + }, + Object { + "x": 1593413230000, + "y": 0, + }, + Object { + "x": 1593413231000, + "y": 0, + }, + Object { + "x": 1593413232000, + "y": 0, + }, + Object { + "x": 1593413233000, + "y": 0, + }, + Object { + "x": 1593413234000, + "y": 0, + }, + Object { + "x": 1593413235000, + "y": 0, + }, + Object { + "x": 1593413236000, + "y": 0, + }, + Object { + "x": 1593413237000, + "y": 0, + }, + Object { + "x": 1593413238000, + "y": 0, + }, + Object { + "x": 1593413239000, + "y": 0, + }, + Object { + "x": 1593413240000, + "y": 0, + }, + Object { + "x": 1593413241000, + "y": 0, + }, + Object { + "x": 1593413242000, + "y": 0, + }, + Object { + "x": 1593413243000, + "y": 0, + }, + Object { + "x": 1593413244000, + "y": 0, + }, + Object { + "x": 1593413245000, + "y": 0, + }, + Object { + "x": 1593413246000, + "y": 0, + }, + Object { + "x": 1593413247000, + "y": 0, + }, + Object { + "x": 1593413248000, + "y": 0, + }, + Object { + "x": 1593413249000, + "y": 0, + }, + Object { + "x": 1593413250000, + "y": 0, + }, + Object { + "x": 1593413251000, + "y": 0, + }, + Object { + "x": 1593413252000, + "y": 0, + }, + Object { + "x": 1593413253000, + "y": 0, + }, + Object { + "x": 1593413254000, + "y": 0, + }, + Object { + "x": 1593413255000, + "y": 0, + }, + Object { + "x": 1593413256000, + "y": 0, + }, + Object { + "x": 1593413257000, + "y": 0, + }, + Object { + "x": 1593413258000, + "y": 0, + }, + Object { + "x": 1593413259000, + "y": 0, + }, + Object { + "x": 1593413260000, + "y": 0, + }, + Object { + "x": 1593413261000, + "y": 0, + }, + Object { + "x": 1593413262000, + "y": 0, + }, + Object { + "x": 1593413263000, + "y": 0, + }, + Object { + "x": 1593413264000, + "y": 0, + }, + Object { + "x": 1593413265000, + "y": 0, + }, + Object { + "x": 1593413266000, + "y": 0, + }, + Object { + "x": 1593413267000, + "y": 0, + }, + Object { + "x": 1593413268000, + "y": 0, + }, + Object { + "x": 1593413269000, + "y": 0, + }, + Object { + "x": 1593413270000, + "y": 0, + }, + Object { + "x": 1593413271000, + "y": 0, + }, + Object { + "x": 1593413272000, + "y": 0, + }, + Object { + "x": 1593413273000, + "y": 0, + }, + Object { + "x": 1593413274000, + "y": 0, + }, + Object { + "x": 1593413275000, + "y": 0, + }, + Object { + "x": 1593413276000, + "y": 0, + }, + Object { + "x": 1593413277000, + "y": 0, + }, + Object { + "x": 1593413278000, + "y": 0, + }, + Object { + "x": 1593413279000, + "y": 0, + }, + Object { + "x": 1593413280000, + "y": 0, + }, + Object { + "x": 1593413281000, + "y": 0, + }, + Object { + "x": 1593413282000, + "y": 0, + }, + Object { + "x": 1593413283000, + "y": 0, + }, + Object { + "x": 1593413284000, + "y": 0, + }, + Object { + "x": 1593413285000, + "y": 0, + }, + Object { + "x": 1593413286000, + "y": 0, + }, + Object { + "x": 1593413287000, + "y": 0, + }, + Object { + "x": 1593413288000, + "y": 0, + }, + Object { + "x": 1593413289000, + "y": 0, + }, + Object { + "x": 1593413290000, + "y": 0, + }, + Object { + "x": 1593413291000, + "y": 0, + }, + Object { + "x": 1593413292000, + "y": 0, + }, + Object { + "x": 1593413293000, + "y": 0, + }, + Object { + "x": 1593413294000, + "y": 0, + }, + Object { + "x": 1593413295000, + "y": 0, + }, + Object { + "x": 1593413296000, + "y": 0, + }, + Object { + "x": 1593413297000, + "y": 0, + }, + Object { + "x": 1593413298000, + "y": 0, + }, + Object { + "x": 1593413299000, + "y": 0, + }, + Object { + "x": 1593413300000, + "y": 0, + }, + Object { + "x": 1593413301000, + "y": 0, + }, + Object { + "x": 1593413302000, + "y": 0, + }, + Object { + "x": 1593413303000, + "y": 0, + }, + Object { + "x": 1593413304000, + "y": 0, + }, + Object { + "x": 1593413305000, + "y": 0, + }, + Object { + "x": 1593413306000, + "y": 0, + }, + Object { + "x": 1593413307000, + "y": 0, + }, + Object { + "x": 1593413308000, + "y": 0, + }, + Object { + "x": 1593413309000, + "y": 1, + }, + Object { + "x": 1593413310000, + "y": 0, + }, + Object { + "x": 1593413311000, + "y": 0, + }, + Object { + "x": 1593413312000, + "y": 0, + }, + Object { + "x": 1593413313000, + "y": 0, + }, + Object { + "x": 1593413314000, + "y": 0, + }, + Object { + "x": 1593413315000, + "y": 0, + }, + Object { + "x": 1593413316000, + "y": 0, + }, + Object { + "x": 1593413317000, + "y": 0, + }, + Object { + "x": 1593413318000, + "y": 0, + }, + Object { + "x": 1593413319000, + "y": 0, + }, + Object { + "x": 1593413320000, + "y": 0, + }, + Object { + "x": 1593413321000, + "y": 0, + }, + Object { + "x": 1593413322000, + "y": 0, + }, + Object { + "x": 1593413323000, + "y": 0, + }, + Object { + "x": 1593413324000, + "y": 0, + }, + Object { + "x": 1593413325000, + "y": 0, + }, + Object { + "x": 1593413326000, + "y": 0, + }, + Object { + "x": 1593413327000, + "y": 0, + }, + Object { + "x": 1593413328000, + "y": 0, + }, + Object { + "x": 1593413329000, + "y": 0, + }, + Object { + "x": 1593413330000, + "y": 0, + }, + Object { + "x": 1593413331000, + "y": 0, + }, + Object { + "x": 1593413332000, + "y": 0, + }, + Object { + "x": 1593413333000, + "y": 0, + }, + Object { + "x": 1593413334000, + "y": 0, + }, + Object { + "x": 1593413335000, + "y": 0, + }, + Object { + "x": 1593413336000, + "y": 0, + }, + Object { + "x": 1593413337000, + "y": 0, + }, + Object { + "x": 1593413338000, + "y": 0, + }, + Object { + "x": 1593413339000, + "y": 0, + }, + Object { + "x": 1593413340000, + "y": 0, + }, + ], + "key": "success", + }, + ], + }, +} +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts index 690935ddc7f6..21f3aaa04a7b 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import expectedAvgDurationByBrowser from './expectation/avg_duration_by_browser.json'; -import expectedAvgDurationByBrowserWithTransactionName from './expectation/avg_duration_by_browser_transaction_name.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -38,7 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql(expectedAvgDurationByBrowser); + expectSnapshot(response.body).toMatch(); }); it('returns the average duration by browser filtering by transaction name', async () => { const response = await supertest.get( @@ -46,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql(expectedAvgDurationByBrowserWithTransactionName); + expectSnapshot(response.body).toMatch(); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts index 0b94abaa1589..4e1b1e57fba0 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/breakdown.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import expectedBreakdown from './expectation/breakdown.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -38,7 +38,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql(expectedBreakdown); + expectSnapshot(response.body).toMatch(); }); it('returns the transaction breakdown for a transaction group', async () => { const response = await supertest.get( @@ -48,22 +48,53 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); const { timeseries } = response.body; const { title, color, type, data, hideLegend, legendValue } = timeseries[0]; - expect(data).to.eql([ - { x: 1593413100000, y: null }, - { x: 1593413130000, y: null }, - { x: 1593413160000, y: null }, - { x: 1593413190000, y: null }, - { x: 1593413220000, y: null }, - { x: 1593413250000, y: null }, - { x: 1593413280000, y: null }, - { x: 1593413310000, y: 1 }, - { x: 1593413340000, y: null }, - ]); - expect(title).to.be('app'); - expect(color).to.be('#54b399'); - expect(type).to.be('areaStacked'); - expect(hideLegend).to.be(false); - expect(legendValue).to.be('100%'); + + expectSnapshot(data).toMatchInline(` + Array [ + Object { + "x": 1593413100000, + "y": null, + }, + Object { + "x": 1593413130000, + "y": null, + }, + Object { + "x": 1593413160000, + "y": null, + }, + Object { + "x": 1593413190000, + "y": null, + }, + Object { + "x": 1593413220000, + "y": null, + }, + Object { + "x": 1593413250000, + "y": null, + }, + Object { + "x": 1593413280000, + "y": null, + }, + Object { + "x": 1593413310000, + "y": 1, + }, + Object { + "x": 1593413340000, + "y": null, + }, + ] + `); + + expectSnapshot(title).toMatchInline(`"app"`); + expectSnapshot(color).toMatchInline(`"#54b399"`); + expectSnapshot(type).toMatchInline(`"areaStacked"`); + expectSnapshot(hideLegend).toMatchInline(`false`); + expectSnapshot(legendValue).toMatchInline(`"100%"`); }); it('returns the transaction breakdown sorted by name', async () => { const response = await supertest.get( @@ -71,12 +102,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body.timeseries.map((serie: { title: string }) => serie.title)).to.eql([ - 'app', - 'http', - 'postgresql', - 'redis', - ]); + expectSnapshot(response.body.timeseries.map((serie: { title: string }) => serie.title)) + .toMatchInline(` + Array [ + "app", + "http", + "postgresql", + "redis", + ] + `); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts index 9aa10d2b307b..cf23883612b7 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/error_rate.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; import { first, last } from 'lodash'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -46,24 +47,30 @@ export default function ApiTest({ getService }: FtrProviderContext) { errorRateResponse = response.body; }); - it('has the correct start date', async () => { - expect(first(errorRateResponse.erroneousTransactionsRate)?.x).to.be(1598439600000); + it('has the correct start date', () => { + expectSnapshot( + new Date(first(errorRateResponse.erroneousTransactionsRate)?.x ?? NaN).toISOString() + ).toMatchInline(`"2020-08-26T11:00:00.000Z"`); }); - it('has the correct end date', async () => { - expect(last(errorRateResponse.erroneousTransactionsRate)?.x).to.be(1598441400000); + it('has the correct end date', () => { + expectSnapshot( + new Date(last(errorRateResponse.erroneousTransactionsRate)?.x ?? NaN).toISOString() + ).toMatchInline(`"2020-08-26T11:30:00.000Z"`); }); - it('has the correct number of buckets', async () => { - expect(errorRateResponse.erroneousTransactionsRate.length).to.be(61); + it('has the correct number of buckets', () => { + expectSnapshot(errorRateResponse.erroneousTransactionsRate.length).toMatchInline(`61`); }); - it('has the correct calculation for average', async () => { - expect(errorRateResponse.average).to.be(0.18894993894993897); + it('has the correct calculation for average', () => { + expectSnapshot(errorRateResponse.average).toMatchInline(`0.18894993894993897`); }); - it('has the correct error rate', async () => { - expect(first(errorRateResponse.erroneousTransactionsRate)?.y).to.be(0.5); + it('has the correct error rate', () => { + expectSnapshot(first(errorRateResponse.erroneousTransactionsRate)?.y).toMatchInline( + `0.5` + ); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser.json deleted file mode 100644 index cd53af3bf708..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser.json +++ /dev/null @@ -1,735 +0,0 @@ -[ - { - "title":"HeadlessChrome", - "data":[ - { - "x":1593413100000 - }, - { - "x":1593413101000 - }, - { - "x":1593413102000 - }, - { - "x":1593413103000 - }, - { - "x":1593413104000 - }, - { - "x":1593413105000 - }, - { - "x":1593413106000 - }, - { - "x":1593413107000 - }, - { - "x":1593413108000 - }, - { - "x":1593413109000 - }, - { - "x":1593413110000 - }, - { - "x":1593413111000 - }, - { - "x":1593413112000 - }, - { - "x":1593413113000 - }, - { - "x":1593413114000 - }, - { - "x":1593413115000 - }, - { - "x":1593413116000 - }, - { - "x":1593413117000 - }, - { - "x":1593413118000 - }, - { - "x":1593413119000 - }, - { - "x":1593413120000 - }, - { - "x":1593413121000 - }, - { - "x":1593413122000 - }, - { - "x":1593413123000 - }, - { - "x":1593413124000 - }, - { - "x":1593413125000 - }, - { - "x":1593413126000 - }, - { - "x":1593413127000 - }, - { - "x":1593413128000 - }, - { - "x":1593413129000 - }, - { - "x":1593413130000 - }, - { - "x":1593413131000 - }, - { - "x":1593413132000 - }, - { - "x":1593413133000 - }, - { - "x":1593413134000 - }, - { - "x":1593413135000 - }, - { - "x":1593413136000 - }, - { - "x":1593413137000 - }, - { - "x":1593413138000 - }, - { - "x":1593413139000 - }, - { - "x":1593413140000 - }, - { - "x":1593413141000 - }, - { - "x":1593413142000 - }, - { - "x":1593413143000 - }, - { - "x":1593413144000 - }, - { - "x":1593413145000 - }, - { - "x":1593413146000 - }, - { - "x":1593413147000 - }, - { - "x":1593413148000 - }, - { - "x":1593413149000 - }, - { - "x":1593413150000 - }, - { - "x":1593413151000 - }, - { - "x":1593413152000 - }, - { - "x":1593413153000 - }, - { - "x":1593413154000 - }, - { - "x":1593413155000 - }, - { - "x":1593413156000 - }, - { - "x":1593413157000 - }, - { - "x":1593413158000 - }, - { - "x":1593413159000 - }, - { - "x":1593413160000 - }, - { - "x":1593413161000 - }, - { - "x":1593413162000 - }, - { - "x":1593413163000 - }, - { - "x":1593413164000 - }, - { - "x":1593413165000 - }, - { - "x":1593413166000 - }, - { - "x":1593413167000 - }, - { - "x":1593413168000 - }, - { - "x":1593413169000 - }, - { - "x":1593413170000 - }, - { - "x":1593413171000 - }, - { - "x":1593413172000 - }, - { - "x":1593413173000 - }, - { - "x":1593413174000 - }, - { - "x":1593413175000 - }, - { - "x":1593413176000 - }, - { - "x":1593413177000 - }, - { - "x":1593413178000 - }, - { - "x":1593413179000 - }, - { - "x":1593413180000 - }, - { - "x":1593413181000 - }, - { - "x":1593413182000 - }, - { - "x":1593413183000 - }, - { - "x":1593413184000 - }, - { - "x":1593413185000 - }, - { - "x":1593413186000 - }, - { - "x":1593413187000 - }, - { - "x":1593413188000 - }, - { - "x":1593413189000 - }, - { - "x":1593413190000 - }, - { - "x":1593413191000 - }, - { - "x":1593413192000 - }, - { - "x":1593413193000 - }, - { - "x":1593413194000 - }, - { - "x":1593413195000 - }, - { - "x":1593413196000 - }, - { - "x":1593413197000 - }, - { - "x":1593413198000 - }, - { - "x":1593413199000 - }, - { - "x":1593413200000 - }, - { - "x":1593413201000 - }, - { - "x":1593413202000 - }, - { - "x":1593413203000 - }, - { - "x":1593413204000 - }, - { - "x":1593413205000 - }, - { - "x":1593413206000 - }, - { - "x":1593413207000 - }, - { - "x":1593413208000 - }, - { - "x":1593413209000 - }, - { - "x":1593413210000 - }, - { - "x":1593413211000 - }, - { - "x":1593413212000 - }, - { - "x":1593413213000 - }, - { - "x":1593413214000 - }, - { - "x":1593413215000 - }, - { - "x":1593413216000 - }, - { - "x":1593413217000 - }, - { - "x":1593413218000 - }, - { - "x":1593413219000 - }, - { - "x":1593413220000 - }, - { - "x":1593413221000 - }, - { - "x":1593413222000 - }, - { - "x":1593413223000 - }, - { - "x":1593413224000 - }, - { - "x":1593413225000 - }, - { - "x":1593413226000 - }, - { - "x":1593413227000 - }, - { - "x":1593413228000 - }, - { - "x":1593413229000 - }, - { - "x":1593413230000 - }, - { - "x":1593413231000 - }, - { - "x":1593413232000 - }, - { - "x":1593413233000 - }, - { - "x":1593413234000 - }, - { - "x":1593413235000 - }, - { - "x":1593413236000 - }, - { - "x":1593413237000 - }, - { - "x":1593413238000 - }, - { - "x":1593413239000 - }, - { - "x":1593413240000 - }, - { - "x":1593413241000 - }, - { - "x":1593413242000 - }, - { - "x":1593413243000 - }, - { - "x":1593413244000 - }, - { - "x":1593413245000 - }, - { - "x":1593413246000 - }, - { - "x":1593413247000 - }, - { - "x":1593413248000 - }, - { - "x":1593413249000 - }, - { - "x":1593413250000 - }, - { - "x":1593413251000 - }, - { - "x":1593413252000 - }, - { - "x":1593413253000 - }, - { - "x":1593413254000 - }, - { - "x":1593413255000 - }, - { - "x":1593413256000 - }, - { - "x":1593413257000 - }, - { - "x":1593413258000 - }, - { - "x":1593413259000 - }, - { - "x":1593413260000 - }, - { - "x":1593413261000 - }, - { - "x":1593413262000 - }, - { - "x":1593413263000 - }, - { - "x":1593413264000 - }, - { - "x":1593413265000 - }, - { - "x":1593413266000 - }, - { - "x":1593413267000 - }, - { - "x":1593413268000 - }, - { - "x":1593413269000 - }, - { - "x":1593413270000 - }, - { - "x":1593413271000 - }, - { - "x":1593413272000 - }, - { - "x":1593413273000 - }, - { - "x":1593413274000 - }, - { - "x":1593413275000 - }, - { - "x":1593413276000 - }, - { - "x":1593413277000 - }, - { - "x":1593413278000 - }, - { - "x":1593413279000 - }, - { - "x":1593413280000 - }, - { - "x":1593413281000 - }, - { - "x":1593413282000 - }, - { - "x":1593413283000 - }, - { - "x":1593413284000 - }, - { - "x":1593413285000 - }, - { - "x":1593413286000 - }, - { - "x":1593413287000, - "y":342000 - }, - { - "x":1593413288000 - }, - { - "x":1593413289000 - }, - { - "x":1593413290000 - }, - { - "x":1593413291000 - }, - { - "x":1593413292000 - }, - { - "x":1593413293000 - }, - { - "x":1593413294000 - }, - { - "x":1593413295000 - }, - { - "x":1593413296000 - }, - { - "x":1593413297000 - }, - { - "x":1593413298000, - "y":173000 - }, - { - "x":1593413299000 - }, - { - "x":1593413300000 - }, - { - "x":1593413301000, - "y":109000 - }, - { - "x":1593413302000 - }, - { - "x":1593413303000 - }, - { - "x":1593413304000 - }, - { - "x":1593413305000 - }, - { - "x":1593413306000 - }, - { - "x":1593413307000 - }, - { - "x":1593413308000 - }, - { - "x":1593413309000 - }, - { - "x":1593413310000 - }, - { - "x":1593413311000 - }, - { - "x":1593413312000 - }, - { - "x":1593413313000 - }, - { - "x":1593413314000 - }, - { - "x":1593413315000 - }, - { - "x":1593413316000 - }, - { - "x":1593413317000 - }, - { - "x":1593413318000, - "y":140000 - }, - { - "x":1593413319000 - }, - { - "x":1593413320000 - }, - { - "x":1593413321000 - }, - { - "x":1593413322000 - }, - { - "x":1593413323000 - }, - { - "x":1593413324000 - }, - { - "x":1593413325000 - }, - { - "x":1593413326000 - }, - { - "x":1593413327000 - }, - { - "x":1593413328000, - "y":77000 - }, - { - "x":1593413329000 - }, - { - "x":1593413330000 - }, - { - "x":1593413331000 - }, - { - "x":1593413332000 - }, - { - "x":1593413333000 - }, - { - "x":1593413334000 - }, - { - "x":1593413335000 - }, - { - "x":1593413336000 - }, - { - "x":1593413337000 - }, - { - "x":1593413338000 - }, - { - "x":1593413339000 - }, - { - "x":1593413340000 - } - ] - } -] \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser_transaction_name.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser_transaction_name.json deleted file mode 100644 index 107302831d55..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/avg_duration_by_browser_transaction_name.json +++ /dev/null @@ -1,731 +0,0 @@ -[ - { - "title":"HeadlessChrome", - "data":[ - { - "x":1593413100000 - }, - { - "x":1593413101000 - }, - { - "x":1593413102000 - }, - { - "x":1593413103000 - }, - { - "x":1593413104000 - }, - { - "x":1593413105000 - }, - { - "x":1593413106000 - }, - { - "x":1593413107000 - }, - { - "x":1593413108000 - }, - { - "x":1593413109000 - }, - { - "x":1593413110000 - }, - { - "x":1593413111000 - }, - { - "x":1593413112000 - }, - { - "x":1593413113000 - }, - { - "x":1593413114000 - }, - { - "x":1593413115000 - }, - { - "x":1593413116000 - }, - { - "x":1593413117000 - }, - { - "x":1593413118000 - }, - { - "x":1593413119000 - }, - { - "x":1593413120000 - }, - { - "x":1593413121000 - }, - { - "x":1593413122000 - }, - { - "x":1593413123000 - }, - { - "x":1593413124000 - }, - { - "x":1593413125000 - }, - { - "x":1593413126000 - }, - { - "x":1593413127000 - }, - { - "x":1593413128000 - }, - { - "x":1593413129000 - }, - { - "x":1593413130000 - }, - { - "x":1593413131000 - }, - { - "x":1593413132000 - }, - { - "x":1593413133000 - }, - { - "x":1593413134000 - }, - { - "x":1593413135000 - }, - { - "x":1593413136000 - }, - { - "x":1593413137000 - }, - { - "x":1593413138000 - }, - { - "x":1593413139000 - }, - { - "x":1593413140000 - }, - { - "x":1593413141000 - }, - { - "x":1593413142000 - }, - { - "x":1593413143000 - }, - { - "x":1593413144000 - }, - { - "x":1593413145000 - }, - { - "x":1593413146000 - }, - { - "x":1593413147000 - }, - { - "x":1593413148000 - }, - { - "x":1593413149000 - }, - { - "x":1593413150000 - }, - { - "x":1593413151000 - }, - { - "x":1593413152000 - }, - { - "x":1593413153000 - }, - { - "x":1593413154000 - }, - { - "x":1593413155000 - }, - { - "x":1593413156000 - }, - { - "x":1593413157000 - }, - { - "x":1593413158000 - }, - { - "x":1593413159000 - }, - { - "x":1593413160000 - }, - { - "x":1593413161000 - }, - { - "x":1593413162000 - }, - { - "x":1593413163000 - }, - { - "x":1593413164000 - }, - { - "x":1593413165000 - }, - { - "x":1593413166000 - }, - { - "x":1593413167000 - }, - { - "x":1593413168000 - }, - { - "x":1593413169000 - }, - { - "x":1593413170000 - }, - { - "x":1593413171000 - }, - { - "x":1593413172000 - }, - { - "x":1593413173000 - }, - { - "x":1593413174000 - }, - { - "x":1593413175000 - }, - { - "x":1593413176000 - }, - { - "x":1593413177000 - }, - { - "x":1593413178000 - }, - { - "x":1593413179000 - }, - { - "x":1593413180000 - }, - { - "x":1593413181000 - }, - { - "x":1593413182000 - }, - { - "x":1593413183000 - }, - { - "x":1593413184000 - }, - { - "x":1593413185000 - }, - { - "x":1593413186000 - }, - { - "x":1593413187000 - }, - { - "x":1593413188000 - }, - { - "x":1593413189000 - }, - { - "x":1593413190000 - }, - { - "x":1593413191000 - }, - { - "x":1593413192000 - }, - { - "x":1593413193000 - }, - { - "x":1593413194000 - }, - { - "x":1593413195000 - }, - { - "x":1593413196000 - }, - { - "x":1593413197000 - }, - { - "x":1593413198000 - }, - { - "x":1593413199000 - }, - { - "x":1593413200000 - }, - { - "x":1593413201000 - }, - { - "x":1593413202000 - }, - { - "x":1593413203000 - }, - { - "x":1593413204000 - }, - { - "x":1593413205000 - }, - { - "x":1593413206000 - }, - { - "x":1593413207000 - }, - { - "x":1593413208000 - }, - { - "x":1593413209000 - }, - { - "x":1593413210000 - }, - { - "x":1593413211000 - }, - { - "x":1593413212000 - }, - { - "x":1593413213000 - }, - { - "x":1593413214000 - }, - { - "x":1593413215000 - }, - { - "x":1593413216000 - }, - { - "x":1593413217000 - }, - { - "x":1593413218000 - }, - { - "x":1593413219000 - }, - { - "x":1593413220000 - }, - { - "x":1593413221000 - }, - { - "x":1593413222000 - }, - { - "x":1593413223000 - }, - { - "x":1593413224000 - }, - { - "x":1593413225000 - }, - { - "x":1593413226000 - }, - { - "x":1593413227000 - }, - { - "x":1593413228000 - }, - { - "x":1593413229000 - }, - { - "x":1593413230000 - }, - { - "x":1593413231000 - }, - { - "x":1593413232000 - }, - { - "x":1593413233000 - }, - { - "x":1593413234000 - }, - { - "x":1593413235000 - }, - { - "x":1593413236000 - }, - { - "x":1593413237000 - }, - { - "x":1593413238000 - }, - { - "x":1593413239000 - }, - { - "x":1593413240000 - }, - { - "x":1593413241000 - }, - { - "x":1593413242000 - }, - { - "x":1593413243000 - }, - { - "x":1593413244000 - }, - { - "x":1593413245000 - }, - { - "x":1593413246000 - }, - { - "x":1593413247000 - }, - { - "x":1593413248000 - }, - { - "x":1593413249000 - }, - { - "x":1593413250000 - }, - { - "x":1593413251000 - }, - { - "x":1593413252000 - }, - { - "x":1593413253000 - }, - { - "x":1593413254000 - }, - { - "x":1593413255000 - }, - { - "x":1593413256000 - }, - { - "x":1593413257000 - }, - { - "x":1593413258000 - }, - { - "x":1593413259000 - }, - { - "x":1593413260000 - }, - { - "x":1593413261000 - }, - { - "x":1593413262000 - }, - { - "x":1593413263000 - }, - { - "x":1593413264000 - }, - { - "x":1593413265000 - }, - { - "x":1593413266000 - }, - { - "x":1593413267000 - }, - { - "x":1593413268000 - }, - { - "x":1593413269000 - }, - { - "x":1593413270000 - }, - { - "x":1593413271000 - }, - { - "x":1593413272000 - }, - { - "x":1593413273000 - }, - { - "x":1593413274000 - }, - { - "x":1593413275000 - }, - { - "x":1593413276000 - }, - { - "x":1593413277000 - }, - { - "x":1593413278000 - }, - { - "x":1593413279000 - }, - { - "x":1593413280000 - }, - { - "x":1593413281000 - }, - { - "x":1593413282000 - }, - { - "x":1593413283000 - }, - { - "x":1593413284000 - }, - { - "x":1593413285000 - }, - { - "x":1593413286000 - }, - { - "x":1593413287000 - }, - { - "x":1593413288000 - }, - { - "x":1593413289000 - }, - { - "x":1593413290000 - }, - { - "x":1593413291000 - }, - { - "x":1593413292000 - }, - { - "x":1593413293000 - }, - { - "x":1593413294000 - }, - { - "x":1593413295000 - }, - { - "x":1593413296000 - }, - { - "x":1593413297000 - }, - { - "x":1593413298000 - }, - { - "x":1593413299000 - }, - { - "x":1593413300000 - }, - { - "x":1593413301000 - }, - { - "x":1593413302000 - }, - { - "x":1593413303000 - }, - { - "x":1593413304000 - }, - { - "x":1593413305000 - }, - { - "x":1593413306000 - }, - { - "x":1593413307000 - }, - { - "x":1593413308000 - }, - { - "x":1593413309000 - }, - { - "x":1593413310000 - }, - { - "x":1593413311000 - }, - { - "x":1593413312000 - }, - { - "x":1593413313000 - }, - { - "x":1593413314000 - }, - { - "x":1593413315000 - }, - { - "x":1593413316000 - }, - { - "x":1593413317000 - }, - { - "x":1593413318000 - }, - { - "x":1593413319000 - }, - { - "x":1593413320000 - }, - { - "x":1593413321000 - }, - { - "x":1593413322000 - }, - { - "x":1593413323000 - }, - { - "x":1593413324000 - }, - { - "x":1593413325000 - }, - { - "x":1593413326000 - }, - { - "x":1593413327000 - }, - { - "x":1593413328000, - "y":77000 - }, - { - "x":1593413329000 - }, - { - "x":1593413330000 - }, - { - "x":1593413331000 - }, - { - "x":1593413332000 - }, - { - "x":1593413333000 - }, - { - "x":1593413334000 - }, - { - "x":1593413335000 - }, - { - "x":1593413336000 - }, - { - "x":1593413337000 - }, - { - "x":1593413338000 - }, - { - "x":1593413339000 - }, - { - "x":1593413340000 - } - ] - } -] \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/breakdown.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/breakdown.json deleted file mode 100644 index 8ffbba64ec7a..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/breakdown.json +++ /dev/null @@ -1,184 +0,0 @@ -{ - "timeseries":[ - { - "title":"app", - "color":"#54b399", - "type":"areaStacked", - "data":[ - { - "x":1593413100000, - "y":null - }, - { - "x":1593413130000, - "y":null - }, - { - "x":1593413160000, - "y":null - }, - { - "x":1593413190000, - "y":null - }, - { - "x":1593413220000, - "y":null - }, - { - "x":1593413250000, - "y":null - }, - { - "x":1593413280000, - "y":null - }, - { - "x":1593413310000, - "y":0.16700861715223636 - }, - { - "x":1593413340000, - "y":null - } - ], - "hideLegend":false, - "legendValue": "17%" - }, - { - "title":"http", - "color":"#6092c0", - "type":"areaStacked", - "data":[ - { - "x":1593413100000, - "y":null - }, - { - "x":1593413130000, - "y":null - }, - { - "x":1593413160000, - "y":null - }, - { - "x":1593413190000, - "y":null - }, - { - "x":1593413220000, - "y":null - }, - { - "x":1593413250000, - "y":null - }, - { - "x":1593413280000, - "y":null - }, - { - "x":1593413310000, - "y":0.7702092736971686 - }, - { - "x":1593413340000, - "y":null - } - ], - "hideLegend":false, - "legendValue": "77%" - }, - { - "title":"postgresql", - "color":"#d36086", - "type":"areaStacked", - "data":[ - { - "x":1593413100000, - "y":null - }, - { - "x":1593413130000, - "y":null - }, - { - "x":1593413160000, - "y":null - }, - { - "x":1593413190000, - "y":null - }, - { - "x":1593413220000, - "y":null - }, - { - "x":1593413250000, - "y":null - }, - { - "x":1593413280000, - "y":null - }, - { - "x":1593413310000, - "y":0.0508822322527698 - }, - { - "x":1593413340000, - "y":null - } - ], - "hideLegend":false, - "legendValue": "5.1%" - }, - { - "title":"redis", - "color":"#9170b8", - "type":"areaStacked", - "data":[ - { - "x":1593413100000, - "y":null - }, - { - "x":1593413130000, - "y":null - }, - { - "x":1593413160000, - "y":null - }, - { - "x":1593413190000, - "y":null - }, - { - "x":1593413220000, - "y":null - }, - { - "x":1593413250000, - "y":null - }, - { - "x":1593413280000, - "y":null - }, - { - "x":1593413310000, - "y":0.011899876897825195 - }, - { - "x":1593413340000, - "y":null - } - ], - "hideLegend":false, - "legendValue": "1.2%" - } - ] -} \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json deleted file mode 100644 index 29c55d4ef1b5..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/top_transaction_groups.json +++ /dev/null @@ -1,3151 +0,0 @@ -{ - "items": [ - { - "key": "GET /api", - "averageResponseTime": 51175.73170731707, - "transactionsPerMinute": 10.25, - "impact": 100, - "p95": 259040, - "sample": { - "@timestamp": "2020-06-29T06:48:06.862Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.8" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:08.305742Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Connection": [ - "keep-alive" - ], - "Host": [ - "opbeans-node:3000" - ], - "Referer": [ - "http://opbeans-node:3000/dashboard" - ], - "Traceparent": [ - "00-ca86ffcac7753ec8733933bd8fd45d11-5dcb98c9c9021cfc-01" - ], - "User-Agent": [ - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.8" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:06 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "parent": { - "id": "5dcb98c9c9021cfc" - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413286862021 - }, - "trace": { - "id": "ca86ffcac7753ec8733933bd8fd45d11" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 15738 - }, - "id": "c95371db21c6f407", - "name": "GET /api", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/top", - "original": "/api/products/top", - "path": "/api/products/top", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": "POST /api/orders", - "averageResponseTime": 270684, - "transactionsPerMinute": 0.25, - "impact": 12.686265169840583, - "p95": 270336, - "sample": { - "@timestamp": "2020-06-29T06:48:39.953Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:43.991549Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "body": { - "original": "[REDACTED]" - }, - "headers": { - "Accept": [ - "application/json" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "129" - ], - "Content-Type": [ - "application/json" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "post", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "13" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:40 GMT" - ], - "Etag": [ - "W/\"d-eEOWU4Cnr5DZ23ErRUeYu9oOIks\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413319953033 - }, - "trace": { - "id": "52b8fda5f6df745b990740ba18378620" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 270684 - }, - "id": "a3afc2a112e9c893", - "name": "POST /api/orders", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 16 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/customers", - "averageResponseTime": 16896.8, - "transactionsPerMinute": 1.25, - "impact": 3.790160870423129, - "p95": 26432, - "sample": { - "@timestamp": "2020-06-29T06:48:28.444Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.982737Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "186769" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:28 GMT" - ], - "Etag": [ - "W/\"2d991-yG3J8W/roH7fSxXTudZrO27Ax9s\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413308444015 - }, - "trace": { - "id": "792fb0b00256164e88b277ec40b65e14" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 26471 - }, - "id": "6c1f848752563d2b", - "name": "GET /api/customers", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/customers", - "original": "/api/customers", - "path": "/api/customers", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /log-message", - "averageResponseTime": 32667.5, - "transactionsPerMinute": 0.5, - "impact": 2.875276331059301, - "p95": 38528, - "sample": { - "@timestamp": "2020-06-29T06:48:25.944Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.976822Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "24" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:25 GMT" - ], - "Etag": [ - "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413305944023 - }, - "trace": { - "id": "cd2ad726ad164d701c5d3103cbab0c81" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 38547 - }, - "id": "9e41667eb64dea55", - "name": "GET /log-message", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/log-message", - "original": "/log-message", - "path": "/log-message", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /*", - "averageResponseTime": 3262.95, - "transactionsPerMinute": 5, - "impact": 2.8716452680799467, - "p95": 4472, - "sample": { - "@timestamp": "2020-06-29T06:48:25.064Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:27.005197Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "Wget" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "813" - ], - "Content-Type": [ - "text/html" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:25 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "parent": { - "id": "f673ceaf4583f0f2" - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413305064023 - }, - "trace": { - "id": "30c12f4d8ef77a5be1b4464e5d2235bc" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 3004 - }, - "id": "18a00dfdb919a978", - "name": "GET /*", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/", - "original": "/", - "path": "/", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Wget", - "original": "Wget" - } - } - }, - { - "key": "GET /api/orders", - "averageResponseTime": 7615.625, - "transactionsPerMinute": 2, - "impact": 2.6645791239678345, - "p95": 11616, - "sample": { - "@timestamp": "2020-06-29T06:48:28.782Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.8" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:29.983252Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Connection": [ - "keep-alive" - ], - "Host": [ - "opbeans-node:3000" - ], - "Referer": [ - "http://opbeans-node:3000/orders" - ], - "Traceparent": [ - "00-978b56807e0b7a27cbc41a0dfb665f47-3358a24e09e23561-01" - ], - "User-Agent": [ - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.8" - } - }, - "response": { - "headers": { - "Connection": [ - "keep-alive" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:28 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "parent": { - "id": "3358a24e09e23561" - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413308782015 - }, - "trace": { - "id": "978b56807e0b7a27cbc41a0dfb665f47" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 7134 - }, - "id": "a6d8f3c5c98903e1", - "name": "GET /api/orders", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": "GET /api/products", - "averageResponseTime": 8585, - "transactionsPerMinute": 1.75, - "impact": 2.624924094061731, - "p95": 22112, - "sample": { - "@timestamp": "2020-06-29T06:48:21.475Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:26.996210Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "1023" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:21 GMT" - ], - "Etag": [ - "W/\"3ff-VyOxcDApb+a/lnjkm9FeTOGSDrs\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413301475015 - }, - "trace": { - "id": "389b26b16949c7f783223de4f14b788c" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 6775 - }, - "id": "d2d4088a0b104fb4", - "name": "GET /api/products", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products", - "original": "/api/products", - "path": "/api/products", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/products/:id", - "averageResponseTime": 13516.5, - "transactionsPerMinute": 1, - "impact": 2.3368756900811305, - "p95": 37856, - "sample": { - "@timestamp": "2020-06-29T06:47:57.555Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:59.085077Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "231" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:57 GMT" - ], - "Etag": [ - "W/\"e7-6JlJegaJ+ir0C8I8EmmOjms1dnc\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 87, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413277555176 - }, - "trace": { - "id": "8365e1763f19e4067b88521d4d9247a0" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 37709 - }, - "id": "be2722a418272f10", - "name": "GET /api/products/:id", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/1", - "original": "/api/products/1", - "path": "/api/products/1", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/types", - "averageResponseTime": 26992.5, - "transactionsPerMinute": 0.5, - "impact": 2.3330057413794503, - "p95": 45248, - "sample": { - "@timestamp": "2020-06-29T06:47:52.935Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.471071Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "112" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:52 GMT" - ], - "Etag": [ - "W/\"70-1z6hT7P1WHgBgS/BeUEVeHhOCQU\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413272935117 - }, - "trace": { - "id": "2946c536a33d163d0c984d00d1f3839a" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 45093 - }, - "id": "103482fda88b9400", - "name": "GET /api/types", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/types", - "original": "/api/types", - "path": "/api/types", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET static file", - "averageResponseTime": 3492.9285714285716, - "transactionsPerMinute": 3.5, - "impact": 2.0901067389184496, - "p95": 11900, - "sample": { - "@timestamp": "2020-06-29T06:47:53.427Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.472070Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Accept-Ranges": [ - "bytes" - ], - "Cache-Control": [ - "public, max-age=0" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "15086" - ], - "Content-Type": [ - "image/x-icon" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:53 GMT" - ], - "Etag": [ - "W/\"3aee-1725aff14f0\"" - ], - "Last-Modified": [ - "Thu, 28 May 2020 11:16:06 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413273427016 - }, - "trace": { - "id": "ec8a804fedf28fcf81d5682d69a16970" - }, - "transaction": { - "duration": { - "us": 4934 - }, - "id": "ab90a62901b770e6", - "name": "GET static file", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/favicon.ico", - "original": "/favicon.ico", - "path": "/favicon.ico", - "port": 3000, - "scheme": "http" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/products/top", - "averageResponseTime": 22958.5, - "transactionsPerMinute": 0.5, - "impact": 1.9475397398343375, - "p95": 33216, - "sample": { - "@timestamp": "2020-06-29T06:48:01.200Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:02.734903Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:01 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 115, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413281200133 - }, - "trace": { - "id": "195f32efeb6f91e2f71b6bc8bb74ae3a" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 33097 - }, - "id": "22e72956dfc8967a", - "name": "GET /api/products/top", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/top", - "original": "/api/products/top", - "path": "/api/products/top", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/stats", - "averageResponseTime": 7105.333333333333, - "transactionsPerMinute": 1.5, - "impact": 1.7905918202662048, - "p95": 15136, - "sample": { - "@timestamp": "2020-06-29T06:48:21.150Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.8" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:26.993832Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Accept": [ - "*/*" - ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Connection": [ - "keep-alive" - ], - "Host": [ - "opbeans-node:3000" - ], - "If-None-Match": [ - "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\"" - ], - "Referer": [ - "http://opbeans-node:3000/dashboard" - ], - "Traceparent": [ - "00-ee0ce8b38b8d5945829fc1c9432538bf-39d52cd5f528d363-01" - ], - "User-Agent": [ - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.8" - } - }, - "response": { - "headers": { - "Connection": [ - "keep-alive" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:21 GMT" - ], - "Etag": [ - "W/\"5c-6I+bqIiLxvkWuwBUnTxhBoK4lBk\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 304 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "parent": { - "id": "39d52cd5f528d363" - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.8" - }, - "timestamp": { - "us": 1593413301150014 - }, - "trace": { - "id": "ee0ce8b38b8d5945829fc1c9432538bf" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 7273 - }, - "id": "05d5b62182c59a54", - "name": "GET /api/stats", - "result": "HTTP 3xx", - "sampled": true, - "span_count": { - "started": 4 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/stats", - "original": "/api/stats", - "path": "/api/stats", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "HeadlessChrome", - "original": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/79.0.3945.0 Safari/537.36", - "os": { - "name": "Linux" - }, - "version": "79.0.3945" - } - } - }, - { - "key": "GET /log-error", - "averageResponseTime": 35846, - "transactionsPerMinute": 0.25, - "impact": 1.466376117925459, - "p95": 35840, - "sample": { - "@timestamp": "2020-06-29T06:48:07.467Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:18.533253Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "24" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:07 GMT" - ], - "Etag": [ - "W/\"18-MS3VbhH7auHMzO0fUuNF6v14N/M\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413287467017 - }, - "trace": { - "id": "d518b2c4d72cd2aaf1e39bad7ebcbdbb" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 35846 - }, - "id": "c7a30c1b076907ec", - "name": "GET /log-error", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/log-error", - "original": "/log-error", - "path": "/log-error", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "POST /api", - "averageResponseTime": 20011, - "transactionsPerMinute": 0.25, - "impact": 0.7098250353192541, - "p95": 19968, - "sample": { - "@timestamp": "2020-06-29T06:48:25.478Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:27.005671Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "body": { - "original": "[REDACTED]" - }, - "headers": { - "Accept": [ - "application/json" - ], - "Connection": [ - "close" - ], - "Content-Length": [ - "129" - ], - "Content-Type": [ - "application/json" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "post", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Allow": [ - "GET" - ], - "Connection": [ - "close" - ], - "Content-Type": [ - "application/json;charset=UTF-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:25 GMT" - ], - "Transfer-Encoding": [ - "chunked" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 405 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413305478010 - }, - "trace": { - "id": "4bd9027dd1e355ec742970e2d6333124" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 20011 - }, - "id": "94104435cf151478", - "name": "POST /api", - "result": "HTTP 4xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders", - "original": "/api/orders", - "path": "/api/orders", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/types/:id", - "averageResponseTime": 8181, - "transactionsPerMinute": 0.5, - "impact": 0.5354862351657939, - "p95": 10080, - "sample": { - "@timestamp": "2020-06-29T06:47:53.928Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:47:55.472718Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "205" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:47:53 GMT" - ], - "Etag": [ - "W/\"cd-pFMi1QOVY6YqWe+nwcbZVviCths\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 63, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413273928016 - }, - "trace": { - "id": "0becaafb422bfeb69e047bf7153aa469" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 10062 - }, - "id": "0cee4574091bda3b", - "name": "GET /api/types/:id", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 2 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/types/2", - "original": "/api/types/2", - "path": "/api/types/2", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/orders/:id", - "averageResponseTime": 4749.666666666667, - "transactionsPerMinute": 0.75, - "impact": 0.43453312891085794, - "p95": 7184, - "sample": { - "@timestamp": "2020-06-29T06:48:35.951Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:39.484133Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "0" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:35 GMT" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 404 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413315951017 - }, - "trace": { - "id": "95979caa80e6622cbbb2d308800c3016" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 3210 - }, - "id": "30344988dace0b43", - "name": "GET /api/orders/:id", - "result": "HTTP 4xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/orders/117", - "original": "/api/orders/117", - "path": "/api/orders/117", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /api/products/:id/customers", - "averageResponseTime": 4757, - "transactionsPerMinute": 0.5, - "impact": 0.20830834986820673, - "p95": 5616, - "sample": { - "@timestamp": "2020-06-29T06:48:22.977Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:27.000765Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "2" - ], - "Content-Type": [ - "application/json; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:22 GMT" - ], - "Etag": [ - "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 200 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413302977008 - }, - "trace": { - "id": "da8f22fe652ccb6680b3029ab6efd284" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 5618 - }, - "id": "bc51c1523afaf57a", - "name": "GET /api/products/:id/customers", - "result": "HTTP 2xx", - "sampled": true, - "span_count": { - "started": 1 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/api/products/3/customers", - "original": "/api/products/3/customers", - "path": "/api/products/3/customers", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - }, - { - "key": "GET /throw-error", - "averageResponseTime": 2577, - "transactionsPerMinute": 0.5, - "impact": 0, - "p95": 3224, - "sample": { - "@timestamp": "2020-06-29T06:48:19.975Z", - "agent": { - "name": "nodejs", - "version": "3.6.1" - }, - "client": { - "ip": "172.18.0.7" - }, - "container": { - "id": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "ecs": { - "version": "1.5.0" - }, - "event": { - "ingested": "2020-06-29T06:48:21.012520Z" - }, - "host": { - "architecture": "x64", - "hostname": "41712ded148f", - "ip": "172.18.0.7", - "name": "41712ded148f", - "os": { - "platform": "linux" - } - }, - "http": { - "request": { - "headers": { - "Connection": [ - "close" - ], - "Host": [ - "opbeans-node:3000" - ], - "User-Agent": [ - "workload/2.4.3" - ] - }, - "method": "get", - "socket": { - "encrypted": false, - "remote_address": "::ffff:172.18.0.7" - } - }, - "response": { - "headers": { - "Connection": [ - "close" - ], - "Content-Length": [ - "148" - ], - "Content-Security-Policy": [ - "default-src 'none'" - ], - "Content-Type": [ - "text/html; charset=utf-8" - ], - "Date": [ - "Mon, 29 Jun 2020 06:48:19 GMT" - ], - "X-Content-Type-Options": [ - "nosniff" - ], - "X-Powered-By": [ - "Express" - ] - }, - "status_code": 500 - }, - "version": "1.1" - }, - "labels": { - "foo": "bar", - "lorem": "ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus, ipsum id scelerisque consequat, enim leo vulputate massa, vel ultricies ante neque ac risus. Curabitur tincidunt vitae sapien id pulvinar. Mauris eu vestibulum tortor. Integer sit amet lorem fringilla, egestas tellus vitae, vulputate purus. Nulla feugiat blandit nunc et semper. Morbi purus libero, mattis sed mauris non, euismod iaculis lacus. Curabitur eleifend ante eros, non faucibus velit lacinia id. Duis posuere libero augue, at dignissim urna consectetur eget. Praesent eu congue est, iaculis finibus augue.", - "multi-line": "foo\nbar\nbaz", - "this-is-a-very-long-tag-name-without-any-spaces": "test" - }, - "observer": { - "ephemeral_id": "99908b73-9813-4a73-baa6-993db405523a", - "hostname": "aa0bd613aa4c", - "id": "1ccc5210-1e6c-4252-a5c8-1d6571a5fa2e", - "type": "apm-server", - "version": "8.0.0", - "version_major": 8 - }, - "process": { - "args": [ - "/usr/local/bin/node", - "/usr/local/lib/node_modules/pm2/lib/ProcessContainer.js", - "ecosystem-workload.config.js" - ], - "pid": 137, - "ppid": 1, - "title": "node /app/server.js" - }, - "processor": { - "event": "transaction", - "name": "transaction" - }, - "service": { - "environment": "production", - "framework": { - "name": "express", - "version": "4.17.1" - }, - "language": { - "name": "javascript" - }, - "name": "opbeans-node", - "node": { - "name": "41712ded148f30ee09a13421780eec4304bf5049b82a0d8dbc877893be6799e4" - }, - "runtime": { - "name": "node", - "version": "12.18.1" - }, - "version": "1.0.0" - }, - "source": { - "ip": "172.18.0.7" - }, - "timestamp": { - "us": 1593413299975019 - }, - "trace": { - "id": "106f3a55b0b0ea327d1bbe4be66c3bcc" - }, - "transaction": { - "custom": { - "shoppingBasketCount": 42 - }, - "duration": { - "us": 3226 - }, - "id": "247b9141552a9e73", - "name": "GET /throw-error", - "result": "HTTP 5xx", - "sampled": true, - "span_count": { - "started": 0 - }, - "type": "request" - }, - "url": { - "domain": "opbeans-node", - "full": "http://opbeans-node:3000/throw-error", - "original": "/throw-error", - "path": "/throw-error", - "port": 3000, - "scheme": "http" - }, - "user": { - "email": "kimchy@elastic.co", - "id": "42", - "name": "kimchy" - }, - "user_agent": { - "device": { - "name": "Other" - }, - "name": "Other", - "original": "workload/2.4.3" - } - } - } - ], - "isAggregationAccurate": true, - "bucketSize": 1000 -} \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/transaction_charts.json b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/transaction_charts.json deleted file mode 100644 index 0e878969f269..000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/expectation/transaction_charts.json +++ /dev/null @@ -1,1973 +0,0 @@ -{ - "apmTimeseries": { - "responseTimes": { - "avg": [ - { "x": 1593413100000, "y": null }, - { "x": 1593413101000, "y": null }, - { "x": 1593413102000, "y": null }, - { "x": 1593413103000, "y": null }, - { "x": 1593413104000, "y": null }, - { "x": 1593413105000, "y": null }, - { "x": 1593413106000, "y": null }, - { "x": 1593413107000, "y": null }, - { "x": 1593413108000, "y": null }, - { "x": 1593413109000, "y": null }, - { "x": 1593413110000, "y": null }, - { "x": 1593413111000, "y": null }, - { "x": 1593413112000, "y": null }, - { "x": 1593413113000, "y": null }, - { "x": 1593413114000, "y": null }, - { "x": 1593413115000, "y": null }, - { "x": 1593413116000, "y": null }, - { "x": 1593413117000, "y": null }, - { "x": 1593413118000, "y": null }, - { "x": 1593413119000, "y": null }, - { "x": 1593413120000, "y": null }, - { "x": 1593413121000, "y": null }, - { "x": 1593413122000, "y": null }, - { "x": 1593413123000, "y": null }, - { "x": 1593413124000, "y": null }, - { "x": 1593413125000, "y": null }, - { "x": 1593413126000, "y": null }, - { "x": 1593413127000, "y": null }, - { "x": 1593413128000, "y": null }, - { "x": 1593413129000, "y": null }, - { "x": 1593413130000, "y": null }, - { "x": 1593413131000, "y": null }, - { "x": 1593413132000, "y": null }, - { "x": 1593413133000, "y": null }, - { "x": 1593413134000, "y": null }, - { "x": 1593413135000, "y": null }, - { "x": 1593413136000, "y": null }, - { "x": 1593413137000, "y": null }, - { "x": 1593413138000, "y": null }, - { "x": 1593413139000, "y": null }, - { "x": 1593413140000, "y": null }, - { "x": 1593413141000, "y": null }, - { "x": 1593413142000, "y": null }, - { "x": 1593413143000, "y": null }, - { "x": 1593413144000, "y": null }, - { "x": 1593413145000, "y": null }, - { "x": 1593413146000, "y": null }, - { "x": 1593413147000, "y": null }, - { "x": 1593413148000, "y": null }, - { "x": 1593413149000, "y": null }, - { "x": 1593413150000, "y": null }, - { "x": 1593413151000, "y": null }, - { "x": 1593413152000, "y": null }, - { "x": 1593413153000, "y": null }, - { "x": 1593413154000, "y": null }, - { "x": 1593413155000, "y": null }, - { "x": 1593413156000, "y": null }, - { "x": 1593413157000, "y": null }, - { "x": 1593413158000, "y": null }, - { "x": 1593413159000, "y": null }, - { "x": 1593413160000, "y": null }, - { "x": 1593413161000, "y": null }, - { "x": 1593413162000, "y": null }, - { "x": 1593413163000, "y": null }, - { "x": 1593413164000, "y": null }, - { "x": 1593413165000, "y": null }, - { "x": 1593413166000, "y": null }, - { "x": 1593413167000, "y": null }, - { "x": 1593413168000, "y": null }, - { "x": 1593413169000, "y": null }, - { "x": 1593413170000, "y": null }, - { "x": 1593413171000, "y": null }, - { "x": 1593413172000, "y": null }, - { "x": 1593413173000, "y": null }, - { "x": 1593413174000, "y": null }, - { "x": 1593413175000, "y": null }, - { "x": 1593413176000, "y": null }, - { "x": 1593413177000, "y": null }, - { "x": 1593413178000, "y": null }, - { "x": 1593413179000, "y": null }, - { "x": 1593413180000, "y": null }, - { "x": 1593413181000, "y": null }, - { "x": 1593413182000, "y": null }, - { "x": 1593413183000, "y": null }, - { "x": 1593413184000, "y": null }, - { "x": 1593413185000, "y": null }, - { "x": 1593413186000, "y": null }, - { "x": 1593413187000, "y": null }, - { "x": 1593413188000, "y": null }, - { "x": 1593413189000, "y": null }, - { "x": 1593413190000, "y": null }, - { "x": 1593413191000, "y": null }, - { "x": 1593413192000, "y": null }, - { "x": 1593413193000, "y": null }, - { "x": 1593413194000, "y": null }, - { "x": 1593413195000, "y": null }, - { "x": 1593413196000, "y": null }, - { "x": 1593413197000, "y": null }, - { "x": 1593413198000, "y": null }, - { "x": 1593413199000, "y": null }, - { "x": 1593413200000, "y": null }, - { "x": 1593413201000, "y": null }, - { "x": 1593413202000, "y": null }, - { "x": 1593413203000, "y": null }, - { "x": 1593413204000, "y": null }, - { "x": 1593413205000, "y": null }, - { "x": 1593413206000, "y": null }, - { "x": 1593413207000, "y": null }, - { "x": 1593413208000, "y": null }, - { "x": 1593413209000, "y": null }, - { "x": 1593413210000, "y": null }, - { "x": 1593413211000, "y": null }, - { "x": 1593413212000, "y": null }, - { "x": 1593413213000, "y": null }, - { "x": 1593413214000, "y": null }, - { "x": 1593413215000, "y": null }, - { "x": 1593413216000, "y": null }, - { "x": 1593413217000, "y": null }, - { "x": 1593413218000, "y": null }, - { "x": 1593413219000, "y": null }, - { "x": 1593413220000, "y": null }, - { "x": 1593413221000, "y": null }, - { "x": 1593413222000, "y": null }, - { "x": 1593413223000, "y": null }, - { "x": 1593413224000, "y": null }, - { "x": 1593413225000, "y": null }, - { "x": 1593413226000, "y": null }, - { "x": 1593413227000, "y": null }, - { "x": 1593413228000, "y": null }, - { "x": 1593413229000, "y": null }, - { "x": 1593413230000, "y": null }, - { "x": 1593413231000, "y": null }, - { "x": 1593413232000, "y": null }, - { "x": 1593413233000, "y": null }, - { "x": 1593413234000, "y": null }, - { "x": 1593413235000, "y": null }, - { "x": 1593413236000, "y": null }, - { "x": 1593413237000, "y": null }, - { "x": 1593413238000, "y": null }, - { "x": 1593413239000, "y": null }, - { "x": 1593413240000, "y": null }, - { "x": 1593413241000, "y": null }, - { "x": 1593413242000, "y": null }, - { "x": 1593413243000, "y": null }, - { "x": 1593413244000, "y": null }, - { "x": 1593413245000, "y": null }, - { "x": 1593413246000, "y": null }, - { "x": 1593413247000, "y": null }, - { "x": 1593413248000, "y": null }, - { "x": 1593413249000, "y": null }, - { "x": 1593413250000, "y": null }, - { "x": 1593413251000, "y": null }, - { "x": 1593413252000, "y": null }, - { "x": 1593413253000, "y": null }, - { "x": 1593413254000, "y": null }, - { "x": 1593413255000, "y": null }, - { "x": 1593413256000, "y": null }, - { "x": 1593413257000, "y": null }, - { "x": 1593413258000, "y": null }, - { "x": 1593413259000, "y": null }, - { "x": 1593413260000, "y": null }, - { "x": 1593413261000, "y": null }, - { "x": 1593413262000, "y": null }, - { "x": 1593413263000, "y": null }, - { "x": 1593413264000, "y": null }, - { "x": 1593413265000, "y": null }, - { "x": 1593413266000, "y": null }, - { "x": 1593413267000, "y": null }, - { "x": 1593413268000, "y": null }, - { "x": 1593413269000, "y": null }, - { "x": 1593413270000, "y": null }, - { "x": 1593413271000, "y": null }, - { "x": 1593413272000, "y": 45093 }, - { "x": 1593413273000, "y": 7498 }, - { "x": 1593413274000, "y": null }, - { "x": 1593413275000, "y": null }, - { "x": 1593413276000, "y": null }, - { "x": 1593413277000, "y": 37709 }, - { "x": 1593413278000, "y": null }, - { "x": 1593413279000, "y": null }, - { "x": 1593413280000, "y": null }, - { "x": 1593413281000, "y": 33097 }, - { "x": 1593413282000, "y": null }, - { "x": 1593413283000, "y": null }, - { "x": 1593413284000, "y": 388507 }, - { "x": 1593413285000, "y": 42331.5 }, - { "x": 1593413286000, "y": 99104.25 }, - { "x": 1593413287000, "y": 18939.5 }, - { "x": 1593413288000, "y": 23229.5 }, - { "x": 1593413289000, "y": 11318 }, - { "x": 1593413290000, "y": 15651.25 }, - { "x": 1593413291000, "y": 2376 }, - { "x": 1593413292000, "y": 7796 }, - { "x": 1593413293000, "y": 7571 }, - { "x": 1593413294000, "y": 4219.333333333333 }, - { "x": 1593413295000, "y": 6827.5 }, - { "x": 1593413296000, "y": 10415.5 }, - { "x": 1593413297000, "y": 10082 }, - { "x": 1593413298000, "y": 6459.375 }, - { "x": 1593413299000, "y": 3131.5 }, - { "x": 1593413300000, "y": 6713.333333333333 }, - { "x": 1593413301000, "y": 8800 }, - { "x": 1593413302000, "y": 3743.5 }, - { "x": 1593413303000, "y": 9239.5 }, - { "x": 1593413304000, "y": 8402 }, - { "x": 1593413305000, "y": 20520.666666666668 }, - { "x": 1593413306000, "y": 9319.5 }, - { "x": 1593413307000, "y": 7694.333333333333 }, - { "x": 1593413308000, "y": 20131 }, - { "x": 1593413309000, "y": 439937.75 }, - { "x": 1593413310000, "y": 11933 }, - { "x": 1593413311000, "y": 18670.5 }, - { "x": 1593413312000, "y": 9232 }, - { "x": 1593413313000, "y": 7602 }, - { "x": 1593413314000, "y": 10428.8 }, - { "x": 1593413315000, "y": 8405.25 }, - { "x": 1593413316000, "y": 10654.5 }, - { "x": 1593413317000, "y": 10250 }, - { "x": 1593413318000, "y": 5775 }, - { "x": 1593413319000, "y": 137867 }, - { "x": 1593413320000, "y": 5694.333333333333 }, - { "x": 1593413321000, "y": 6115 }, - { "x": 1593413322000, "y": 1832.5 }, - { "x": 1593413323000, "y": null }, - { "x": 1593413324000, "y": null }, - { "x": 1593413325000, "y": null }, - { "x": 1593413326000, "y": null }, - { "x": 1593413327000, "y": null }, - { "x": 1593413328000, "y": null }, - { "x": 1593413329000, "y": null }, - { "x": 1593413330000, "y": null }, - { "x": 1593413331000, "y": null }, - { "x": 1593413332000, "y": null }, - { "x": 1593413333000, "y": null }, - { "x": 1593413334000, "y": null }, - { "x": 1593413335000, "y": null }, - { "x": 1593413336000, "y": null }, - { "x": 1593413337000, "y": null }, - { "x": 1593413338000, "y": null }, - { "x": 1593413339000, "y": null }, - { "x": 1593413340000, "y": null } - ], - "p95": [ - { "x": 1593413100000, "y": null }, - { "x": 1593413101000, "y": null }, - { "x": 1593413102000, "y": null }, - { "x": 1593413103000, "y": null }, - { "x": 1593413104000, "y": null }, - { "x": 1593413105000, "y": null }, - { "x": 1593413106000, "y": null }, - { "x": 1593413107000, "y": null }, - { "x": 1593413108000, "y": null }, - { "x": 1593413109000, "y": null }, - { "x": 1593413110000, "y": null }, - { "x": 1593413111000, "y": null }, - { "x": 1593413112000, "y": null }, - { "x": 1593413113000, "y": null }, - { "x": 1593413114000, "y": null }, - { "x": 1593413115000, "y": null }, - { "x": 1593413116000, "y": null }, - { "x": 1593413117000, "y": null }, - { "x": 1593413118000, "y": null }, - { "x": 1593413119000, "y": null }, - { "x": 1593413120000, "y": null }, - { "x": 1593413121000, "y": null }, - { "x": 1593413122000, "y": null }, - { "x": 1593413123000, "y": null }, - { "x": 1593413124000, "y": null }, - { "x": 1593413125000, "y": null }, - { "x": 1593413126000, "y": null }, - { "x": 1593413127000, "y": null }, - { "x": 1593413128000, "y": null }, - { "x": 1593413129000, "y": null }, - { "x": 1593413130000, "y": null }, - { "x": 1593413131000, "y": null }, - { "x": 1593413132000, "y": null }, - { "x": 1593413133000, "y": null }, - { "x": 1593413134000, "y": null }, - { "x": 1593413135000, "y": null }, - { "x": 1593413136000, "y": null }, - { "x": 1593413137000, "y": null }, - { "x": 1593413138000, "y": null }, - { "x": 1593413139000, "y": null }, - { "x": 1593413140000, "y": null }, - { "x": 1593413141000, "y": null }, - { "x": 1593413142000, "y": null }, - { "x": 1593413143000, "y": null }, - { "x": 1593413144000, "y": null }, - { "x": 1593413145000, "y": null }, - { "x": 1593413146000, "y": null }, - { "x": 1593413147000, "y": null }, - { "x": 1593413148000, "y": null }, - { "x": 1593413149000, "y": null }, - { "x": 1593413150000, "y": null }, - { "x": 1593413151000, "y": null }, - { "x": 1593413152000, "y": null }, - { "x": 1593413153000, "y": null }, - { "x": 1593413154000, "y": null }, - { "x": 1593413155000, "y": null }, - { "x": 1593413156000, "y": null }, - { "x": 1593413157000, "y": null }, - { "x": 1593413158000, "y": null }, - { "x": 1593413159000, "y": null }, - { "x": 1593413160000, "y": null }, - { "x": 1593413161000, "y": null }, - { "x": 1593413162000, "y": null }, - { "x": 1593413163000, "y": null }, - { "x": 1593413164000, "y": null }, - { "x": 1593413165000, "y": null }, - { "x": 1593413166000, "y": null }, - { "x": 1593413167000, "y": null }, - { "x": 1593413168000, "y": null }, - { "x": 1593413169000, "y": null }, - { "x": 1593413170000, "y": null }, - { "x": 1593413171000, "y": null }, - { "x": 1593413172000, "y": null }, - { "x": 1593413173000, "y": null }, - { "x": 1593413174000, "y": null }, - { "x": 1593413175000, "y": null }, - { "x": 1593413176000, "y": null }, - { "x": 1593413177000, "y": null }, - { "x": 1593413178000, "y": null }, - { "x": 1593413179000, "y": null }, - { "x": 1593413180000, "y": null }, - { "x": 1593413181000, "y": null }, - { "x": 1593413182000, "y": null }, - { "x": 1593413183000, "y": null }, - { "x": 1593413184000, "y": null }, - { "x": 1593413185000, "y": null }, - { "x": 1593413186000, "y": null }, - { "x": 1593413187000, "y": null }, - { "x": 1593413188000, "y": null }, - { "x": 1593413189000, "y": null }, - { "x": 1593413190000, "y": null }, - { "x": 1593413191000, "y": null }, - { "x": 1593413192000, "y": null }, - { "x": 1593413193000, "y": null }, - { "x": 1593413194000, "y": null }, - { "x": 1593413195000, "y": null }, - { "x": 1593413196000, "y": null }, - { "x": 1593413197000, "y": null }, - { "x": 1593413198000, "y": null }, - { "x": 1593413199000, "y": null }, - { "x": 1593413200000, "y": null }, - { "x": 1593413201000, "y": null }, - { "x": 1593413202000, "y": null }, - { "x": 1593413203000, "y": null }, - { "x": 1593413204000, "y": null }, - { "x": 1593413205000, "y": null }, - { "x": 1593413206000, "y": null }, - { "x": 1593413207000, "y": null }, - { "x": 1593413208000, "y": null }, - { "x": 1593413209000, "y": null }, - { "x": 1593413210000, "y": null }, - { "x": 1593413211000, "y": null }, - { "x": 1593413212000, "y": null }, - { "x": 1593413213000, "y": null }, - { "x": 1593413214000, "y": null }, - { "x": 1593413215000, "y": null }, - { "x": 1593413216000, "y": null }, - { "x": 1593413217000, "y": null }, - { "x": 1593413218000, "y": null }, - { "x": 1593413219000, "y": null }, - { "x": 1593413220000, "y": null }, - { "x": 1593413221000, "y": null }, - { "x": 1593413222000, "y": null }, - { "x": 1593413223000, "y": null }, - { "x": 1593413224000, "y": null }, - { "x": 1593413225000, "y": null }, - { "x": 1593413226000, "y": null }, - { "x": 1593413227000, "y": null }, - { "x": 1593413228000, "y": null }, - { "x": 1593413229000, "y": null }, - { "x": 1593413230000, "y": null }, - { "x": 1593413231000, "y": null }, - { "x": 1593413232000, "y": null }, - { "x": 1593413233000, "y": null }, - { "x": 1593413234000, "y": null }, - { "x": 1593413235000, "y": null }, - { "x": 1593413236000, "y": null }, - { "x": 1593413237000, "y": null }, - { "x": 1593413238000, "y": null }, - { "x": 1593413239000, "y": null }, - { "x": 1593413240000, "y": null }, - { "x": 1593413241000, "y": null }, - { "x": 1593413242000, "y": null }, - { "x": 1593413243000, "y": null }, - { "x": 1593413244000, "y": null }, - { "x": 1593413245000, "y": null }, - { "x": 1593413246000, "y": null }, - { "x": 1593413247000, "y": null }, - { "x": 1593413248000, "y": null }, - { "x": 1593413249000, "y": null }, - { "x": 1593413250000, "y": null }, - { "x": 1593413251000, "y": null }, - { "x": 1593413252000, "y": null }, - { "x": 1593413253000, "y": null }, - { "x": 1593413254000, "y": null }, - { "x": 1593413255000, "y": null }, - { "x": 1593413256000, "y": null }, - { "x": 1593413257000, "y": null }, - { "x": 1593413258000, "y": null }, - { "x": 1593413259000, "y": null }, - { "x": 1593413260000, "y": null }, - { "x": 1593413261000, "y": null }, - { "x": 1593413262000, "y": null }, - { "x": 1593413263000, "y": null }, - { "x": 1593413264000, "y": null }, - { "x": 1593413265000, "y": null }, - { "x": 1593413266000, "y": null }, - { "x": 1593413267000, "y": null }, - { "x": 1593413268000, "y": null }, - { "x": 1593413269000, "y": null }, - { "x": 1593413270000, "y": null }, - { "x": 1593413271000, "y": null }, - { "x": 1593413272000, "y": 45056 }, - { "x": 1593413273000, "y": 10080 }, - { "x": 1593413274000, "y": null }, - { "x": 1593413275000, "y": null }, - { "x": 1593413276000, "y": null }, - { "x": 1593413277000, "y": 37632 }, - { "x": 1593413278000, "y": null }, - { "x": 1593413279000, "y": null }, - { "x": 1593413280000, "y": null }, - { "x": 1593413281000, "y": 33024 }, - { "x": 1593413282000, "y": null }, - { "x": 1593413283000, "y": null }, - { "x": 1593413284000, "y": 761728 }, - { "x": 1593413285000, "y": 81904 }, - { "x": 1593413286000, "y": 358384 }, - { "x": 1593413287000, "y": 36088 }, - { "x": 1593413288000, "y": 44536 }, - { "x": 1593413289000, "y": 11648 }, - { "x": 1593413290000, "y": 31984 }, - { "x": 1593413291000, "y": 2920 }, - { "x": 1593413292000, "y": 9312 }, - { "x": 1593413293000, "y": 10912 }, - { "x": 1593413294000, "y": 6392 }, - { "x": 1593413295000, "y": 11704 }, - { "x": 1593413296000, "y": 10816 }, - { "x": 1593413297000, "y": 12000 }, - { "x": 1593413298000, "y": 15164 }, - { "x": 1593413299000, "y": 3216 }, - { "x": 1593413300000, "y": 9584 }, - { "x": 1593413301000, "y": 21240 }, - { "x": 1593413302000, "y": 5624 }, - { "x": 1593413303000, "y": 11360 }, - { "x": 1593413304000, "y": 12320 }, - { "x": 1593413305000, "y": 38640 }, - { "x": 1593413306000, "y": 9728 }, - { "x": 1593413307000, "y": 17016 }, - { "x": 1593413308000, "y": 26848 }, - { "x": 1593413309000, "y": 1753072 }, - { "x": 1593413310000, "y": 16992 }, - { "x": 1593413311000, "y": 26560 }, - { "x": 1593413312000, "y": 11232 }, - { "x": 1593413313000, "y": 11424 }, - { "x": 1593413314000, "y": 16096 }, - { "x": 1593413315000, "y": 18800 }, - { "x": 1593413316000, "y": 12672 }, - { "x": 1593413317000, "y": 24316 }, - { "x": 1593413318000, "y": 8944 }, - { "x": 1593413319000, "y": 272352 }, - { "x": 1593413320000, "y": 7992 }, - { "x": 1593413321000, "y": 8368 }, - { "x": 1593413322000, "y": 1928 }, - { "x": 1593413323000, "y": null }, - { "x": 1593413324000, "y": null }, - { "x": 1593413325000, "y": null }, - { "x": 1593413326000, "y": null }, - { "x": 1593413327000, "y": null }, - { "x": 1593413328000, "y": null }, - { "x": 1593413329000, "y": null }, - { "x": 1593413330000, "y": null }, - { "x": 1593413331000, "y": null }, - { "x": 1593413332000, "y": null }, - { "x": 1593413333000, "y": null }, - { "x": 1593413334000, "y": null }, - { "x": 1593413335000, "y": null }, - { "x": 1593413336000, "y": null }, - { "x": 1593413337000, "y": null }, - { "x": 1593413338000, "y": null }, - { "x": 1593413339000, "y": null }, - { "x": 1593413340000, "y": null } - ], - "p99": [ - { "x": 1593413100000, "y": null }, - { "x": 1593413101000, "y": null }, - { "x": 1593413102000, "y": null }, - { "x": 1593413103000, "y": null }, - { "x": 1593413104000, "y": null }, - { "x": 1593413105000, "y": null }, - { "x": 1593413106000, "y": null }, - { "x": 1593413107000, "y": null }, - { "x": 1593413108000, "y": null }, - { "x": 1593413109000, "y": null }, - { "x": 1593413110000, "y": null }, - { "x": 1593413111000, "y": null }, - { "x": 1593413112000, "y": null }, - { "x": 1593413113000, "y": null }, - { "x": 1593413114000, "y": null }, - { "x": 1593413115000, "y": null }, - { "x": 1593413116000, "y": null }, - { "x": 1593413117000, "y": null }, - { "x": 1593413118000, "y": null }, - { "x": 1593413119000, "y": null }, - { "x": 1593413120000, "y": null }, - { "x": 1593413121000, "y": null }, - { "x": 1593413122000, "y": null }, - { "x": 1593413123000, "y": null }, - { "x": 1593413124000, "y": null }, - { "x": 1593413125000, "y": null }, - { "x": 1593413126000, "y": null }, - { "x": 1593413127000, "y": null }, - { "x": 1593413128000, "y": null }, - { "x": 1593413129000, "y": null }, - { "x": 1593413130000, "y": null }, - { "x": 1593413131000, "y": null }, - { "x": 1593413132000, "y": null }, - { "x": 1593413133000, "y": null }, - { "x": 1593413134000, "y": null }, - { "x": 1593413135000, "y": null }, - { "x": 1593413136000, "y": null }, - { "x": 1593413137000, "y": null }, - { "x": 1593413138000, "y": null }, - { "x": 1593413139000, "y": null }, - { "x": 1593413140000, "y": null }, - { "x": 1593413141000, "y": null }, - { "x": 1593413142000, "y": null }, - { "x": 1593413143000, "y": null }, - { "x": 1593413144000, "y": null }, - { "x": 1593413145000, "y": null }, - { "x": 1593413146000, "y": null }, - { "x": 1593413147000, "y": null }, - { "x": 1593413148000, "y": null }, - { "x": 1593413149000, "y": null }, - { "x": 1593413150000, "y": null }, - { "x": 1593413151000, "y": null }, - { "x": 1593413152000, "y": null }, - { "x": 1593413153000, "y": null }, - { "x": 1593413154000, "y": null }, - { "x": 1593413155000, "y": null }, - { "x": 1593413156000, "y": null }, - { "x": 1593413157000, "y": null }, - { "x": 1593413158000, "y": null }, - { "x": 1593413159000, "y": null }, - { "x": 1593413160000, "y": null }, - { "x": 1593413161000, "y": null }, - { "x": 1593413162000, "y": null }, - { "x": 1593413163000, "y": null }, - { "x": 1593413164000, "y": null }, - { "x": 1593413165000, "y": null }, - { "x": 1593413166000, "y": null }, - { "x": 1593413167000, "y": null }, - { "x": 1593413168000, "y": null }, - { "x": 1593413169000, "y": null }, - { "x": 1593413170000, "y": null }, - { "x": 1593413171000, "y": null }, - { "x": 1593413172000, "y": null }, - { "x": 1593413173000, "y": null }, - { "x": 1593413174000, "y": null }, - { "x": 1593413175000, "y": null }, - { "x": 1593413176000, "y": null }, - { "x": 1593413177000, "y": null }, - { "x": 1593413178000, "y": null }, - { "x": 1593413179000, "y": null }, - { "x": 1593413180000, "y": null }, - { "x": 1593413181000, "y": null }, - { "x": 1593413182000, "y": null }, - { "x": 1593413183000, "y": null }, - { "x": 1593413184000, "y": null }, - { "x": 1593413185000, "y": null }, - { "x": 1593413186000, "y": null }, - { "x": 1593413187000, "y": null }, - { "x": 1593413188000, "y": null }, - { "x": 1593413189000, "y": null }, - { "x": 1593413190000, "y": null }, - { "x": 1593413191000, "y": null }, - { "x": 1593413192000, "y": null }, - { "x": 1593413193000, "y": null }, - { "x": 1593413194000, "y": null }, - { "x": 1593413195000, "y": null }, - { "x": 1593413196000, "y": null }, - { "x": 1593413197000, "y": null }, - { "x": 1593413198000, "y": null }, - { "x": 1593413199000, "y": null }, - { "x": 1593413200000, "y": null }, - { "x": 1593413201000, "y": null }, - { "x": 1593413202000, "y": null }, - { "x": 1593413203000, "y": null }, - { "x": 1593413204000, "y": null }, - { "x": 1593413205000, "y": null }, - { "x": 1593413206000, "y": null }, - { "x": 1593413207000, "y": null }, - { "x": 1593413208000, "y": null }, - { "x": 1593413209000, "y": null }, - { "x": 1593413210000, "y": null }, - { "x": 1593413211000, "y": null }, - { "x": 1593413212000, "y": null }, - { "x": 1593413213000, "y": null }, - { "x": 1593413214000, "y": null }, - { "x": 1593413215000, "y": null }, - { "x": 1593413216000, "y": null }, - { "x": 1593413217000, "y": null }, - { "x": 1593413218000, "y": null }, - { "x": 1593413219000, "y": null }, - { "x": 1593413220000, "y": null }, - { "x": 1593413221000, "y": null }, - { "x": 1593413222000, "y": null }, - { "x": 1593413223000, "y": null }, - { "x": 1593413224000, "y": null }, - { "x": 1593413225000, "y": null }, - { "x": 1593413226000, "y": null }, - { "x": 1593413227000, "y": null }, - { "x": 1593413228000, "y": null }, - { "x": 1593413229000, "y": null }, - { "x": 1593413230000, "y": null }, - { "x": 1593413231000, "y": null }, - { "x": 1593413232000, "y": null }, - { "x": 1593413233000, "y": null }, - { "x": 1593413234000, "y": null }, - { "x": 1593413235000, "y": null }, - { "x": 1593413236000, "y": null }, - { "x": 1593413237000, "y": null }, - { "x": 1593413238000, "y": null }, - { "x": 1593413239000, "y": null }, - { "x": 1593413240000, "y": null }, - { "x": 1593413241000, "y": null }, - { "x": 1593413242000, "y": null }, - { "x": 1593413243000, "y": null }, - { "x": 1593413244000, "y": null }, - { "x": 1593413245000, "y": null }, - { "x": 1593413246000, "y": null }, - { "x": 1593413247000, "y": null }, - { "x": 1593413248000, "y": null }, - { "x": 1593413249000, "y": null }, - { "x": 1593413250000, "y": null }, - { "x": 1593413251000, "y": null }, - { "x": 1593413252000, "y": null }, - { "x": 1593413253000, "y": null }, - { "x": 1593413254000, "y": null }, - { "x": 1593413255000, "y": null }, - { "x": 1593413256000, "y": null }, - { "x": 1593413257000, "y": null }, - { "x": 1593413258000, "y": null }, - { "x": 1593413259000, "y": null }, - { "x": 1593413260000, "y": null }, - { "x": 1593413261000, "y": null }, - { "x": 1593413262000, "y": null }, - { "x": 1593413263000, "y": null }, - { "x": 1593413264000, "y": null }, - { "x": 1593413265000, "y": null }, - { "x": 1593413266000, "y": null }, - { "x": 1593413267000, "y": null }, - { "x": 1593413268000, "y": null }, - { "x": 1593413269000, "y": null }, - { "x": 1593413270000, "y": null }, - { "x": 1593413271000, "y": null }, - { "x": 1593413272000, "y": 45056 }, - { "x": 1593413273000, "y": 10080 }, - { "x": 1593413274000, "y": null }, - { "x": 1593413275000, "y": null }, - { "x": 1593413276000, "y": null }, - { "x": 1593413277000, "y": 37632 }, - { "x": 1593413278000, "y": null }, - { "x": 1593413279000, "y": null }, - { "x": 1593413280000, "y": null }, - { "x": 1593413281000, "y": 33024 }, - { "x": 1593413282000, "y": null }, - { "x": 1593413283000, "y": null }, - { "x": 1593413284000, "y": 761728 }, - { "x": 1593413285000, "y": 81904 }, - { "x": 1593413286000, "y": 358384 }, - { "x": 1593413287000, "y": 36088 }, - { "x": 1593413288000, "y": 44536 }, - { "x": 1593413289000, "y": 11648 }, - { "x": 1593413290000, "y": 31984 }, - { "x": 1593413291000, "y": 2920 }, - { "x": 1593413292000, "y": 9312 }, - { "x": 1593413293000, "y": 10912 }, - { "x": 1593413294000, "y": 6392 }, - { "x": 1593413295000, "y": 11704 }, - { "x": 1593413296000, "y": 10816 }, - { "x": 1593413297000, "y": 12000 }, - { "x": 1593413298000, "y": 15164 }, - { "x": 1593413299000, "y": 3216 }, - { "x": 1593413300000, "y": 9584 }, - { "x": 1593413301000, "y": 21240 }, - { "x": 1593413302000, "y": 5624 }, - { "x": 1593413303000, "y": 11360 }, - { "x": 1593413304000, "y": 12320 }, - { "x": 1593413305000, "y": 38640 }, - { "x": 1593413306000, "y": 9728 }, - { "x": 1593413307000, "y": 17016 }, - { "x": 1593413308000, "y": 26848 }, - { "x": 1593413309000, "y": 1753072 }, - { "x": 1593413310000, "y": 16992 }, - { "x": 1593413311000, "y": 26560 }, - { "x": 1593413312000, "y": 11232 }, - { "x": 1593413313000, "y": 11424 }, - { "x": 1593413314000, "y": 16096 }, - { "x": 1593413315000, "y": 18800 }, - { "x": 1593413316000, "y": 12672 }, - { "x": 1593413317000, "y": 24316 }, - { "x": 1593413318000, "y": 8944 }, - { "x": 1593413319000, "y": 272352 }, - { "x": 1593413320000, "y": 7992 }, - { "x": 1593413321000, "y": 8368 }, - { "x": 1593413322000, "y": 1928 }, - { "x": 1593413323000, "y": null }, - { "x": 1593413324000, "y": null }, - { "x": 1593413325000, "y": null }, - { "x": 1593413326000, "y": null }, - { "x": 1593413327000, "y": null }, - { "x": 1593413328000, "y": null }, - { "x": 1593413329000, "y": null }, - { "x": 1593413330000, "y": null }, - { "x": 1593413331000, "y": null }, - { "x": 1593413332000, "y": null }, - { "x": 1593413333000, "y": null }, - { "x": 1593413334000, "y": null }, - { "x": 1593413335000, "y": null }, - { "x": 1593413336000, "y": null }, - { "x": 1593413337000, "y": null }, - { "x": 1593413338000, "y": null }, - { "x": 1593413339000, "y": null }, - { "x": 1593413340000, "y": null } - ] - }, - "tpmBuckets": [ - { - "key": "HTTP 2xx", - "dataPoints": [ - { "x": 1593413100000, "y": 0 }, - { "x": 1593413101000, "y": 0 }, - { "x": 1593413102000, "y": 0 }, - { "x": 1593413103000, "y": 0 }, - { "x": 1593413104000, "y": 0 }, - { "x": 1593413105000, "y": 0 }, - { "x": 1593413106000, "y": 0 }, - { "x": 1593413107000, "y": 0 }, - { "x": 1593413108000, "y": 0 }, - { "x": 1593413109000, "y": 0 }, - { "x": 1593413110000, "y": 0 }, - { "x": 1593413111000, "y": 0 }, - { "x": 1593413112000, "y": 0 }, - { "x": 1593413113000, "y": 0 }, - { "x": 1593413114000, "y": 0 }, - { "x": 1593413115000, "y": 0 }, - { "x": 1593413116000, "y": 0 }, - { "x": 1593413117000, "y": 0 }, - { "x": 1593413118000, "y": 0 }, - { "x": 1593413119000, "y": 0 }, - { "x": 1593413120000, "y": 0 }, - { "x": 1593413121000, "y": 0 }, - { "x": 1593413122000, "y": 0 }, - { "x": 1593413123000, "y": 0 }, - { "x": 1593413124000, "y": 0 }, - { "x": 1593413125000, "y": 0 }, - { "x": 1593413126000, "y": 0 }, - { "x": 1593413127000, "y": 0 }, - { "x": 1593413128000, "y": 0 }, - { "x": 1593413129000, "y": 0 }, - { "x": 1593413130000, "y": 0 }, - { "x": 1593413131000, "y": 0 }, - { "x": 1593413132000, "y": 0 }, - { "x": 1593413133000, "y": 0 }, - { "x": 1593413134000, "y": 0 }, - { "x": 1593413135000, "y": 0 }, - { "x": 1593413136000, "y": 0 }, - { "x": 1593413137000, "y": 0 }, - { "x": 1593413138000, "y": 0 }, - { "x": 1593413139000, "y": 0 }, - { "x": 1593413140000, "y": 0 }, - { "x": 1593413141000, "y": 0 }, - { "x": 1593413142000, "y": 0 }, - { "x": 1593413143000, "y": 0 }, - { "x": 1593413144000, "y": 0 }, - { "x": 1593413145000, "y": 0 }, - { "x": 1593413146000, "y": 0 }, - { "x": 1593413147000, "y": 0 }, - { "x": 1593413148000, "y": 0 }, - { "x": 1593413149000, "y": 0 }, - { "x": 1593413150000, "y": 0 }, - { "x": 1593413151000, "y": 0 }, - { "x": 1593413152000, "y": 0 }, - { "x": 1593413153000, "y": 0 }, - { "x": 1593413154000, "y": 0 }, - { "x": 1593413155000, "y": 0 }, - { "x": 1593413156000, "y": 0 }, - { "x": 1593413157000, "y": 0 }, - { "x": 1593413158000, "y": 0 }, - { "x": 1593413159000, "y": 0 }, - { "x": 1593413160000, "y": 0 }, - { "x": 1593413161000, "y": 0 }, - { "x": 1593413162000, "y": 0 }, - { "x": 1593413163000, "y": 0 }, - { "x": 1593413164000, "y": 0 }, - { "x": 1593413165000, "y": 0 }, - { "x": 1593413166000, "y": 0 }, - { "x": 1593413167000, "y": 0 }, - { "x": 1593413168000, "y": 0 }, - { "x": 1593413169000, "y": 0 }, - { "x": 1593413170000, "y": 0 }, - { "x": 1593413171000, "y": 0 }, - { "x": 1593413172000, "y": 0 }, - { "x": 1593413173000, "y": 0 }, - { "x": 1593413174000, "y": 0 }, - { "x": 1593413175000, "y": 0 }, - { "x": 1593413176000, "y": 0 }, - { "x": 1593413177000, "y": 0 }, - { "x": 1593413178000, "y": 0 }, - { "x": 1593413179000, "y": 0 }, - { "x": 1593413180000, "y": 0 }, - { "x": 1593413181000, "y": 0 }, - { "x": 1593413182000, "y": 0 }, - { "x": 1593413183000, "y": 0 }, - { "x": 1593413184000, "y": 0 }, - { "x": 1593413185000, "y": 0 }, - { "x": 1593413186000, "y": 0 }, - { "x": 1593413187000, "y": 0 }, - { "x": 1593413188000, "y": 0 }, - { "x": 1593413189000, "y": 0 }, - { "x": 1593413190000, "y": 0 }, - { "x": 1593413191000, "y": 0 }, - { "x": 1593413192000, "y": 0 }, - { "x": 1593413193000, "y": 0 }, - { "x": 1593413194000, "y": 0 }, - { "x": 1593413195000, "y": 0 }, - { "x": 1593413196000, "y": 0 }, - { "x": 1593413197000, "y": 0 }, - { "x": 1593413198000, "y": 0 }, - { "x": 1593413199000, "y": 0 }, - { "x": 1593413200000, "y": 0 }, - { "x": 1593413201000, "y": 0 }, - { "x": 1593413202000, "y": 0 }, - { "x": 1593413203000, "y": 0 }, - { "x": 1593413204000, "y": 0 }, - { "x": 1593413205000, "y": 0 }, - { "x": 1593413206000, "y": 0 }, - { "x": 1593413207000, "y": 0 }, - { "x": 1593413208000, "y": 0 }, - { "x": 1593413209000, "y": 0 }, - { "x": 1593413210000, "y": 0 }, - { "x": 1593413211000, "y": 0 }, - { "x": 1593413212000, "y": 0 }, - { "x": 1593413213000, "y": 0 }, - { "x": 1593413214000, "y": 0 }, - { "x": 1593413215000, "y": 0 }, - { "x": 1593413216000, "y": 0 }, - { "x": 1593413217000, "y": 0 }, - { "x": 1593413218000, "y": 0 }, - { "x": 1593413219000, "y": 0 }, - { "x": 1593413220000, "y": 0 }, - { "x": 1593413221000, "y": 0 }, - { "x": 1593413222000, "y": 0 }, - { "x": 1593413223000, "y": 0 }, - { "x": 1593413224000, "y": 0 }, - { "x": 1593413225000, "y": 0 }, - { "x": 1593413226000, "y": 0 }, - { "x": 1593413227000, "y": 0 }, - { "x": 1593413228000, "y": 0 }, - { "x": 1593413229000, "y": 0 }, - { "x": 1593413230000, "y": 0 }, - { "x": 1593413231000, "y": 0 }, - { "x": 1593413232000, "y": 0 }, - { "x": 1593413233000, "y": 0 }, - { "x": 1593413234000, "y": 0 }, - { "x": 1593413235000, "y": 0 }, - { "x": 1593413236000, "y": 0 }, - { "x": 1593413237000, "y": 0 }, - { "x": 1593413238000, "y": 0 }, - { "x": 1593413239000, "y": 0 }, - { "x": 1593413240000, "y": 0 }, - { "x": 1593413241000, "y": 0 }, - { "x": 1593413242000, "y": 0 }, - { "x": 1593413243000, "y": 0 }, - { "x": 1593413244000, "y": 0 }, - { "x": 1593413245000, "y": 0 }, - { "x": 1593413246000, "y": 0 }, - { "x": 1593413247000, "y": 0 }, - { "x": 1593413248000, "y": 0 }, - { "x": 1593413249000, "y": 0 }, - { "x": 1593413250000, "y": 0 }, - { "x": 1593413251000, "y": 0 }, - { "x": 1593413252000, "y": 0 }, - { "x": 1593413253000, "y": 0 }, - { "x": 1593413254000, "y": 0 }, - { "x": 1593413255000, "y": 0 }, - { "x": 1593413256000, "y": 0 }, - { "x": 1593413257000, "y": 0 }, - { "x": 1593413258000, "y": 0 }, - { "x": 1593413259000, "y": 0 }, - { "x": 1593413260000, "y": 0 }, - { "x": 1593413261000, "y": 0 }, - { "x": 1593413262000, "y": 0 }, - { "x": 1593413263000, "y": 0 }, - { "x": 1593413264000, "y": 0 }, - { "x": 1593413265000, "y": 0 }, - { "x": 1593413266000, "y": 0 }, - { "x": 1593413267000, "y": 0 }, - { "x": 1593413268000, "y": 0 }, - { "x": 1593413269000, "y": 0 }, - { "x": 1593413270000, "y": 0 }, - { "x": 1593413271000, "y": 0 }, - { "x": 1593413272000, "y": 1 }, - { "x": 1593413273000, "y": 2 }, - { "x": 1593413274000, "y": 0 }, - { "x": 1593413275000, "y": 0 }, - { "x": 1593413276000, "y": 0 }, - { "x": 1593413277000, "y": 1 }, - { "x": 1593413278000, "y": 0 }, - { "x": 1593413279000, "y": 0 }, - { "x": 1593413280000, "y": 0 }, - { "x": 1593413281000, "y": 1 }, - { "x": 1593413282000, "y": 0 }, - { "x": 1593413283000, "y": 0 }, - { "x": 1593413284000, "y": 2 }, - { "x": 1593413285000, "y": 2 }, - { "x": 1593413286000, "y": 7 }, - { "x": 1593413287000, "y": 1 }, - { "x": 1593413288000, "y": 2 }, - { "x": 1593413289000, "y": 1 }, - { "x": 1593413290000, "y": 4 }, - { "x": 1593413291000, "y": 2 }, - { "x": 1593413292000, "y": 1 }, - { "x": 1593413293000, "y": 2 }, - { "x": 1593413294000, "y": 3 }, - { "x": 1593413295000, "y": 2 }, - { "x": 1593413296000, "y": 2 }, - { "x": 1593413297000, "y": 2 }, - { "x": 1593413298000, "y": 6 }, - { "x": 1593413299000, "y": 1 }, - { "x": 1593413300000, "y": 2 }, - { "x": 1593413301000, "y": 3 }, - { "x": 1593413302000, "y": 2 }, - { "x": 1593413303000, "y": 2 }, - { "x": 1593413304000, "y": 2 }, - { "x": 1593413305000, "y": 1 }, - { "x": 1593413306000, "y": 2 }, - { "x": 1593413307000, "y": 3 }, - { "x": 1593413308000, "y": 2 }, - { "x": 1593413309000, "y": 2 }, - { "x": 1593413310000, "y": 2 }, - { "x": 1593413311000, "y": 1 }, - { "x": 1593413312000, "y": 3 }, - { "x": 1593413313000, "y": 3 }, - { "x": 1593413314000, "y": 5 }, - { "x": 1593413315000, "y": 2 }, - { "x": 1593413316000, "y": 2 }, - { "x": 1593413317000, "y": 6 }, - { "x": 1593413318000, "y": 2 }, - { "x": 1593413319000, "y": 2 }, - { "x": 1593413320000, "y": 2 }, - { "x": 1593413321000, "y": 2 }, - { "x": 1593413322000, "y": 1 }, - { "x": 1593413323000, "y": 0 }, - { "x": 1593413324000, "y": 0 }, - { "x": 1593413325000, "y": 0 }, - { "x": 1593413326000, "y": 0 }, - { "x": 1593413327000, "y": 0 }, - { "x": 1593413328000, "y": 0 }, - { "x": 1593413329000, "y": 0 }, - { "x": 1593413330000, "y": 0 }, - { "x": 1593413331000, "y": 0 }, - { "x": 1593413332000, "y": 0 }, - { "x": 1593413333000, "y": 0 }, - { "x": 1593413334000, "y": 0 }, - { "x": 1593413335000, "y": 0 }, - { "x": 1593413336000, "y": 0 }, - { "x": 1593413337000, "y": 0 }, - { "x": 1593413338000, "y": 0 }, - { "x": 1593413339000, "y": 0 }, - { "x": 1593413340000, "y": 0 } - ], - "avg": 24.75 - }, - { - "key": "HTTP 3xx", - "dataPoints": [ - { "x": 1593413100000, "y": 0 }, - { "x": 1593413101000, "y": 0 }, - { "x": 1593413102000, "y": 0 }, - { "x": 1593413103000, "y": 0 }, - { "x": 1593413104000, "y": 0 }, - { "x": 1593413105000, "y": 0 }, - { "x": 1593413106000, "y": 0 }, - { "x": 1593413107000, "y": 0 }, - { "x": 1593413108000, "y": 0 }, - { "x": 1593413109000, "y": 0 }, - { "x": 1593413110000, "y": 0 }, - { "x": 1593413111000, "y": 0 }, - { "x": 1593413112000, "y": 0 }, - { "x": 1593413113000, "y": 0 }, - { "x": 1593413114000, "y": 0 }, - { "x": 1593413115000, "y": 0 }, - { "x": 1593413116000, "y": 0 }, - { "x": 1593413117000, "y": 0 }, - { "x": 1593413118000, "y": 0 }, - { "x": 1593413119000, "y": 0 }, - { "x": 1593413120000, "y": 0 }, - { "x": 1593413121000, "y": 0 }, - { "x": 1593413122000, "y": 0 }, - { "x": 1593413123000, "y": 0 }, - { "x": 1593413124000, "y": 0 }, - { "x": 1593413125000, "y": 0 }, - { "x": 1593413126000, "y": 0 }, - { "x": 1593413127000, "y": 0 }, - { "x": 1593413128000, "y": 0 }, - { "x": 1593413129000, "y": 0 }, - { "x": 1593413130000, "y": 0 }, - { "x": 1593413131000, "y": 0 }, - { "x": 1593413132000, "y": 0 }, - { "x": 1593413133000, "y": 0 }, - { "x": 1593413134000, "y": 0 }, - { "x": 1593413135000, "y": 0 }, - { "x": 1593413136000, "y": 0 }, - { "x": 1593413137000, "y": 0 }, - { "x": 1593413138000, "y": 0 }, - { "x": 1593413139000, "y": 0 }, - { "x": 1593413140000, "y": 0 }, - { "x": 1593413141000, "y": 0 }, - { "x": 1593413142000, "y": 0 }, - { "x": 1593413143000, "y": 0 }, - { "x": 1593413144000, "y": 0 }, - { "x": 1593413145000, "y": 0 }, - { "x": 1593413146000, "y": 0 }, - { "x": 1593413147000, "y": 0 }, - { "x": 1593413148000, "y": 0 }, - { "x": 1593413149000, "y": 0 }, - { "x": 1593413150000, "y": 0 }, - { "x": 1593413151000, "y": 0 }, - { "x": 1593413152000, "y": 0 }, - { "x": 1593413153000, "y": 0 }, - { "x": 1593413154000, "y": 0 }, - { "x": 1593413155000, "y": 0 }, - { "x": 1593413156000, "y": 0 }, - { "x": 1593413157000, "y": 0 }, - { "x": 1593413158000, "y": 0 }, - { "x": 1593413159000, "y": 0 }, - { "x": 1593413160000, "y": 0 }, - { "x": 1593413161000, "y": 0 }, - { "x": 1593413162000, "y": 0 }, - { "x": 1593413163000, "y": 0 }, - { "x": 1593413164000, "y": 0 }, - { "x": 1593413165000, "y": 0 }, - { "x": 1593413166000, "y": 0 }, - { "x": 1593413167000, "y": 0 }, - { "x": 1593413168000, "y": 0 }, - { "x": 1593413169000, "y": 0 }, - { "x": 1593413170000, "y": 0 }, - { "x": 1593413171000, "y": 0 }, - { "x": 1593413172000, "y": 0 }, - { "x": 1593413173000, "y": 0 }, - { "x": 1593413174000, "y": 0 }, - { "x": 1593413175000, "y": 0 }, - { "x": 1593413176000, "y": 0 }, - { "x": 1593413177000, "y": 0 }, - { "x": 1593413178000, "y": 0 }, - { "x": 1593413179000, "y": 0 }, - { "x": 1593413180000, "y": 0 }, - { "x": 1593413181000, "y": 0 }, - { "x": 1593413182000, "y": 0 }, - { "x": 1593413183000, "y": 0 }, - { "x": 1593413184000, "y": 0 }, - { "x": 1593413185000, "y": 0 }, - { "x": 1593413186000, "y": 0 }, - { "x": 1593413187000, "y": 0 }, - { "x": 1593413188000, "y": 0 }, - { "x": 1593413189000, "y": 0 }, - { "x": 1593413190000, "y": 0 }, - { "x": 1593413191000, "y": 0 }, - { "x": 1593413192000, "y": 0 }, - { "x": 1593413193000, "y": 0 }, - { "x": 1593413194000, "y": 0 }, - { "x": 1593413195000, "y": 0 }, - { "x": 1593413196000, "y": 0 }, - { "x": 1593413197000, "y": 0 }, - { "x": 1593413198000, "y": 0 }, - { "x": 1593413199000, "y": 0 }, - { "x": 1593413200000, "y": 0 }, - { "x": 1593413201000, "y": 0 }, - { "x": 1593413202000, "y": 0 }, - { "x": 1593413203000, "y": 0 }, - { "x": 1593413204000, "y": 0 }, - { "x": 1593413205000, "y": 0 }, - { "x": 1593413206000, "y": 0 }, - { "x": 1593413207000, "y": 0 }, - { "x": 1593413208000, "y": 0 }, - { "x": 1593413209000, "y": 0 }, - { "x": 1593413210000, "y": 0 }, - { "x": 1593413211000, "y": 0 }, - { "x": 1593413212000, "y": 0 }, - { "x": 1593413213000, "y": 0 }, - { "x": 1593413214000, "y": 0 }, - { "x": 1593413215000, "y": 0 }, - { "x": 1593413216000, "y": 0 }, - { "x": 1593413217000, "y": 0 }, - { "x": 1593413218000, "y": 0 }, - { "x": 1593413219000, "y": 0 }, - { "x": 1593413220000, "y": 0 }, - { "x": 1593413221000, "y": 0 }, - { "x": 1593413222000, "y": 0 }, - { "x": 1593413223000, "y": 0 }, - { "x": 1593413224000, "y": 0 }, - { "x": 1593413225000, "y": 0 }, - { "x": 1593413226000, "y": 0 }, - { "x": 1593413227000, "y": 0 }, - { "x": 1593413228000, "y": 0 }, - { "x": 1593413229000, "y": 0 }, - { "x": 1593413230000, "y": 0 }, - { "x": 1593413231000, "y": 0 }, - { "x": 1593413232000, "y": 0 }, - { "x": 1593413233000, "y": 0 }, - { "x": 1593413234000, "y": 0 }, - { "x": 1593413235000, "y": 0 }, - { "x": 1593413236000, "y": 0 }, - { "x": 1593413237000, "y": 0 }, - { "x": 1593413238000, "y": 0 }, - { "x": 1593413239000, "y": 0 }, - { "x": 1593413240000, "y": 0 }, - { "x": 1593413241000, "y": 0 }, - { "x": 1593413242000, "y": 0 }, - { "x": 1593413243000, "y": 0 }, - { "x": 1593413244000, "y": 0 }, - { "x": 1593413245000, "y": 0 }, - { "x": 1593413246000, "y": 0 }, - { "x": 1593413247000, "y": 0 }, - { "x": 1593413248000, "y": 0 }, - { "x": 1593413249000, "y": 0 }, - { "x": 1593413250000, "y": 0 }, - { "x": 1593413251000, "y": 0 }, - { "x": 1593413252000, "y": 0 }, - { "x": 1593413253000, "y": 0 }, - { "x": 1593413254000, "y": 0 }, - { "x": 1593413255000, "y": 0 }, - { "x": 1593413256000, "y": 0 }, - { "x": 1593413257000, "y": 0 }, - { "x": 1593413258000, "y": 0 }, - { "x": 1593413259000, "y": 0 }, - { "x": 1593413260000, "y": 0 }, - { "x": 1593413261000, "y": 0 }, - { "x": 1593413262000, "y": 0 }, - { "x": 1593413263000, "y": 0 }, - { "x": 1593413264000, "y": 0 }, - { "x": 1593413265000, "y": 0 }, - { "x": 1593413266000, "y": 0 }, - { "x": 1593413267000, "y": 0 }, - { "x": 1593413268000, "y": 0 }, - { "x": 1593413269000, "y": 0 }, - { "x": 1593413270000, "y": 0 }, - { "x": 1593413271000, "y": 0 }, - { "x": 1593413272000, "y": 0 }, - { "x": 1593413273000, "y": 0 }, - { "x": 1593413274000, "y": 0 }, - { "x": 1593413275000, "y": 0 }, - { "x": 1593413276000, "y": 0 }, - { "x": 1593413277000, "y": 0 }, - { "x": 1593413278000, "y": 0 }, - { "x": 1593413279000, "y": 0 }, - { "x": 1593413280000, "y": 0 }, - { "x": 1593413281000, "y": 0 }, - { "x": 1593413282000, "y": 0 }, - { "x": 1593413283000, "y": 0 }, - { "x": 1593413284000, "y": 0 }, - { "x": 1593413285000, "y": 0 }, - { "x": 1593413286000, "y": 0 }, - { "x": 1593413287000, "y": 0 }, - { "x": 1593413288000, "y": 0 }, - { "x": 1593413289000, "y": 0 }, - { "x": 1593413290000, "y": 0 }, - { "x": 1593413291000, "y": 0 }, - { "x": 1593413292000, "y": 0 }, - { "x": 1593413293000, "y": 0 }, - { "x": 1593413294000, "y": 0 }, - { "x": 1593413295000, "y": 0 }, - { "x": 1593413296000, "y": 0 }, - { "x": 1593413297000, "y": 0 }, - { "x": 1593413298000, "y": 2 }, - { "x": 1593413299000, "y": 0 }, - { "x": 1593413300000, "y": 0 }, - { "x": 1593413301000, "y": 3 }, - { "x": 1593413302000, "y": 0 }, - { "x": 1593413303000, "y": 0 }, - { "x": 1593413304000, "y": 0 }, - { "x": 1593413305000, "y": 0 }, - { "x": 1593413306000, "y": 0 }, - { "x": 1593413307000, "y": 0 }, - { "x": 1593413308000, "y": 0 }, - { "x": 1593413309000, "y": 0 }, - { "x": 1593413310000, "y": 0 }, - { "x": 1593413311000, "y": 0 }, - { "x": 1593413312000, "y": 0 }, - { "x": 1593413313000, "y": 0 }, - { "x": 1593413314000, "y": 0 }, - { "x": 1593413315000, "y": 0 }, - { "x": 1593413316000, "y": 0 }, - { "x": 1593413317000, "y": 2 }, - { "x": 1593413318000, "y": 0 }, - { "x": 1593413319000, "y": 0 }, - { "x": 1593413320000, "y": 0 }, - { "x": 1593413321000, "y": 0 }, - { "x": 1593413322000, "y": 0 }, - { "x": 1593413323000, "y": 0 }, - { "x": 1593413324000, "y": 0 }, - { "x": 1593413325000, "y": 0 }, - { "x": 1593413326000, "y": 0 }, - { "x": 1593413327000, "y": 0 }, - { "x": 1593413328000, "y": 0 }, - { "x": 1593413329000, "y": 0 }, - { "x": 1593413330000, "y": 0 }, - { "x": 1593413331000, "y": 0 }, - { "x": 1593413332000, "y": 0 }, - { "x": 1593413333000, "y": 0 }, - { "x": 1593413334000, "y": 0 }, - { "x": 1593413335000, "y": 0 }, - { "x": 1593413336000, "y": 0 }, - { "x": 1593413337000, "y": 0 }, - { "x": 1593413338000, "y": 0 }, - { "x": 1593413339000, "y": 0 }, - { "x": 1593413340000, "y": 0 } - ], - "avg": 1.75 - }, - { - "key": "HTTP 4xx", - "dataPoints": [ - { "x": 1593413100000, "y": 0 }, - { "x": 1593413101000, "y": 0 }, - { "x": 1593413102000, "y": 0 }, - { "x": 1593413103000, "y": 0 }, - { "x": 1593413104000, "y": 0 }, - { "x": 1593413105000, "y": 0 }, - { "x": 1593413106000, "y": 0 }, - { "x": 1593413107000, "y": 0 }, - { "x": 1593413108000, "y": 0 }, - { "x": 1593413109000, "y": 0 }, - { "x": 1593413110000, "y": 0 }, - { "x": 1593413111000, "y": 0 }, - { "x": 1593413112000, "y": 0 }, - { "x": 1593413113000, "y": 0 }, - { "x": 1593413114000, "y": 0 }, - { "x": 1593413115000, "y": 0 }, - { "x": 1593413116000, "y": 0 }, - { "x": 1593413117000, "y": 0 }, - { "x": 1593413118000, "y": 0 }, - { "x": 1593413119000, "y": 0 }, - { "x": 1593413120000, "y": 0 }, - { "x": 1593413121000, "y": 0 }, - { "x": 1593413122000, "y": 0 }, - { "x": 1593413123000, "y": 0 }, - { "x": 1593413124000, "y": 0 }, - { "x": 1593413125000, "y": 0 }, - { "x": 1593413126000, "y": 0 }, - { "x": 1593413127000, "y": 0 }, - { "x": 1593413128000, "y": 0 }, - { "x": 1593413129000, "y": 0 }, - { "x": 1593413130000, "y": 0 }, - { "x": 1593413131000, "y": 0 }, - { "x": 1593413132000, "y": 0 }, - { "x": 1593413133000, "y": 0 }, - { "x": 1593413134000, "y": 0 }, - { "x": 1593413135000, "y": 0 }, - { "x": 1593413136000, "y": 0 }, - { "x": 1593413137000, "y": 0 }, - { "x": 1593413138000, "y": 0 }, - { "x": 1593413139000, "y": 0 }, - { "x": 1593413140000, "y": 0 }, - { "x": 1593413141000, "y": 0 }, - { "x": 1593413142000, "y": 0 }, - { "x": 1593413143000, "y": 0 }, - { "x": 1593413144000, "y": 0 }, - { "x": 1593413145000, "y": 0 }, - { "x": 1593413146000, "y": 0 }, - { "x": 1593413147000, "y": 0 }, - { "x": 1593413148000, "y": 0 }, - { "x": 1593413149000, "y": 0 }, - { "x": 1593413150000, "y": 0 }, - { "x": 1593413151000, "y": 0 }, - { "x": 1593413152000, "y": 0 }, - { "x": 1593413153000, "y": 0 }, - { "x": 1593413154000, "y": 0 }, - { "x": 1593413155000, "y": 0 }, - { "x": 1593413156000, "y": 0 }, - { "x": 1593413157000, "y": 0 }, - { "x": 1593413158000, "y": 0 }, - { "x": 1593413159000, "y": 0 }, - { "x": 1593413160000, "y": 0 }, - { "x": 1593413161000, "y": 0 }, - { "x": 1593413162000, "y": 0 }, - { "x": 1593413163000, "y": 0 }, - { "x": 1593413164000, "y": 0 }, - { "x": 1593413165000, "y": 0 }, - { "x": 1593413166000, "y": 0 }, - { "x": 1593413167000, "y": 0 }, - { "x": 1593413168000, "y": 0 }, - { "x": 1593413169000, "y": 0 }, - { "x": 1593413170000, "y": 0 }, - { "x": 1593413171000, "y": 0 }, - { "x": 1593413172000, "y": 0 }, - { "x": 1593413173000, "y": 0 }, - { "x": 1593413174000, "y": 0 }, - { "x": 1593413175000, "y": 0 }, - { "x": 1593413176000, "y": 0 }, - { "x": 1593413177000, "y": 0 }, - { "x": 1593413178000, "y": 0 }, - { "x": 1593413179000, "y": 0 }, - { "x": 1593413180000, "y": 0 }, - { "x": 1593413181000, "y": 0 }, - { "x": 1593413182000, "y": 0 }, - { "x": 1593413183000, "y": 0 }, - { "x": 1593413184000, "y": 0 }, - { "x": 1593413185000, "y": 0 }, - { "x": 1593413186000, "y": 0 }, - { "x": 1593413187000, "y": 0 }, - { "x": 1593413188000, "y": 0 }, - { "x": 1593413189000, "y": 0 }, - { "x": 1593413190000, "y": 0 }, - { "x": 1593413191000, "y": 0 }, - { "x": 1593413192000, "y": 0 }, - { "x": 1593413193000, "y": 0 }, - { "x": 1593413194000, "y": 0 }, - { "x": 1593413195000, "y": 0 }, - { "x": 1593413196000, "y": 0 }, - { "x": 1593413197000, "y": 0 }, - { "x": 1593413198000, "y": 0 }, - { "x": 1593413199000, "y": 0 }, - { "x": 1593413200000, "y": 0 }, - { "x": 1593413201000, "y": 0 }, - { "x": 1593413202000, "y": 0 }, - { "x": 1593413203000, "y": 0 }, - { "x": 1593413204000, "y": 0 }, - { "x": 1593413205000, "y": 0 }, - { "x": 1593413206000, "y": 0 }, - { "x": 1593413207000, "y": 0 }, - { "x": 1593413208000, "y": 0 }, - { "x": 1593413209000, "y": 0 }, - { "x": 1593413210000, "y": 0 }, - { "x": 1593413211000, "y": 0 }, - { "x": 1593413212000, "y": 0 }, - { "x": 1593413213000, "y": 0 }, - { "x": 1593413214000, "y": 0 }, - { "x": 1593413215000, "y": 0 }, - { "x": 1593413216000, "y": 0 }, - { "x": 1593413217000, "y": 0 }, - { "x": 1593413218000, "y": 0 }, - { "x": 1593413219000, "y": 0 }, - { "x": 1593413220000, "y": 0 }, - { "x": 1593413221000, "y": 0 }, - { "x": 1593413222000, "y": 0 }, - { "x": 1593413223000, "y": 0 }, - { "x": 1593413224000, "y": 0 }, - { "x": 1593413225000, "y": 0 }, - { "x": 1593413226000, "y": 0 }, - { "x": 1593413227000, "y": 0 }, - { "x": 1593413228000, "y": 0 }, - { "x": 1593413229000, "y": 0 }, - { "x": 1593413230000, "y": 0 }, - { "x": 1593413231000, "y": 0 }, - { "x": 1593413232000, "y": 0 }, - { "x": 1593413233000, "y": 0 }, - { "x": 1593413234000, "y": 0 }, - { "x": 1593413235000, "y": 0 }, - { "x": 1593413236000, "y": 0 }, - { "x": 1593413237000, "y": 0 }, - { "x": 1593413238000, "y": 0 }, - { "x": 1593413239000, "y": 0 }, - { "x": 1593413240000, "y": 0 }, - { "x": 1593413241000, "y": 0 }, - { "x": 1593413242000, "y": 0 }, - { "x": 1593413243000, "y": 0 }, - { "x": 1593413244000, "y": 0 }, - { "x": 1593413245000, "y": 0 }, - { "x": 1593413246000, "y": 0 }, - { "x": 1593413247000, "y": 0 }, - { "x": 1593413248000, "y": 0 }, - { "x": 1593413249000, "y": 0 }, - { "x": 1593413250000, "y": 0 }, - { "x": 1593413251000, "y": 0 }, - { "x": 1593413252000, "y": 0 }, - { "x": 1593413253000, "y": 0 }, - { "x": 1593413254000, "y": 0 }, - { "x": 1593413255000, "y": 0 }, - { "x": 1593413256000, "y": 0 }, - { "x": 1593413257000, "y": 0 }, - { "x": 1593413258000, "y": 0 }, - { "x": 1593413259000, "y": 0 }, - { "x": 1593413260000, "y": 0 }, - { "x": 1593413261000, "y": 0 }, - { "x": 1593413262000, "y": 0 }, - { "x": 1593413263000, "y": 0 }, - { "x": 1593413264000, "y": 0 }, - { "x": 1593413265000, "y": 0 }, - { "x": 1593413266000, "y": 0 }, - { "x": 1593413267000, "y": 0 }, - { "x": 1593413268000, "y": 0 }, - { "x": 1593413269000, "y": 0 }, - { "x": 1593413270000, "y": 0 }, - { "x": 1593413271000, "y": 0 }, - { "x": 1593413272000, "y": 0 }, - { "x": 1593413273000, "y": 0 }, - { "x": 1593413274000, "y": 0 }, - { "x": 1593413275000, "y": 0 }, - { "x": 1593413276000, "y": 0 }, - { "x": 1593413277000, "y": 0 }, - { "x": 1593413278000, "y": 0 }, - { "x": 1593413279000, "y": 0 }, - { "x": 1593413280000, "y": 0 }, - { "x": 1593413281000, "y": 0 }, - { "x": 1593413282000, "y": 0 }, - { "x": 1593413283000, "y": 0 }, - { "x": 1593413284000, "y": 0 }, - { "x": 1593413285000, "y": 0 }, - { "x": 1593413286000, "y": 0 }, - { "x": 1593413287000, "y": 0 }, - { "x": 1593413288000, "y": 0 }, - { "x": 1593413289000, "y": 1 }, - { "x": 1593413290000, "y": 0 }, - { "x": 1593413291000, "y": 0 }, - { "x": 1593413292000, "y": 1 }, - { "x": 1593413293000, "y": 0 }, - { "x": 1593413294000, "y": 0 }, - { "x": 1593413295000, "y": 0 }, - { "x": 1593413296000, "y": 0 }, - { "x": 1593413297000, "y": 0 }, - { "x": 1593413298000, "y": 0 }, - { "x": 1593413299000, "y": 0 }, - { "x": 1593413300000, "y": 1 }, - { "x": 1593413301000, "y": 0 }, - { "x": 1593413302000, "y": 0 }, - { "x": 1593413303000, "y": 0 }, - { "x": 1593413304000, "y": 0 }, - { "x": 1593413305000, "y": 1 }, - { "x": 1593413306000, "y": 0 }, - { "x": 1593413307000, "y": 0 }, - { "x": 1593413308000, "y": 0 }, - { "x": 1593413309000, "y": 1 }, - { "x": 1593413310000, "y": 1 }, - { "x": 1593413311000, "y": 0 }, - { "x": 1593413312000, "y": 0 }, - { "x": 1593413313000, "y": 0 }, - { "x": 1593413314000, "y": 0 }, - { "x": 1593413315000, "y": 1 }, - { "x": 1593413316000, "y": 0 }, - { "x": 1593413317000, "y": 0 }, - { "x": 1593413318000, "y": 0 }, - { "x": 1593413319000, "y": 0 }, - { "x": 1593413320000, "y": 1 }, - { "x": 1593413321000, "y": 0 }, - { "x": 1593413322000, "y": 0 }, - { "x": 1593413323000, "y": 0 }, - { "x": 1593413324000, "y": 0 }, - { "x": 1593413325000, "y": 0 }, - { "x": 1593413326000, "y": 0 }, - { "x": 1593413327000, "y": 0 }, - { "x": 1593413328000, "y": 0 }, - { "x": 1593413329000, "y": 0 }, - { "x": 1593413330000, "y": 0 }, - { "x": 1593413331000, "y": 0 }, - { "x": 1593413332000, "y": 0 }, - { "x": 1593413333000, "y": 0 }, - { "x": 1593413334000, "y": 0 }, - { "x": 1593413335000, "y": 0 }, - { "x": 1593413336000, "y": 0 }, - { "x": 1593413337000, "y": 0 }, - { "x": 1593413338000, "y": 0 }, - { "x": 1593413339000, "y": 0 }, - { "x": 1593413340000, "y": 0 } - ], - "avg": 2 - }, - { - "key": "HTTP 5xx", - "dataPoints": [ - { "x": 1593413100000, "y": 0 }, - { "x": 1593413101000, "y": 0 }, - { "x": 1593413102000, "y": 0 }, - { "x": 1593413103000, "y": 0 }, - { "x": 1593413104000, "y": 0 }, - { "x": 1593413105000, "y": 0 }, - { "x": 1593413106000, "y": 0 }, - { "x": 1593413107000, "y": 0 }, - { "x": 1593413108000, "y": 0 }, - { "x": 1593413109000, "y": 0 }, - { "x": 1593413110000, "y": 0 }, - { "x": 1593413111000, "y": 0 }, - { "x": 1593413112000, "y": 0 }, - { "x": 1593413113000, "y": 0 }, - { "x": 1593413114000, "y": 0 }, - { "x": 1593413115000, "y": 0 }, - { "x": 1593413116000, "y": 0 }, - { "x": 1593413117000, "y": 0 }, - { "x": 1593413118000, "y": 0 }, - { "x": 1593413119000, "y": 0 }, - { "x": 1593413120000, "y": 0 }, - { "x": 1593413121000, "y": 0 }, - { "x": 1593413122000, "y": 0 }, - { "x": 1593413123000, "y": 0 }, - { "x": 1593413124000, "y": 0 }, - { "x": 1593413125000, "y": 0 }, - { "x": 1593413126000, "y": 0 }, - { "x": 1593413127000, "y": 0 }, - { "x": 1593413128000, "y": 0 }, - { "x": 1593413129000, "y": 0 }, - { "x": 1593413130000, "y": 0 }, - { "x": 1593413131000, "y": 0 }, - { "x": 1593413132000, "y": 0 }, - { "x": 1593413133000, "y": 0 }, - { "x": 1593413134000, "y": 0 }, - { "x": 1593413135000, "y": 0 }, - { "x": 1593413136000, "y": 0 }, - { "x": 1593413137000, "y": 0 }, - { "x": 1593413138000, "y": 0 }, - { "x": 1593413139000, "y": 0 }, - { "x": 1593413140000, "y": 0 }, - { "x": 1593413141000, "y": 0 }, - { "x": 1593413142000, "y": 0 }, - { "x": 1593413143000, "y": 0 }, - { "x": 1593413144000, "y": 0 }, - { "x": 1593413145000, "y": 0 }, - { "x": 1593413146000, "y": 0 }, - { "x": 1593413147000, "y": 0 }, - { "x": 1593413148000, "y": 0 }, - { "x": 1593413149000, "y": 0 }, - { "x": 1593413150000, "y": 0 }, - { "x": 1593413151000, "y": 0 }, - { "x": 1593413152000, "y": 0 }, - { "x": 1593413153000, "y": 0 }, - { "x": 1593413154000, "y": 0 }, - { "x": 1593413155000, "y": 0 }, - { "x": 1593413156000, "y": 0 }, - { "x": 1593413157000, "y": 0 }, - { "x": 1593413158000, "y": 0 }, - { "x": 1593413159000, "y": 0 }, - { "x": 1593413160000, "y": 0 }, - { "x": 1593413161000, "y": 0 }, - { "x": 1593413162000, "y": 0 }, - { "x": 1593413163000, "y": 0 }, - { "x": 1593413164000, "y": 0 }, - { "x": 1593413165000, "y": 0 }, - { "x": 1593413166000, "y": 0 }, - { "x": 1593413167000, "y": 0 }, - { "x": 1593413168000, "y": 0 }, - { "x": 1593413169000, "y": 0 }, - { "x": 1593413170000, "y": 0 }, - { "x": 1593413171000, "y": 0 }, - { "x": 1593413172000, "y": 0 }, - { "x": 1593413173000, "y": 0 }, - { "x": 1593413174000, "y": 0 }, - { "x": 1593413175000, "y": 0 }, - { "x": 1593413176000, "y": 0 }, - { "x": 1593413177000, "y": 0 }, - { "x": 1593413178000, "y": 0 }, - { "x": 1593413179000, "y": 0 }, - { "x": 1593413180000, "y": 0 }, - { "x": 1593413181000, "y": 0 }, - { "x": 1593413182000, "y": 0 }, - { "x": 1593413183000, "y": 0 }, - { "x": 1593413184000, "y": 0 }, - { "x": 1593413185000, "y": 0 }, - { "x": 1593413186000, "y": 0 }, - { "x": 1593413187000, "y": 0 }, - { "x": 1593413188000, "y": 0 }, - { "x": 1593413189000, "y": 0 }, - { "x": 1593413190000, "y": 0 }, - { "x": 1593413191000, "y": 0 }, - { "x": 1593413192000, "y": 0 }, - { "x": 1593413193000, "y": 0 }, - { "x": 1593413194000, "y": 0 }, - { "x": 1593413195000, "y": 0 }, - { "x": 1593413196000, "y": 0 }, - { "x": 1593413197000, "y": 0 }, - { "x": 1593413198000, "y": 0 }, - { "x": 1593413199000, "y": 0 }, - { "x": 1593413200000, "y": 0 }, - { "x": 1593413201000, "y": 0 }, - { "x": 1593413202000, "y": 0 }, - { "x": 1593413203000, "y": 0 }, - { "x": 1593413204000, "y": 0 }, - { "x": 1593413205000, "y": 0 }, - { "x": 1593413206000, "y": 0 }, - { "x": 1593413207000, "y": 0 }, - { "x": 1593413208000, "y": 0 }, - { "x": 1593413209000, "y": 0 }, - { "x": 1593413210000, "y": 0 }, - { "x": 1593413211000, "y": 0 }, - { "x": 1593413212000, "y": 0 }, - { "x": 1593413213000, "y": 0 }, - { "x": 1593413214000, "y": 0 }, - { "x": 1593413215000, "y": 0 }, - { "x": 1593413216000, "y": 0 }, - { "x": 1593413217000, "y": 0 }, - { "x": 1593413218000, "y": 0 }, - { "x": 1593413219000, "y": 0 }, - { "x": 1593413220000, "y": 0 }, - { "x": 1593413221000, "y": 0 }, - { "x": 1593413222000, "y": 0 }, - { "x": 1593413223000, "y": 0 }, - { "x": 1593413224000, "y": 0 }, - { "x": 1593413225000, "y": 0 }, - { "x": 1593413226000, "y": 0 }, - { "x": 1593413227000, "y": 0 }, - { "x": 1593413228000, "y": 0 }, - { "x": 1593413229000, "y": 0 }, - { "x": 1593413230000, "y": 0 }, - { "x": 1593413231000, "y": 0 }, - { "x": 1593413232000, "y": 0 }, - { "x": 1593413233000, "y": 0 }, - { "x": 1593413234000, "y": 0 }, - { "x": 1593413235000, "y": 0 }, - { "x": 1593413236000, "y": 0 }, - { "x": 1593413237000, "y": 0 }, - { "x": 1593413238000, "y": 0 }, - { "x": 1593413239000, "y": 0 }, - { "x": 1593413240000, "y": 0 }, - { "x": 1593413241000, "y": 0 }, - { "x": 1593413242000, "y": 0 }, - { "x": 1593413243000, "y": 0 }, - { "x": 1593413244000, "y": 0 }, - { "x": 1593413245000, "y": 0 }, - { "x": 1593413246000, "y": 0 }, - { "x": 1593413247000, "y": 0 }, - { "x": 1593413248000, "y": 0 }, - { "x": 1593413249000, "y": 0 }, - { "x": 1593413250000, "y": 0 }, - { "x": 1593413251000, "y": 0 }, - { "x": 1593413252000, "y": 0 }, - { "x": 1593413253000, "y": 0 }, - { "x": 1593413254000, "y": 0 }, - { "x": 1593413255000, "y": 0 }, - { "x": 1593413256000, "y": 0 }, - { "x": 1593413257000, "y": 0 }, - { "x": 1593413258000, "y": 0 }, - { "x": 1593413259000, "y": 0 }, - { "x": 1593413260000, "y": 0 }, - { "x": 1593413261000, "y": 0 }, - { "x": 1593413262000, "y": 0 }, - { "x": 1593413263000, "y": 0 }, - { "x": 1593413264000, "y": 0 }, - { "x": 1593413265000, "y": 0 }, - { "x": 1593413266000, "y": 0 }, - { "x": 1593413267000, "y": 0 }, - { "x": 1593413268000, "y": 0 }, - { "x": 1593413269000, "y": 0 }, - { "x": 1593413270000, "y": 0 }, - { "x": 1593413271000, "y": 0 }, - { "x": 1593413272000, "y": 0 }, - { "x": 1593413273000, "y": 0 }, - { "x": 1593413274000, "y": 0 }, - { "x": 1593413275000, "y": 0 }, - { "x": 1593413276000, "y": 0 }, - { "x": 1593413277000, "y": 0 }, - { "x": 1593413278000, "y": 0 }, - { "x": 1593413279000, "y": 0 }, - { "x": 1593413280000, "y": 0 }, - { "x": 1593413281000, "y": 0 }, - { "x": 1593413282000, "y": 0 }, - { "x": 1593413283000, "y": 0 }, - { "x": 1593413284000, "y": 0 }, - { "x": 1593413285000, "y": 0 }, - { "x": 1593413286000, "y": 1 }, - { "x": 1593413287000, "y": 1 }, - { "x": 1593413288000, "y": 0 }, - { "x": 1593413289000, "y": 0 }, - { "x": 1593413290000, "y": 0 }, - { "x": 1593413291000, "y": 0 }, - { "x": 1593413292000, "y": 0 }, - { "x": 1593413293000, "y": 0 }, - { "x": 1593413294000, "y": 0 }, - { "x": 1593413295000, "y": 0 }, - { "x": 1593413296000, "y": 0 }, - { "x": 1593413297000, "y": 0 }, - { "x": 1593413298000, "y": 0 }, - { "x": 1593413299000, "y": 1 }, - { "x": 1593413300000, "y": 0 }, - { "x": 1593413301000, "y": 1 }, - { "x": 1593413302000, "y": 0 }, - { "x": 1593413303000, "y": 0 }, - { "x": 1593413304000, "y": 0 }, - { "x": 1593413305000, "y": 1 }, - { "x": 1593413306000, "y": 0 }, - { "x": 1593413307000, "y": 0 }, - { "x": 1593413308000, "y": 1 }, - { "x": 1593413309000, "y": 0 }, - { "x": 1593413310000, "y": 0 }, - { "x": 1593413311000, "y": 1 }, - { "x": 1593413312000, "y": 0 }, - { "x": 1593413313000, "y": 0 }, - { "x": 1593413314000, "y": 0 }, - { "x": 1593413315000, "y": 1 }, - { "x": 1593413316000, "y": 0 }, - { "x": 1593413317000, "y": 0 }, - { "x": 1593413318000, "y": 0 }, - { "x": 1593413319000, "y": 0 }, - { "x": 1593413320000, "y": 0 }, - { "x": 1593413321000, "y": 0 }, - { "x": 1593413322000, "y": 1 }, - { "x": 1593413323000, "y": 0 }, - { "x": 1593413324000, "y": 0 }, - { "x": 1593413325000, "y": 0 }, - { "x": 1593413326000, "y": 0 }, - { "x": 1593413327000, "y": 0 }, - { "x": 1593413328000, "y": 0 }, - { "x": 1593413329000, "y": 0 }, - { "x": 1593413330000, "y": 0 }, - { "x": 1593413331000, "y": 0 }, - { "x": 1593413332000, "y": 0 }, - { "x": 1593413333000, "y": 0 }, - { "x": 1593413334000, "y": 0 }, - { "x": 1593413335000, "y": 0 }, - { "x": 1593413336000, "y": 0 }, - { "x": 1593413337000, "y": 0 }, - { "x": 1593413338000, "y": 0 }, - { "x": 1593413339000, "y": 0 }, - { "x": 1593413340000, "y": 0 } - ], - "avg": 2.25 - }, - { - "key": "success", - "dataPoints": [ - { "x": 1593413100000, "y": 0 }, - { "x": 1593413101000, "y": 0 }, - { "x": 1593413102000, "y": 0 }, - { "x": 1593413103000, "y": 0 }, - { "x": 1593413104000, "y": 0 }, - { "x": 1593413105000, "y": 0 }, - { "x": 1593413106000, "y": 0 }, - { "x": 1593413107000, "y": 0 }, - { "x": 1593413108000, "y": 0 }, - { "x": 1593413109000, "y": 0 }, - { "x": 1593413110000, "y": 0 }, - { "x": 1593413111000, "y": 0 }, - { "x": 1593413112000, "y": 0 }, - { "x": 1593413113000, "y": 0 }, - { "x": 1593413114000, "y": 0 }, - { "x": 1593413115000, "y": 0 }, - { "x": 1593413116000, "y": 0 }, - { "x": 1593413117000, "y": 0 }, - { "x": 1593413118000, "y": 0 }, - { "x": 1593413119000, "y": 0 }, - { "x": 1593413120000, "y": 0 }, - { "x": 1593413121000, "y": 0 }, - { "x": 1593413122000, "y": 0 }, - { "x": 1593413123000, "y": 0 }, - { "x": 1593413124000, "y": 0 }, - { "x": 1593413125000, "y": 0 }, - { "x": 1593413126000, "y": 0 }, - { "x": 1593413127000, "y": 0 }, - { "x": 1593413128000, "y": 0 }, - { "x": 1593413129000, "y": 0 }, - { "x": 1593413130000, "y": 0 }, - { "x": 1593413131000, "y": 0 }, - { "x": 1593413132000, "y": 0 }, - { "x": 1593413133000, "y": 0 }, - { "x": 1593413134000, "y": 0 }, - { "x": 1593413135000, "y": 0 }, - { "x": 1593413136000, "y": 0 }, - { "x": 1593413137000, "y": 0 }, - { "x": 1593413138000, "y": 0 }, - { "x": 1593413139000, "y": 0 }, - { "x": 1593413140000, "y": 0 }, - { "x": 1593413141000, "y": 0 }, - { "x": 1593413142000, "y": 0 }, - { "x": 1593413143000, "y": 0 }, - { "x": 1593413144000, "y": 0 }, - { "x": 1593413145000, "y": 0 }, - { "x": 1593413146000, "y": 0 }, - { "x": 1593413147000, "y": 0 }, - { "x": 1593413148000, "y": 0 }, - { "x": 1593413149000, "y": 0 }, - { "x": 1593413150000, "y": 0 }, - { "x": 1593413151000, "y": 0 }, - { "x": 1593413152000, "y": 0 }, - { "x": 1593413153000, "y": 0 }, - { "x": 1593413154000, "y": 0 }, - { "x": 1593413155000, "y": 0 }, - { "x": 1593413156000, "y": 0 }, - { "x": 1593413157000, "y": 0 }, - { "x": 1593413158000, "y": 0 }, - { "x": 1593413159000, "y": 0 }, - { "x": 1593413160000, "y": 0 }, - { "x": 1593413161000, "y": 0 }, - { "x": 1593413162000, "y": 0 }, - { "x": 1593413163000, "y": 0 }, - { "x": 1593413164000, "y": 0 }, - { "x": 1593413165000, "y": 0 }, - { "x": 1593413166000, "y": 0 }, - { "x": 1593413167000, "y": 0 }, - { "x": 1593413168000, "y": 0 }, - { "x": 1593413169000, "y": 0 }, - { "x": 1593413170000, "y": 0 }, - { "x": 1593413171000, "y": 0 }, - { "x": 1593413172000, "y": 0 }, - { "x": 1593413173000, "y": 0 }, - { "x": 1593413174000, "y": 0 }, - { "x": 1593413175000, "y": 0 }, - { "x": 1593413176000, "y": 0 }, - { "x": 1593413177000, "y": 0 }, - { "x": 1593413178000, "y": 0 }, - { "x": 1593413179000, "y": 0 }, - { "x": 1593413180000, "y": 0 }, - { "x": 1593413181000, "y": 0 }, - { "x": 1593413182000, "y": 0 }, - { "x": 1593413183000, "y": 0 }, - { "x": 1593413184000, "y": 0 }, - { "x": 1593413185000, "y": 0 }, - { "x": 1593413186000, "y": 0 }, - { "x": 1593413187000, "y": 0 }, - { "x": 1593413188000, "y": 0 }, - { "x": 1593413189000, "y": 0 }, - { "x": 1593413190000, "y": 0 }, - { "x": 1593413191000, "y": 0 }, - { "x": 1593413192000, "y": 0 }, - { "x": 1593413193000, "y": 0 }, - { "x": 1593413194000, "y": 0 }, - { "x": 1593413195000, "y": 0 }, - { "x": 1593413196000, "y": 0 }, - { "x": 1593413197000, "y": 0 }, - { "x": 1593413198000, "y": 0 }, - { "x": 1593413199000, "y": 0 }, - { "x": 1593413200000, "y": 0 }, - { "x": 1593413201000, "y": 0 }, - { "x": 1593413202000, "y": 0 }, - { "x": 1593413203000, "y": 0 }, - { "x": 1593413204000, "y": 0 }, - { "x": 1593413205000, "y": 0 }, - { "x": 1593413206000, "y": 0 }, - { "x": 1593413207000, "y": 0 }, - { "x": 1593413208000, "y": 0 }, - { "x": 1593413209000, "y": 0 }, - { "x": 1593413210000, "y": 0 }, - { "x": 1593413211000, "y": 0 }, - { "x": 1593413212000, "y": 0 }, - { "x": 1593413213000, "y": 0 }, - { "x": 1593413214000, "y": 0 }, - { "x": 1593413215000, "y": 0 }, - { "x": 1593413216000, "y": 0 }, - { "x": 1593413217000, "y": 0 }, - { "x": 1593413218000, "y": 0 }, - { "x": 1593413219000, "y": 0 }, - { "x": 1593413220000, "y": 0 }, - { "x": 1593413221000, "y": 0 }, - { "x": 1593413222000, "y": 0 }, - { "x": 1593413223000, "y": 0 }, - { "x": 1593413224000, "y": 0 }, - { "x": 1593413225000, "y": 0 }, - { "x": 1593413226000, "y": 0 }, - { "x": 1593413227000, "y": 0 }, - { "x": 1593413228000, "y": 0 }, - { "x": 1593413229000, "y": 0 }, - { "x": 1593413230000, "y": 0 }, - { "x": 1593413231000, "y": 0 }, - { "x": 1593413232000, "y": 0 }, - { "x": 1593413233000, "y": 0 }, - { "x": 1593413234000, "y": 0 }, - { "x": 1593413235000, "y": 0 }, - { "x": 1593413236000, "y": 0 }, - { "x": 1593413237000, "y": 0 }, - { "x": 1593413238000, "y": 0 }, - { "x": 1593413239000, "y": 0 }, - { "x": 1593413240000, "y": 0 }, - { "x": 1593413241000, "y": 0 }, - { "x": 1593413242000, "y": 0 }, - { "x": 1593413243000, "y": 0 }, - { "x": 1593413244000, "y": 0 }, - { "x": 1593413245000, "y": 0 }, - { "x": 1593413246000, "y": 0 }, - { "x": 1593413247000, "y": 0 }, - { "x": 1593413248000, "y": 0 }, - { "x": 1593413249000, "y": 0 }, - { "x": 1593413250000, "y": 0 }, - { "x": 1593413251000, "y": 0 }, - { "x": 1593413252000, "y": 0 }, - { "x": 1593413253000, "y": 0 }, - { "x": 1593413254000, "y": 0 }, - { "x": 1593413255000, "y": 0 }, - { "x": 1593413256000, "y": 0 }, - { "x": 1593413257000, "y": 0 }, - { "x": 1593413258000, "y": 0 }, - { "x": 1593413259000, "y": 0 }, - { "x": 1593413260000, "y": 0 }, - { "x": 1593413261000, "y": 0 }, - { "x": 1593413262000, "y": 0 }, - { "x": 1593413263000, "y": 0 }, - { "x": 1593413264000, "y": 0 }, - { "x": 1593413265000, "y": 0 }, - { "x": 1593413266000, "y": 0 }, - { "x": 1593413267000, "y": 0 }, - { "x": 1593413268000, "y": 0 }, - { "x": 1593413269000, "y": 0 }, - { "x": 1593413270000, "y": 0 }, - { "x": 1593413271000, "y": 0 }, - { "x": 1593413272000, "y": 0 }, - { "x": 1593413273000, "y": 0 }, - { "x": 1593413274000, "y": 0 }, - { "x": 1593413275000, "y": 0 }, - { "x": 1593413276000, "y": 0 }, - { "x": 1593413277000, "y": 0 }, - { "x": 1593413278000, "y": 0 }, - { "x": 1593413279000, "y": 0 }, - { "x": 1593413280000, "y": 0 }, - { "x": 1593413281000, "y": 0 }, - { "x": 1593413282000, "y": 0 }, - { "x": 1593413283000, "y": 0 }, - { "x": 1593413284000, "y": 0 }, - { "x": 1593413285000, "y": 0 }, - { "x": 1593413286000, "y": 0 }, - { "x": 1593413287000, "y": 0 }, - { "x": 1593413288000, "y": 0 }, - { "x": 1593413289000, "y": 0 }, - { "x": 1593413290000, "y": 0 }, - { "x": 1593413291000, "y": 0 }, - { "x": 1593413292000, "y": 0 }, - { "x": 1593413293000, "y": 0 }, - { "x": 1593413294000, "y": 0 }, - { "x": 1593413295000, "y": 0 }, - { "x": 1593413296000, "y": 0 }, - { "x": 1593413297000, "y": 0 }, - { "x": 1593413298000, "y": 0 }, - { "x": 1593413299000, "y": 0 }, - { "x": 1593413300000, "y": 0 }, - { "x": 1593413301000, "y": 0 }, - { "x": 1593413302000, "y": 0 }, - { "x": 1593413303000, "y": 0 }, - { "x": 1593413304000, "y": 0 }, - { "x": 1593413305000, "y": 0 }, - { "x": 1593413306000, "y": 0 }, - { "x": 1593413307000, "y": 0 }, - { "x": 1593413308000, "y": 0 }, - { "x": 1593413309000, "y": 1 }, - { "x": 1593413310000, "y": 0 }, - { "x": 1593413311000, "y": 0 }, - { "x": 1593413312000, "y": 0 }, - { "x": 1593413313000, "y": 0 }, - { "x": 1593413314000, "y": 0 }, - { "x": 1593413315000, "y": 0 }, - { "x": 1593413316000, "y": 0 }, - { "x": 1593413317000, "y": 0 }, - { "x": 1593413318000, "y": 0 }, - { "x": 1593413319000, "y": 0 }, - { "x": 1593413320000, "y": 0 }, - { "x": 1593413321000, "y": 0 }, - { "x": 1593413322000, "y": 0 }, - { "x": 1593413323000, "y": 0 }, - { "x": 1593413324000, "y": 0 }, - { "x": 1593413325000, "y": 0 }, - { "x": 1593413326000, "y": 0 }, - { "x": 1593413327000, "y": 0 }, - { "x": 1593413328000, "y": 0 }, - { "x": 1593413329000, "y": 0 }, - { "x": 1593413330000, "y": 0 }, - { "x": 1593413331000, "y": 0 }, - { "x": 1593413332000, "y": 0 }, - { "x": 1593413333000, "y": 0 }, - { "x": 1593413334000, "y": 0 }, - { "x": 1593413335000, "y": 0 }, - { "x": 1593413336000, "y": 0 }, - { "x": 1593413337000, "y": 0 }, - { "x": 1593413338000, "y": 0 }, - { "x": 1593413339000, "y": 0 }, - { "x": 1593413340000, "y": 0 } - ], - "avg": 0.25 - } - ], - "overallAvgDuration": 38682.52419354839 - } -} diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts index 94559a3e4aa5..cebf27ecdff2 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/top_transaction_groups.ts @@ -5,8 +5,8 @@ */ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import expectedTransactionGroups from './expectation/top_transaction_groups.json'; function sortTransactionGroups(items: any[]) { return sortBy(items, 'impact'); @@ -34,7 +34,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql({ items: [], isAggregationAccurate: true, bucketSize: 1000 }); + expectSnapshot(response.body).toMatchInline(` + Object { + "bucketSize": 1000, + "isAggregationAccurate": true, + "items": Array [], + } + `); }); }); @@ -53,13 +59,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the correct number of buckets', async () => { - expect(response.body.items.length).to.be(18); + expectSnapshot(response.body.items.length).toMatchInline(`18`); }); it('returns the correct buckets (when ignoring samples)', async () => { - expect(omitSampleFromTransactionGroups(response.body.items)).to.eql( - omitSampleFromTransactionGroups(expectedTransactionGroups.items) - ); + expectSnapshot(omitSampleFromTransactionGroups(response.body.items)).toMatch(); }); it('returns the correct buckets and samples', async () => { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts index 68a7499a2389..a8418fe2860a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import expectedTransactionCharts from './expectation/transaction_charts.json'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -24,17 +24,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql({ - apmTimeseries: { - overallAvgDuration: null, - responseTimes: { - avg: [], - p95: [], - p99: [], + expectSnapshot(response.body).toMatchInline(` + Object { + "apmTimeseries": Object { + "overallAvgDuration": null, + "responseTimes": Object { + "avg": Array [], + "p95": Array [], + "p99": Array [], + }, + "tpmBuckets": Array [], }, - tpmBuckets: [], - }, - }); + } + `); }); }); @@ -48,7 +50,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(response.status).to.be(200); - expect(response.body).to.eql(expectedTransactionCharts); + expectSnapshot(response.body).toMatch(); }); }); }); diff --git a/x-pack/test/apm_api_integration/common/archives_metadata.ts b/x-pack/test/apm_api_integration/common/archives_metadata.ts index 38dde685dd3f..ab9e3a191e24 100644 --- a/x-pack/test/apm_api_integration/common/archives_metadata.ts +++ b/x-pack/test/apm_api_integration/common/archives_metadata.ts @@ -6,7 +6,7 @@ export default { 'apm_8.0.0': { - start: '2020-09-10T08:07:13.274Z', - end: '2020-09-10T08:37:13.274Z', + start: '2020-09-10T06:00:00.000Z', + end: '2020-09-10T07:00:00.000Z', }, }; diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_8.0.0/data.json.gz b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_8.0.0/data.json.gz index 34b673790ec0..fa40458dedaf 100644 Binary files a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_8.0.0/data.json.gz and b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/apm_8.0.0/data.json.gz differ diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/data.json.gz b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/data.json.gz deleted file mode 100644 index d23b9e51d734..000000000000 Binary files a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/data.json.gz and /dev/null differ diff --git a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/mappings.json b/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/mappings.json deleted file mode 100644 index 5a5d48464b8b..000000000000 --- a/x-pack/test/apm_api_integration/common/fixtures/es_archiver/ml_8.0.0/mappings.json +++ /dev/null @@ -1,63452 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".ml-annotations-read": { - "is_hidden": true - }, - ".ml-annotations-write": { - "is_hidden": true - } - }, - "index": ".ml-annotations-6", - "mappings": { - "_meta": { - "version": "8.0.0" - }, - "properties": { - "annotation": { - "type": "text" - }, - "by_field_name": { - "type": "keyword" - }, - "by_field_value": { - "type": "keyword" - }, - "create_time": { - "type": "date" - }, - "create_username": { - "type": "keyword" - }, - "detector_index": { - "type": "integer" - }, - "end_timestamp": { - "type": "date" - }, - "event": { - "type": "keyword" - }, - "job_id": { - "type": "keyword" - }, - "modified_time": { - "type": "date" - }, - "modified_username": { - "type": "keyword" - }, - "over_field_name": { - "type": "keyword" - }, - "over_field_value": { - "type": "keyword" - }, - "partition_field_name": { - "type": "keyword" - }, - "partition_field_value": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "hidden": "true", - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".ml-anomalies-.write-android_homepage_high_latency_by_geo": { - "is_hidden": true - }, - ".ml-anomalies-.write-apm-environment_not_defined-7ed6-high_mean_transaction_duration": { - "is_hidden": true - }, - ".ml-anomalies-.write-apm-production-229a-high_mean_transaction_duration": { - "is_hidden": true - }, - ".ml-anomalies-.write-apm-testing-d457-high_mean_transaction_duration": { - "is_hidden": true - }, - ".ml-anomalies-.write-auto_http_0x73c4bc9426fb6908_high_latency_by_geo": { - "is_hidden": true - }, - ".ml-anomalies-.write-kibana-logs-ui-default-default-log-entry-categories-count": { - "is_hidden": true - }, - ".ml-anomalies-.write-kibana-logs-ui-default-default-log-entry-rate": { - "is_hidden": true - }, - ".ml-anomalies-android_homepage_high_latency_by_geo": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "android_homepage_high_latency_by_geo" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-apm-environment_not_defined-7ed6-high_mean_transaction_duration": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "apm-environment_not_defined-7ed6-high_mean_transaction_duration" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-apm-production-229a-high_mean_transaction_duration": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "apm-production-229a-high_mean_transaction_duration" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-apm-testing-d457-high_mean_transaction_duration": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "apm-testing-d457-high_mean_transaction_duration" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-auto_http_0x73c4bc9426fb6908_high_latency_by_geo": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "auto_http_0x73c4bc9426fb6908_high_latency_by_geo" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-kibana-logs-ui-default-default-log-entry-categories-count": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "kibana-logs-ui-default-default-log-entry-categories-count" - } - } - }, - "is_hidden": true - }, - ".ml-anomalies-kibana-logs-ui-default-default-log-entry-rate": { - "filter": { - "term": { - "job_id": { - "boost": 1, - "value": "kibana-logs-ui-default-default-log-entry-rate" - } - } - }, - "is_hidden": true - } - }, - "index": ".ml-anomalies-shared", - "mappings": { - "_meta": { - "version": "8.0.0" - }, - "dynamic_templates": [ - { - "strings_as_keywords": { - "mapping": { - "type": "keyword" - }, - "match": "*" - } - } - ], - "properties": { - "actual": { - "type": "double" - }, - "all_field_values": { - "analyzer": "whitespace", - "type": "text" - }, - "anomaly_score": { - "type": "double" - }, - "average_bucket_processing_time_ms": { - "type": "double" - }, - "bucket_allocation_failures_count": { - "type": "long" - }, - "bucket_count": { - "type": "long" - }, - "bucket_influencers": { - "properties": { - "anomaly_score": { - "type": "double" - }, - "bucket_span": { - "type": "long" - }, - "influencer_field_name": { - "type": "keyword" - }, - "initial_anomaly_score": { - "type": "double" - }, - "is_interim": { - "type": "boolean" - }, - "job_id": { - "type": "keyword" - }, - "probability": { - "type": "double" - }, - "raw_anomaly_score": { - "type": "double" - }, - "result_type": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - } - }, - "type": "nested" - }, - "bucket_span": { - "type": "long" - }, - "by_field_name": { - "type": "keyword" - }, - "by_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "categorization_status": { - "type": "keyword" - }, - "categorized_doc_count": { - "type": "keyword" - }, - "category_id": { - "type": "long" - }, - "causes": { - "properties": { - "actual": { - "type": "double" - }, - "by_field_name": { - "type": "keyword" - }, - "by_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "correlated_by_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "field_name": { - "type": "keyword" - }, - "function": { - "type": "keyword" - }, - "function_description": { - "type": "keyword" - }, - "geo_results": { - "properties": { - "actual_point": { - "type": "geo_point" - }, - "typical_point": { - "type": "geo_point" - } - } - }, - "over_field_name": { - "type": "keyword" - }, - "over_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "partition_field_name": { - "type": "keyword" - }, - "partition_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "probability": { - "type": "double" - }, - "typical": { - "type": "double" - } - }, - "type": "nested" - }, - "dead_category_count": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "detector_index": { - "type": "integer" - }, - "earliest_record_timestamp": { - "type": "date" - }, - "empty_bucket_count": { - "type": "long" - }, - "event": { - "properties": { - "dataset": { - "type": "keyword" - } - } - }, - "event_count": { - "type": "long" - }, - "examples": { - "type": "text" - }, - "exponential_average_bucket_processing_time_ms": { - "type": "double" - }, - "exponential_average_calculation_context": { - "properties": { - "incremental_metric_value_ms": { - "type": "double" - }, - "latest_timestamp": { - "type": "date" - }, - "previous_exponential_average_ms": { - "type": "double" - } - } - }, - "failed_category_count": { - "type": "keyword" - }, - "field_name": { - "type": "keyword" - }, - "forecast_create_timestamp": { - "type": "date" - }, - "forecast_end_timestamp": { - "type": "date" - }, - "forecast_expiry_timestamp": { - "type": "date" - }, - "forecast_id": { - "type": "keyword" - }, - "forecast_lower": { - "type": "double" - }, - "forecast_memory_bytes": { - "type": "long" - }, - "forecast_messages": { - "type": "keyword" - }, - "forecast_prediction": { - "type": "double" - }, - "forecast_progress": { - "type": "double" - }, - "forecast_start_timestamp": { - "type": "date" - }, - "forecast_status": { - "type": "keyword" - }, - "forecast_upper": { - "type": "double" - }, - "frequent_category_count": { - "type": "keyword" - }, - "function": { - "type": "keyword" - }, - "function_description": { - "type": "keyword" - }, - "geo_results": { - "properties": { - "actual_point": { - "type": "geo_point" - }, - "typical_point": { - "type": "geo_point" - } - } - }, - "influencer_field_name": { - "type": "keyword" - }, - "influencer_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "influencer_score": { - "type": "double" - }, - "influencers": { - "properties": { - "influencer_field_name": { - "type": "keyword" - }, - "influencer_field_values": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - } - }, - "type": "nested" - }, - "initial_anomaly_score": { - "type": "double" - }, - "initial_influencer_score": { - "type": "double" - }, - "initial_record_score": { - "type": "double" - }, - "input_bytes": { - "type": "long" - }, - "input_field_count": { - "type": "long" - }, - "input_record_count": { - "type": "long" - }, - "invalid_date_count": { - "type": "long" - }, - "is_interim": { - "type": "boolean" - }, - "job_id": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "last_data_time": { - "type": "date" - }, - "latest_empty_bucket_timestamp": { - "type": "date" - }, - "latest_record_time_stamp": { - "type": "date" - }, - "latest_record_timestamp": { - "type": "date" - }, - "latest_result_time_stamp": { - "type": "date" - }, - "latest_sparse_bucket_timestamp": { - "type": "date" - }, - "log_time": { - "type": "date" - }, - "max_matching_length": { - "type": "long" - }, - "maximum_bucket_processing_time_ms": { - "type": "double" - }, - "memory_status": { - "type": "keyword" - }, - "min_version": { - "type": "keyword" - }, - "minimum_bucket_processing_time_ms": { - "type": "double" - }, - "missing_field_count": { - "type": "long" - }, - "mlcategory": { - "type": "keyword" - }, - "model_bytes": { - "type": "long" - }, - "model_bytes_exceeded": { - "type": "keyword" - }, - "model_bytes_memory_limit": { - "type": "keyword" - }, - "model_feature": { - "type": "keyword" - }, - "model_lower": { - "type": "double" - }, - "model_median": { - "type": "double" - }, - "model_size_stats": { - "properties": { - "bucket_allocation_failures_count": { - "type": "long" - }, - "categorization_status": { - "type": "keyword" - }, - "categorized_doc_count": { - "type": "keyword" - }, - "dead_category_count": { - "type": "keyword" - }, - "failed_category_count": { - "type": "keyword" - }, - "frequent_category_count": { - "type": "keyword" - }, - "job_id": { - "type": "keyword" - }, - "log_time": { - "type": "date" - }, - "memory_status": { - "type": "keyword" - }, - "model_bytes": { - "type": "long" - }, - "model_bytes_exceeded": { - "type": "keyword" - }, - "model_bytes_memory_limit": { - "type": "keyword" - }, - "peak_model_bytes": { - "type": "long" - }, - "rare_category_count": { - "type": "keyword" - }, - "result_type": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "total_by_field_count": { - "type": "long" - }, - "total_category_count": { - "type": "keyword" - }, - "total_over_field_count": { - "type": "long" - }, - "total_partition_field_count": { - "type": "long" - } - } - }, - "model_upper": { - "type": "double" - }, - "monitor": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - } - } - }, - "multi_bucket_impact": { - "type": "double" - }, - "num_matches": { - "type": "long" - }, - "observer": { - "properties": { - "geo": { - "properties": { - "name": { - "type": "keyword" - } - } - } - } - }, - "out_of_order_timestamp_count": { - "type": "long" - }, - "over_field_name": { - "type": "keyword" - }, - "over_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "partition_field_name": { - "type": "keyword" - }, - "partition_field_value": { - "copy_to": [ - "all_field_values" - ], - "type": "keyword" - }, - "peak_model_bytes": { - "type": "keyword" - }, - "preferred_to_categories": { - "type": "long" - }, - "probability": { - "type": "double" - }, - "processed_field_count": { - "type": "long" - }, - "processed_record_count": { - "type": "long" - }, - "processing_time_ms": { - "type": "long" - }, - "quantiles": { - "enabled": false, - "type": "object" - }, - "rare_category_count": { - "type": "keyword" - }, - "raw_anomaly_score": { - "type": "double" - }, - "record_score": { - "type": "double" - }, - "regex": { - "type": "keyword" - }, - "result_type": { - "type": "keyword" - }, - "retain": { - "type": "boolean" - }, - "scheduled_events": { - "type": "keyword" - }, - "search_count": { - "type": "long" - }, - "service": { - "properties": { - "name": { - "type": "keyword" - } - } - }, - "snapshot_doc_count": { - "type": "integer" - }, - "snapshot_id": { - "type": "keyword" - }, - "sparse_bucket_count": { - "type": "long" - }, - "terms": { - "type": "text" - }, - "timestamp": { - "type": "date" - }, - "total_by_field_count": { - "type": "long" - }, - "total_category_count": { - "type": "keyword" - }, - "total_over_field_count": { - "type": "long" - }, - "total_partition_field_count": { - "type": "long" - }, - "total_search_time_ms": { - "type": "double" - }, - "transaction": { - "properties": { - "type": { - "type": "keyword" - } - } - }, - "typical": { - "type": "double" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "hidden": "true", - "number_of_replicas": "1", - "number_of_shards": "1", - "translog": { - "durability": "async" - } - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - }, - "index": ".ml-config", - "mappings": { - "_meta": { - "version": "8.0.0" - }, - "dynamic_templates": [ - { - "strings_as_keywords": { - "mapping": { - "type": "keyword" - }, - "match": "*" - } - } - ], - "properties": { - "aggregations": { - "enabled": false, - "type": "object" - }, - "allow_lazy_open": { - "type": "keyword" - }, - "analysis": { - "properties": { - "classification": { - "properties": { - "class_assignment_objective": { - "type": "keyword" - }, - "dependent_variable": { - "type": "keyword" - }, - "eta": { - "type": "double" - }, - "feature_bag_fraction": { - "type": "double" - }, - "gamma": { - "type": "double" - }, - "lambda": { - "type": "double" - }, - "max_trees": { - "type": "integer" - }, - "num_top_classes": { - "type": "integer" - }, - "num_top_feature_importance_values": { - "type": "integer" - }, - "prediction_field_name": { - "type": "keyword" - }, - "training_percent": { - "type": "double" - } - } - }, - "outlier_detection": { - "properties": { - "feature_influence_threshold": { - "type": "double" - }, - "method": { - "type": "keyword" - }, - "n_neighbors": { - "type": "integer" - } - } - }, - "regression": { - "properties": { - "dependent_variable": { - "type": "keyword" - }, - "eta": { - "type": "double" - }, - "feature_bag_fraction": { - "type": "double" - }, - "gamma": { - "type": "double" - }, - "lambda": { - "type": "double" - }, - "loss_function": { - "type": "keyword" - }, - "loss_function_parameter": { - "type": "double" - }, - "max_trees": { - "type": "integer" - }, - "num_top_feature_importance_values": { - "type": "integer" - }, - "prediction_field_name": { - "type": "keyword" - }, - "training_percent": { - "type": "double" - } - } - } - } - }, - "analysis_config": { - "properties": { - "bucket_span": { - "type": "keyword" - }, - "categorization_analyzer": { - "enabled": false, - "type": "object" - }, - "categorization_field_name": { - "type": "keyword" - }, - "categorization_filters": { - "type": "keyword" - }, - "detectors": { - "properties": { - "by_field_name": { - "type": "keyword" - }, - "custom_rules": { - "properties": { - "actions": { - "type": "keyword" - }, - "conditions": { - "properties": { - "applies_to": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "double" - } - }, - "type": "nested" - }, - "scope": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "detector_description": { - "type": "text" - }, - "detector_index": { - "type": "integer" - }, - "exclude_frequent": { - "type": "keyword" - }, - "field_name": { - "type": "keyword" - }, - "function": { - "type": "keyword" - }, - "over_field_name": { - "type": "keyword" - }, - "partition_field_name": { - "type": "keyword" - }, - "use_null": { - "type": "boolean" - } - } - }, - "influencers": { - "type": "keyword" - }, - "latency": { - "type": "keyword" - }, - "multivariate_by_fields": { - "type": "boolean" - }, - "per_partition_categorization": { - "properties": { - "enabled": { - "type": "boolean" - }, - "stop_on_warn": { - "type": "boolean" - } - } - }, - "summary_count_field_name": { - "type": "keyword" - } - } - }, - "analysis_limits": { - "properties": { - "categorization_examples_limit": { - "type": "long" - }, - "model_memory_limit": { - "type": "keyword" - } - } - }, - "analyzed_fields": { - "enabled": false, - "type": "object" - }, - "background_persist_interval": { - "type": "keyword" - }, - "chunking_config": { - "properties": { - "mode": { - "type": "keyword" - }, - "time_span": { - "type": "keyword" - } - } - }, - "config_type": { - "type": "keyword" - }, - "create_time": { - "type": "date" - }, - "custom_settings": { - "enabled": false, - "type": "object" - }, - "daily_model_snapshot_retention_after_days": { - "type": "long" - }, - "data_description": { - "properties": { - "field_delimiter": { - "type": "keyword" - }, - "format": { - "type": "keyword" - }, - "quote_character": { - "type": "keyword" - }, - "time_field": { - "type": "keyword" - }, - "time_format": { - "type": "keyword" - } - } - }, - "datafeed_id": { - "type": "keyword" - }, - "delayed_data_check_config": { - "properties": { - "check_window": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - } - } - }, - "deleting": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "dest": { - "properties": { - "index": { - "type": "keyword" - }, - "results_field": { - "type": "keyword" - } - } - }, - "finished_time": { - "type": "date" - }, - "frequency": { - "type": "keyword" - }, - "groups": { - "type": "keyword" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "id": { - "type": "keyword" - }, - "indices": { - "type": "keyword" - }, - "indices_options": { - "enabled": false, - "type": "object" - }, - "job_id": { - "type": "keyword" - }, - "job_type": { - "type": "keyword" - }, - "job_version": { - "type": "keyword" - }, - "max_empty_searches": { - "type": "keyword" - }, - "max_num_threads": { - "type": "integer" - }, - "model_plot_config": { - "properties": { - "annotations_enabled": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "terms": { - "type": "keyword" - } - } - }, - "model_snapshot_id": { - "type": "keyword" - }, - "model_snapshot_min_version": { - "type": "keyword" - }, - "model_snapshot_retention_days": { - "type": "long" - }, - "query": { - "enabled": false, - "type": "object" - }, - "query_delay": { - "type": "keyword" - }, - "renormalization_window_days": { - "type": "long" - }, - "results_index_name": { - "type": "keyword" - }, - "results_retention_days": { - "type": "long" - }, - "script_fields": { - "enabled": false, - "type": "object" - }, - "scroll_size": { - "type": "long" - }, - "source": { - "properties": { - "_source": { - "enabled": false, - "type": "object" - }, - "index": { - "type": "keyword" - }, - "query": { - "enabled": false, - "type": "object" - } - } - }, - "version": { - "type": "keyword" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "max_result_window": "10000", - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".ml-state-write": { - "is_hidden": true - } - }, - "index": ".ml-state-000001", - "mappings": { - "_meta": { - "version": "8000099" - }, - "enabled": false - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "hidden": "true", - "lifecycle": { - "name": "ml-size-based-ilm-policy", - "rollover_alias": ".ml-state-write" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-error": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-error-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-error" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-error": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-error-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-error" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-error": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-error-2020.07.31-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-error" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-error": { - "is_write_index": true - } - }, - "index": "apm-8.0.0-error-2020.07.31-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-error" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-metric": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-metric-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "golang": { - "properties": { - "goroutines": { - "type": "long" - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "active": { - "type": "float" - }, - "allocated": { - "type": "float" - }, - "frees": { - "type": "float" - }, - "idle": { - "type": "float" - }, - "mallocs": { - "type": "float" - }, - "objects": { - "type": "long" - }, - "total": { - "type": "float" - } - } - }, - "gc": { - "properties": { - "cpu_fraction": { - "type": "float" - }, - "next_gc_limit": { - "type": "float" - }, - "total_count": { - "type": "long" - }, - "total_pause": { - "properties": { - "ns": { - "type": "float" - } - } - } - } - }, - "system": { - "properties": { - "obtained": { - "type": "float" - }, - "released": { - "type": "float" - }, - "stack": { - "type": "long" - }, - "total": { - "type": "float" - } - } - } - } - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "alloc": { - "type": "float" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "used": { - "type": "float" - } - } - }, - "non_heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "long" - }, - "used": { - "type": "float" - } - } - } - } - }, - "thread": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "env": { - "type": "keyword" - }, - "hostname": { - "type": "keyword" - }, - "name": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "nodejs": { - "properties": { - "eventloop": { - "properties": { - "delay": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "ns": { - "type": "long" - } - } - } - } - }, - "handles": { - "properties": { - "active": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "allocated": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - }, - "requests": { - "properties": { - "active": { - "type": "long" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ruby": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - } - } - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "total": { - "type": "float" - } - } - }, - "slots": { - "properties": { - "free": { - "type": "long" - }, - "live": { - "type": "long" - } - } - } - } - }, - "threads": { - "type": "long" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-metric" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-metric": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-metric-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "golang": { - "properties": { - "goroutines": { - "type": "long" - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "active": { - "type": "float" - }, - "allocated": { - "type": "float" - }, - "frees": { - "type": "long" - }, - "idle": { - "type": "float" - }, - "mallocs": { - "type": "long" - }, - "objects": { - "type": "long" - }, - "total": { - "type": "float" - } - } - }, - "gc": { - "properties": { - "cpu_fraction": { - "type": "float" - }, - "next_gc_limit": { - "type": "float" - }, - "total_count": { - "type": "long" - }, - "total_pause": { - "properties": { - "ns": { - "type": "float" - } - } - } - } - }, - "system": { - "properties": { - "obtained": { - "type": "float" - }, - "released": { - "type": "float" - }, - "stack": { - "type": "long" - }, - "total": { - "type": "float" - } - } - } - } - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "alloc": { - "type": "float" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "used": { - "type": "float" - } - } - }, - "non_heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "long" - }, - "used": { - "type": "float" - } - } - } - } - }, - "thread": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "env": { - "type": "keyword" - }, - "hostname": { - "type": "keyword" - }, - "name": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "nodejs": { - "properties": { - "eventloop": { - "properties": { - "delay": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "ns": { - "type": "long" - } - } - } - } - }, - "handles": { - "properties": { - "active": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "arrayBuffers": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "external": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "heap": { - "properties": { - "allocated": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - }, - "requests": { - "properties": { - "active": { - "type": "long" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ruby": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - } - } - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "total": { - "type": "float" - } - } - }, - "slots": { - "properties": { - "free": { - "type": "long" - }, - "live": { - "type": "long" - } - } - } - } - }, - "threads": { - "type": "long" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-metric" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-metric": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-metric-2020.07.31-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "clr": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - }, - "gen0size": { - "type": "float" - }, - "gen1size": { - "type": "float" - }, - "gen2size": { - "type": "float" - }, - "gen3size": { - "type": "float" - } - } - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "golang": { - "properties": { - "goroutines": { - "type": "long" - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "active": { - "type": "float" - }, - "allocated": { - "type": "float" - }, - "frees": { - "type": "long" - }, - "idle": { - "type": "float" - }, - "mallocs": { - "type": "long" - }, - "objects": { - "type": "long" - }, - "total": { - "type": "float" - } - } - }, - "gc": { - "properties": { - "cpu_fraction": { - "type": "float" - }, - "next_gc_limit": { - "type": "float" - }, - "total_count": { - "type": "long" - }, - "total_pause": { - "properties": { - "ns": { - "type": "long" - } - } - } - } - }, - "system": { - "properties": { - "obtained": { - "type": "float" - }, - "released": { - "type": "float" - }, - "stack": { - "type": "long" - }, - "total": { - "type": "float" - } - } - } - } - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "alloc": { - "type": "float" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "pool": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "used": { - "type": "long" - } - } - }, - "used": { - "type": "float" - } - } - }, - "non_heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "long" - }, - "used": { - "type": "float" - } - } - } - } - }, - "thread": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "env": { - "type": "keyword" - }, - "hostname": { - "type": "keyword" - }, - "name": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "nodejs": { - "properties": { - "eventloop": { - "properties": { - "delay": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "ns": { - "type": "long" - } - } - } - } - }, - "handles": { - "properties": { - "active": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "arrayBuffers": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "external": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "heap": { - "properties": { - "allocated": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - }, - "requests": { - "properties": { - "active": { - "type": "long" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ruby": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - } - } - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "total": { - "type": "long" - } - } - }, - "slots": { - "properties": { - "free": { - "type": "long" - }, - "live": { - "type": "long" - } - } - } - } - }, - "threads": { - "type": "long" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cgroup": { - "properties": { - "memory": { - "properties": { - "mem": { - "properties": { - "limit": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - }, - "stats": { - "properties": { - "inactive_file": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-metric" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-metric": { - "is_write_index": true - } - }, - "index": "apm-8.0.0-metric-2020.07.31-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "clr": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - }, - "gen0size": { - "type": "float" - }, - "gen1size": { - "type": "float" - }, - "gen2size": { - "type": "float" - }, - "gen3size": { - "type": "float" - } - } - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "golang": { - "properties": { - "goroutines": { - "type": "long" - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "active": { - "type": "float" - }, - "allocated": { - "type": "float" - }, - "frees": { - "type": "float" - }, - "idle": { - "type": "float" - }, - "mallocs": { - "type": "float" - }, - "objects": { - "type": "long" - }, - "total": { - "type": "float" - } - } - }, - "gc": { - "properties": { - "cpu_fraction": { - "type": "float" - }, - "next_gc_limit": { - "type": "float" - }, - "total_count": { - "type": "long" - }, - "total_pause": { - "properties": { - "ns": { - "type": "float" - } - } - } - } - }, - "system": { - "properties": { - "obtained": { - "type": "float" - }, - "released": { - "type": "float" - }, - "stack": { - "type": "long" - }, - "total": { - "type": "float" - } - } - } - } - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jvm": { - "properties": { - "gc": { - "properties": { - "alloc": { - "type": "float" - }, - "count": { - "type": "long" - }, - "time": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "pool": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "float" - }, - "used": { - "type": "float" - } - } - }, - "used": { - "type": "float" - } - } - }, - "non_heap": { - "properties": { - "committed": { - "type": "float" - }, - "max": { - "type": "long" - }, - "used": { - "type": "float" - } - } - } - } - }, - "thread": { - "properties": { - "count": { - "type": "long" - } - } - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "env": { - "type": "keyword" - }, - "hostname": { - "type": "keyword" - }, - "name": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "nodejs": { - "properties": { - "eventloop": { - "properties": { - "delay": { - "properties": { - "avg": { - "properties": { - "ms": { - "type": "float" - } - } - }, - "ns": { - "type": "long" - } - } - } - } - }, - "handles": { - "properties": { - "active": { - "type": "long" - } - } - }, - "memory": { - "properties": { - "arrayBuffers": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "external": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "heap": { - "properties": { - "allocated": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "used": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - }, - "requests": { - "properties": { - "active": { - "type": "long" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ruby": { - "properties": { - "gc": { - "properties": { - "count": { - "type": "long" - } - } - }, - "heap": { - "properties": { - "allocations": { - "properties": { - "total": { - "type": "float" - } - } - }, - "slots": { - "properties": { - "free": { - "type": "long" - }, - "live": { - "type": "long" - } - } - } - } - }, - "threads": { - "type": "long" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cgroup": { - "properties": { - "memory": { - "properties": { - "mem": { - "properties": { - "limit": { - "properties": { - "bytes": { - "type": "float" - } - } - }, - "usage": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - }, - "stats": { - "properties": { - "inactive_file": { - "properties": { - "bytes": { - "type": "float" - } - } - } - } - } - } - } - } - }, - "cpu": { - "properties": { - "system": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - }, - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - }, - "user": { - "properties": { - "norm": { - "properties": { - "pct": { - "type": "float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-metric" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-span": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-span-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "foo": { - "type": "keyword" - }, - "productId": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-span" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-span": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-span-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "foo": { - "type": "keyword" - }, - "productId": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-span" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-span": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-span-2020.07.31-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "foo": { - "type": "keyword" - }, - "productId": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-span" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-span": { - "is_write_index": true - } - }, - "index": "apm-8.0.0-span-2020.07.31-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "foo": { - "type": "keyword" - }, - "productId": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-span" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-transaction": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-transaction-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_email": { - "type": "keyword" - }, - "customer_name": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "served_from_cache": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - }, - "agent": { - "properties": { - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "firstContentfulPaint": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "largestContentfulPaint": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "timeToFirstByte": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - }, - "navigationTiming": { - "properties": { - "connectEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "connectStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domLoading": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "fetchStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "requestStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-transaction" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-transaction": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-transaction-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_email": { - "type": "keyword" - }, - "customer_name": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - }, - "served_from_cache": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - }, - "agent": { - "properties": { - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "firstContentfulPaint": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "largestContentfulPaint": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "timeToFirstByte": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - }, - "navigationTiming": { - "properties": { - "connectEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "connectStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domLoading": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "fetchStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "requestStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "false", - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-transaction" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "0", - "number_of_shards": "1", - "priority": "50", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-transaction": { - "is_write_index": false - } - }, - "index": "apm-8.0.0-transaction-2020.07.31-000001", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_email": { - "type": "keyword" - }, - "customer_name": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - }, - "served_from_cache": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - }, - "agent": { - "properties": { - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "timeToFirstByte": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - }, - "navigationTiming": { - "properties": { - "connectEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "connectStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domLoading": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "fetchStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "requestStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "indexing_complete": "true", - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-transaction" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "apm-8.0.0-transaction": { - "is_write_index": true - } - }, - "index": "apm-8.0.0-transaction-2020.07.31-000002", - "mappings": { - "_meta": { - "beat": "apm", - "version": "8.0.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "network.inner": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "network.inner.*" - } - }, - { - "observer.egress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.egress.*" - } - }, - { - "observer.ingress": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "observer.ingress.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "labels_string": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "labels_boolean": { - "mapping": { - "type": "boolean" - }, - "match_mapping_type": "boolean", - "path_match": "labels.*" - } - }, - { - "labels_*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "labels.*" - } - }, - { - "transaction.marks": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "transaction.marks.*" - } - }, - { - "transaction.marks.*.*": { - "mapping": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "path_match": "transaction.marks.*.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "dynamic": "false", - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "path": "agent.name", - "type": "alias" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "child": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "client": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "dynamic": "false", - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "dynamic": "false", - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "culprit": { - "ignore_above": 1024, - "type": "keyword" - }, - "exception": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "handled": { - "type": "boolean" - }, - "message": { - "norms": false, - "type": "text" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "grouping_key": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "param_message": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "experimental": { - "dynamic": "true", - "type": "object" - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mime_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "dynamic": "false", - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "dynamic": "false", - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "finished": { - "type": "boolean" - }, - "headers": { - "enabled": false, - "type": "object" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "dynamic": "false", - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "dynamic": "true", - "properties": { - "company": { - "type": "keyword" - }, - "customer_email": { - "type": "keyword" - }, - "customer_name": { - "type": "keyword" - }, - "customer_tier": { - "type": "keyword" - }, - "foo": { - "type": "keyword" - }, - "lorem": { - "type": "keyword" - }, - "multi-line": { - "type": "keyword" - }, - "request_id": { - "type": "keyword" - }, - "served_from_cache": { - "type": "keyword" - }, - "this-is-a-very-long-tag-name-without-any-spaces": { - "type": "keyword" - } - } - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "observer": { - "dynamic": "false", - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "zone": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "listening": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_major": { - "type": "byte" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "parent": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "dynamic": "false", - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pe": { - "properties": { - "company": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "file_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "original_file_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "processor": { - "properties": { - "event": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "profile": { - "dynamic": "false", - "properties": { - "alloc_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "alloc_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "cpu": { - "properties": { - "ns": { - "type": "long" - } - } - }, - "duration": { - "type": "long" - }, - "inuse_objects": { - "properties": { - "count": { - "type": "long" - } - } - }, - "inuse_space": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "samples": { - "properties": { - "count": { - "type": "long" - } - } - }, - "stack": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - }, - "top": { - "dynamic": "false", - "properties": { - "filename": { - "ignore_above": 1024, - "type": "keyword" - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "line": { - "type": "long" - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "dynamic": "false", - "properties": { - "environment": { - "ignore_above": 1024, - "type": "keyword" - }, - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "framework": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "language": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "dynamic": "false", - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "sourcemap": { - "dynamic": "false", - "properties": { - "bundle_filepath": { - "ignore_above": 1024, - "type": "keyword" - }, - "service": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "span": { - "dynamic": "false", - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "db": { - "dynamic": "false", - "properties": { - "link": { - "ignore_above": 1024, - "type": "keyword" - }, - "rows_affected": { - "type": "long" - } - } - }, - "destination": { - "dynamic": "false", - "properties": { - "service": { - "dynamic": "false", - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "resource": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "duration": { - "properties": { - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "start": { - "properties": { - "us": { - "type": "long" - } - } - }, - "subtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "sync": { - "type": "boolean" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "system": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "actual": { - "properties": { - "free": { - "type": "long" - } - } - }, - "total": { - "type": "long" - } - } - }, - "process": { - "properties": { - "cpu": { - "properties": { - "total": { - "properties": { - "norm": { - "properties": { - "pct": { - "scaling_factor": 1000, - "type": "scaled_float" - } - } - } - } - } - } - }, - "memory": { - "properties": { - "rss": { - "properties": { - "bytes": { - "type": "long" - } - } - }, - "size": { - "type": "long" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timestamp": { - "properties": { - "us": { - "type": "long" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "dynamic": "false", - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "transaction": { - "dynamic": "false", - "properties": { - "breakdown": { - "properties": { - "count": { - "type": "long" - } - } - }, - "duration": { - "properties": { - "count": { - "type": "long" - }, - "histogram": { - "type": "histogram" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - }, - "us": { - "type": "long" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "marks": { - "dynamic": "true", - "properties": { - "*": { - "properties": { - "*": { - "dynamic": "true", - "type": "object" - } - } - }, - "agent": { - "properties": { - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "timeToFirstByte": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - }, - "navigationTiming": { - "properties": { - "connectEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "connectStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domComplete": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domContentLoadedEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domInteractive": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domLoading": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "domainLookupStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "fetchStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "loadEventStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "requestStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseEnd": { - "scaling_factor": 1000000, - "type": "scaled_float" - }, - "responseStart": { - "scaling_factor": 1000000, - "type": "scaled_float" - } - } - } - } - }, - "message": { - "dynamic": "false", - "properties": { - "age": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "queue": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "root": { - "type": "boolean" - }, - "sampled": { - "type": "boolean" - }, - "self_time": { - "properties": { - "count": { - "type": "long" - }, - "sum": { - "properties": { - "us": { - "type": "long" - } - } - } - } - }, - "span_count": { - "properties": { - "dropped": { - "type": "long" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "dynamic": "false", - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "dynamic": "false", - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "view spans": { - "ignore_above": 1024, - "type": "keyword" - }, - "vlan": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "codec": "best_compression", - "lifecycle": { - "name": "apm-rollover-30-days", - "rollover_alias": "apm-8.0.0-transaction" - }, - "mapping": { - "total_fields": { - "limit": "2000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "priority": "100", - "refresh_interval": "5s" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/apm_api_integration/common/match_snapshot.ts b/x-pack/test/apm_api_integration/common/match_snapshot.ts new file mode 100644 index 000000000000..a8cb0418583a --- /dev/null +++ b/x-pack/test/apm_api_integration/common/match_snapshot.ts @@ -0,0 +1,205 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SnapshotState, toMatchSnapshot, toMatchInlineSnapshot } from 'jest-snapshot'; +import path from 'path'; +import expect from '@kbn/expect'; +// @ts-expect-error +import prettier from 'prettier'; +// @ts-expect-error +import babelTraverse from '@babel/traverse'; +import { Suite, Test } from 'mocha'; + +type ISnapshotState = InstanceType; + +interface SnapshotContext { + snapshotState: ISnapshotState; + currentTestName: string; +} + +let testContext: { + file: string; + snapshotTitle: string; + snapshotContext: SnapshotContext; +} | null = null; + +let registered: boolean = false; + +function getSnapshotMeta(currentTest: Test) { + // Make sure snapshot title is unique per-file, rather than entire + // suite. This allows reuse of tests, for instance to compare + // results for different configurations. + + const titles = [currentTest.title]; + const file = currentTest.file; + + let test: Suite | undefined = currentTest?.parent; + + while (test && test.file === file) { + titles.push(test.title); + test = test.parent; + } + + const snapshotTitle = titles.reverse().join(' '); + + if (!file || !snapshotTitle) { + throw new Error(`file or snapshotTitle not available in Mocha test context`); + } + + return { + file, + snapshotTitle, + }; +} + +export function registerMochaHooksForSnapshots() { + let snapshotStatesByFilePath: Record< + string, + { snapshotState: ISnapshotState; testsInFile: Test[] } + > = {}; + + registered = true; + + beforeEach(function () { + const currentTest = this.currentTest!; + + const { file, snapshotTitle } = getSnapshotMeta(currentTest); + + if (!snapshotStatesByFilePath[file]) { + snapshotStatesByFilePath[file] = getSnapshotState(file, currentTest); + } + + testContext = { + file, + snapshotTitle, + snapshotContext: { + snapshotState: snapshotStatesByFilePath[file].snapshotState, + currentTestName: snapshotTitle, + }, + }; + }); + + afterEach(function () { + testContext = null; + }); + + after(function () { + // save snapshot after tests complete + + const unused: string[] = []; + + const isUpdatingSnapshots = process.env.UPDATE_SNAPSHOTS; + + Object.keys(snapshotStatesByFilePath).forEach((file) => { + const { snapshotState, testsInFile } = snapshotStatesByFilePath[file]; + + testsInFile.forEach((test) => { + const snapshotMeta = getSnapshotMeta(test); + // If test is failed or skipped, mark snapshots as used. Otherwise, + // running a test in isolation will generate false positives. + if (!test.isPassed()) { + snapshotState.markSnapshotsAsCheckedForTest(snapshotMeta.snapshotTitle); + } + }); + + if (!isUpdatingSnapshots) { + unused.push(...snapshotState.getUncheckedKeys()); + } else { + snapshotState.removeUncheckedKeys(); + } + + snapshotState.save(); + }); + + if (unused.length) { + throw new Error( + `${unused.length} obsolete snapshot(s) found:\n${unused.join( + '\n\t' + )}.\n\nRun tests again with \`UPDATE_SNAPSHOTS=1\` to remove them.` + ); + } + + snapshotStatesByFilePath = {}; + + registered = false; + }); +} + +const originalPrepareStackTrace = Error.prepareStackTrace; + +// jest-snapshot uses a stack trace to determine which file/line/column +// an inline snapshot should be written to. We filter out match_snapshot +// from the stack trace to prevent it from wanting to write to this file. + +Error.prepareStackTrace = (error, structuredStackTrace) => { + const filteredStrackTrace = structuredStackTrace.filter((callSite) => { + return !callSite.getFileName()?.endsWith('match_snapshot.ts'); + }); + if (originalPrepareStackTrace) { + return originalPrepareStackTrace(error, filteredStrackTrace); + } +}; + +function getSnapshotState(file: string, test: Test) { + const dirname = path.dirname(file); + const filename = path.basename(file); + + let parent = test.parent; + const testsInFile: Test[] = []; + + while (parent) { + testsInFile.push(...parent.tests); + parent = parent.parent; + } + + const snapshotState = new SnapshotState( + path.join(dirname + `/__snapshots__/` + filename.replace(path.extname(filename), '.snap')), + { + updateSnapshot: process.env.UPDATE_SNAPSHOTS ? 'all' : 'new', + getPrettier: () => prettier, + getBabelTraverse: () => babelTraverse, + } + ); + + return { snapshotState, testsInFile }; +} + +export function expectSnapshot(received: any) { + if (!registered) { + throw new Error( + 'Mocha hooks were not registered before expectSnapshot was used. Call `registerMochaHooksForSnapshots` in your top-level describe().' + ); + } + + if (!testContext) { + throw new Error('A current Mocha context is needed to match snapshots'); + } + + return { + toMatch: expectToMatchSnapshot.bind(null, testContext.snapshotContext, received), + // use bind to support optional 3rd argument (actual) + toMatchInline: expectToMatchInlineSnapshot.bind(null, testContext.snapshotContext, received), + }; +} + +function expectToMatchSnapshot(snapshotContext: SnapshotContext, received: any) { + const matcher = toMatchSnapshot.bind(snapshotContext as any); + const result = matcher(received); + + expect(result.pass).to.eql(true, result.message()); +} + +function expectToMatchInlineSnapshot( + snapshotContext: SnapshotContext, + received: any, + _actual?: any +) { + const matcher = toMatchInlineSnapshot.bind(snapshotContext as any); + + const result = arguments.length === 2 ? matcher(received) : matcher(received, _actual); + + expect(result.pass).to.eql(true, result.message()); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index 1b3b5602445e..c5ca086b5f37 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -5,14 +5,18 @@ */ import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { registerMochaHooksForSnapshots } from '../../common/match_snapshot'; export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { describe('APM specs (trial)', function () { this.tags('ciGroup1'); + registerMochaHooksForSnapshots(); + describe('Services', function () { loadTestFile(require.resolve('./services/annotations')); loadTestFile(require.resolve('./services/rum_services.ts')); + loadTestFile(require.resolve('./services/top_services.ts')); }); describe('Settings', function () { diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts index aadff2c31a17..f799d80f6ef1 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts @@ -6,6 +6,8 @@ import querystring from 'querystring'; import expect from '@kbn/expect'; +import { isEmpty } from 'lodash'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { @@ -21,7 +23,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) ); expect(response.status).to.be(200); - expect(response.body).to.eql({ elements: [] }); + expect(response.body.elements.length).to.be(0); }); }); @@ -36,227 +38,229 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) expect(response.status).to.be(200); - expect(response.body).to.eql({ - elements: [ - { - data: { - source: 'client', - target: 'opbeans-node', - id: 'client~opbeans-node', - sourceData: { - id: 'client', - 'service.name': 'client', - 'agent.name': 'rum-js', - }, - targetData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', + expectSnapshot(response.body).toMatchInline(` + Object { + "elements": Array [ + Object { + "data": Object { + "id": "client~opbeans-node", + "source": "client", + "sourceData": Object { + "agent.name": "rum-js", + "id": "client", + "service.name": "client", + }, + "target": "opbeans-node", + "targetData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, }, }, - }, - { - data: { - source: 'opbeans-java', - target: '>opbeans-java:3000', - id: 'opbeans-java~>opbeans-java:3000', - sourceData: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', - }, - targetData: { - 'span.subtype': 'http', - 'span.destination.service.resource': 'opbeans-java:3000', - 'span.type': 'external', - id: '>opbeans-java:3000', - label: 'opbeans-java:3000', + Object { + "data": Object { + "id": "opbeans-java~>opbeans-java:3000", + "source": "opbeans-java", + "sourceData": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + }, + "target": ">opbeans-java:3000", + "targetData": Object { + "id": ">opbeans-java:3000", + "label": "opbeans-java:3000", + "span.destination.service.resource": "opbeans-java:3000", + "span.subtype": "http", + "span.type": "external", + }, }, }, - }, - { - data: { - source: 'opbeans-java', - target: '>postgresql', - id: 'opbeans-java~>postgresql', - sourceData: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', - }, - targetData: { - 'span.subtype': 'postgresql', - 'span.destination.service.resource': 'postgresql', - 'span.type': 'db', - id: '>postgresql', - label: 'postgresql', + Object { + "data": Object { + "id": "opbeans-java~>postgresql", + "source": "opbeans-java", + "sourceData": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + }, + "target": ">postgresql", + "targetData": Object { + "id": ">postgresql", + "label": "postgresql", + "span.destination.service.resource": "postgresql", + "span.subtype": "postgresql", + "span.type": "db", + }, }, }, - }, - { - data: { - source: 'opbeans-java', - target: 'opbeans-node', - id: 'opbeans-java~opbeans-node', - sourceData: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', - }, - targetData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', + Object { + "data": Object { + "bidirectional": true, + "id": "opbeans-java~opbeans-node", + "source": "opbeans-java", + "sourceData": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + }, + "target": "opbeans-node", + "targetData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, }, - bidirectional: true, }, - }, - { - data: { - source: 'opbeans-node', - target: '>93.184.216.34:80', - id: 'opbeans-node~>93.184.216.34:80', - sourceData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', - }, - targetData: { - 'span.subtype': 'http', - 'span.destination.service.resource': '93.184.216.34:80', - 'span.type': 'external', - id: '>93.184.216.34:80', - label: '93.184.216.34:80', + Object { + "data": Object { + "id": "opbeans-node~>93.184.216.34:80", + "source": "opbeans-node", + "sourceData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, + "target": ">93.184.216.34:80", + "targetData": Object { + "id": ">93.184.216.34:80", + "label": "93.184.216.34:80", + "span.destination.service.resource": "93.184.216.34:80", + "span.subtype": "http", + "span.type": "external", + }, }, }, - }, - { - data: { - source: 'opbeans-node', - target: '>postgresql', - id: 'opbeans-node~>postgresql', - sourceData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', - }, - targetData: { - 'span.subtype': 'postgresql', - 'span.destination.service.resource': 'postgresql', - 'span.type': 'db', - id: '>postgresql', - label: 'postgresql', + Object { + "data": Object { + "id": "opbeans-node~>postgresql", + "source": "opbeans-node", + "sourceData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, + "target": ">postgresql", + "targetData": Object { + "id": ">postgresql", + "label": "postgresql", + "span.destination.service.resource": "postgresql", + "span.subtype": "postgresql", + "span.type": "db", + }, }, }, - }, - { - data: { - source: 'opbeans-node', - target: '>redis', - id: 'opbeans-node~>redis', - sourceData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', - }, - targetData: { - 'span.subtype': 'redis', - 'span.destination.service.resource': 'redis', - 'span.type': 'cache', - id: '>redis', - label: 'redis', + Object { + "data": Object { + "id": "opbeans-node~>redis", + "source": "opbeans-node", + "sourceData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, + "target": ">redis", + "targetData": Object { + "id": ">redis", + "label": "redis", + "span.destination.service.resource": "redis", + "span.subtype": "redis", + "span.type": "cache", + }, }, }, - }, - { - data: { - source: 'opbeans-node', - target: 'opbeans-java', - id: 'opbeans-node~opbeans-java', - sourceData: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', - }, - targetData: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', + Object { + "data": Object { + "id": "opbeans-node~opbeans-java", + "isInverseEdge": true, + "source": "opbeans-node", + "sourceData": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, + "target": "opbeans-java", + "targetData": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + }, }, - isInverseEdge: true, }, - }, - { - data: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', + Object { + "data": Object { + "agent.name": "java", + "id": "opbeans-java", + "service.environment": "production", + "service.name": "opbeans-java", + }, }, - }, - { - data: { - id: 'opbeans-node', - 'service.environment': 'production', - 'service.name': 'opbeans-node', - 'agent.name': 'nodejs', + Object { + "data": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "production", + "service.name": "opbeans-node", + }, }, - }, - { - data: { - 'span.subtype': 'http', - 'span.destination.service.resource': 'opbeans-java:3000', - 'span.type': 'external', - id: '>opbeans-java:3000', - label: 'opbeans-java:3000', + Object { + "data": Object { + "id": ">opbeans-java:3000", + "label": "opbeans-java:3000", + "span.destination.service.resource": "opbeans-java:3000", + "span.subtype": "http", + "span.type": "external", + }, }, - }, - { - data: { - id: 'client', - 'service.name': 'client', - 'agent.name': 'rum-js', + Object { + "data": Object { + "agent.name": "rum-js", + "id": "client", + "service.name": "client", + }, }, - }, - { - data: { - 'span.subtype': 'redis', - 'span.destination.service.resource': 'redis', - 'span.type': 'cache', - id: '>redis', - label: 'redis', + Object { + "data": Object { + "id": ">redis", + "label": "redis", + "span.destination.service.resource": "redis", + "span.subtype": "redis", + "span.type": "cache", + }, }, - }, - { - data: { - 'span.subtype': 'postgresql', - 'span.destination.service.resource': 'postgresql', - 'span.type': 'db', - id: '>postgresql', - label: 'postgresql', + Object { + "data": Object { + "id": ">postgresql", + "label": "postgresql", + "span.destination.service.resource": "postgresql", + "span.subtype": "postgresql", + "span.type": "db", + }, }, - }, - { - data: { - 'span.subtype': 'http', - 'span.destination.service.resource': '93.184.216.34:80', - 'span.type': 'external', - id: '>93.184.216.34:80', - label: '93.184.216.34:80', + Object { + "data": Object { + "id": ">93.184.216.34:80", + "label": "93.184.216.34:80", + "span.destination.service.resource": "93.184.216.34:80", + "span.subtype": "http", + "span.type": "external", + }, }, - }, - ], - }); + ], + } + `); }); }); }); @@ -287,34 +291,25 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); describe('when there is data with anomalies', () => { - before(() => esArchiver.load('ml_8.0.0')); - after(() => esArchiver.unload('ml_8.0.0')); + before(() => esArchiver.load('apm_8.0.0')); + after(() => esArchiver.unload('apm_8.0.0')); it('returns service map elements', async () => { - const response = await supertest.get( - '/api/apm/service-map?start=2020-08-26T11%3A00%3A00.000Z&end=2020-08-26T11%3A30%3A00.000Z' - ); + const start = encodeURIComponent('2020-09-10T06:00:00.000Z'); + const end = encodeURIComponent('2020-09-10T07:00:00.000Z'); + + const response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); expect(response.status).to.be(200); - const opbeansJavaWithAnomaly = response.body.elements.filter( - (el: { data: { id: string } }) => el.data.id === 'opbeans-java' + const dataWithAnomalies = response.body.elements.filter( + (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) ); - expect(opbeansJavaWithAnomaly).to.eql([ - { - data: { - id: 'opbeans-java', - 'service.environment': 'production', - 'service.name': 'opbeans-java', - 'agent.name': 'java', - serviceAnomalyStats: { - transactionType: 'request', - anomalyScore: 0.21359169006333134, - actualValue: 1526662.1320754716, - jobId: 'apm-production-229a-high_mean_transaction_duration', - }, - }, - }, - ]); + expect(dataWithAnomalies).to.not.empty(); + dataWithAnomalies.forEach(({ data }: any) => { + expect( + Object.values(data.serviceAnomalyStats).filter((value) => isEmpty(value)) + ).to.not.empty(); + }); }); }); }); diff --git a/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts index 78171a65a11f..088488bc143f 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/rum_services.ts @@ -5,6 +5,7 @@ */ import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { @@ -40,7 +41,12 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) expect(response.status).to.be(200); - expect(response.body).to.eql(['client', 'opbean-client-rum']); + expectSnapshot(response.body).toMatchInline(` + Array [ + "client", + "opbean-client-rum", + ] + `); }); }); }); diff --git a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts new file mode 100644 index 000000000000..76af02ec1606 --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; +import { PromiseReturnType } from '../../../../../plugins/apm/typings/common'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../../common/archives_metadata'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + const archiveName = 'apm_8.0.0'; + + const range = archives_metadata[archiveName]; + + // url parameters + const start = encodeURIComponent(range.start); + const end = encodeURIComponent(range.end); + + const uiFilters = encodeURIComponent(JSON.stringify({})); + + describe('APM Services Overview', () => { + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + describe('and fetching a list of services', () => { + let response: PromiseReturnType; + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + }); + + it('the response is successful', () => { + expect(response.status).to.eql(200); + }); + + it('there is at least one service', () => { + expect(response.body.items.length).to.be.greaterThan(0); + }); + + it('some items have severity set', () => { + // Under the assumption that the loaded archive has + // at least one APM ML job, and the time range is longer + // than 15m, at least one items should have severity set. + // Note that we currently have a bug where healthy services + // report as unknown (so without any severity status): + // https://github.com/elastic/kibana/issues/77083 + + const severityScores = response.body.items.map((item: any) => item.severity); + + expect(severityScores.filter(Boolean).length).to.be.greaterThan(0); + + expectSnapshot(severityScores).toMatchInline(` + Array [ + undefined, + undefined, + undefined, + undefined, + undefined, + "warning", + undefined, + ] + `); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts index 39cd578917ba..8c3ed246adba 100644 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts +++ b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts @@ -25,14 +25,16 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns an error because the user does not have access', async () => { const { body } = await getJobs(); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); describe('when calling create endpoint', () => { it('returns an error because the user does not have access', async () => { const { body } = await createJobs(['production', 'staging']); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); }); diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts index 6ea0124e5ee8..d158ed847fbb 100644 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts +++ b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts @@ -25,17 +25,18 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns a list of jobs', async () => { const { body } = await getJobs(); - expect(body).to.eql({ - jobs: [], - hasLegacyJobs: false, - }); + + expect(body.jobs.length).to.be(0); + expect(body.hasLegacyJobs).to.be(false); }); }); describe('when calling create endpoint', () => { it('returns an error because the user does not have access', async () => { const { body } = await createJobs(['production', 'staging']); - expect(body).to.eql({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); + + expect(body.statusCode).to.be(404); + expect(body.error).to.be('Not Found'); }); }); }); diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts index 56a2d5dc0f66..d257fe1dd0b0 100644 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts +++ b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts @@ -35,7 +35,8 @@ export default function apiTest({ getService }: FtrProviderContext) { describe('when calling the endpoint for listing jobs', () => { it('returns a list of jobs', async () => { const { body } = await getJobs(); - expect(body).to.eql({ jobs: [], hasLegacyJobs: false }); + expect(body.jobs.length).to.be(0); + expect(body.hasLegacyJobs).to.be(false); }); }); diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index c23df53c4fee..41f92d022f06 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -99,7 +99,7 @@ export const getResilientConnector = () => ({ config: { apiUrl: 'http://some.non.existent.com', orgId: 'pkey', - casesConfiguration: { + incidentConfiguration: { mapping: [ { source: 'title', @@ -118,6 +118,7 @@ export const getResilientConnector = () => ({ }, ], }, + isCaseOwned: true, }, }); diff --git a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts index 5b0d28bf0950..ac4a1298e28b 100644 --- a/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts +++ b/x-pack/test/functional/apps/advanced_settings/feature_controls/advanced_settings_security.ts @@ -10,7 +10,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const security = getService('security'); - const config = getService('config'); const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']); const appsMenu = getService('appsMenu'); const testSubjects = getService('testSubjects'); @@ -174,20 +173,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await security.user.delete('no_advanced_settings_privileges_user'); }); - it('shows Management navlink', async () => { + it('does not show Management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.eql(['Discover']); }); - it(`does not allow navigation to advanced settings; redirects to management home`, async () => { + it(`does not allow navigation to advanced settings; shows "not found" error`, async () => { await PageObjects.common.navigateToUrl('management', 'kibana/settings', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, shouldUseHashForSubUrl: false, }); - await testSubjects.existOrFail('managementHome', { - timeout: config.get('timeouts.waitFor'), - }); + await testSubjects.existOrFail('appNotFoundPageContent'); }); }); }); diff --git a/x-pack/test/functional/apps/api_keys/feature_controls/api_keys_security.ts b/x-pack/test/functional/apps/api_keys/feature_controls/api_keys_security.ts new file mode 100644 index 000000000000..d3d284608285 --- /dev/null +++ b/x-pack/test/functional/apps/api_keys/feature_controls/api_keys_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Security" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with manage_security', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'manage_security'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Security" section with API Keys', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'security', + sectionLinks: ['users', 'roles', 'api_keys', 'role_mappings'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/api_keys/feature_controls/index.ts b/x-pack/test/functional/apps/api_keys/feature_controls/index.ts new file mode 100644 index 000000000000..169b5c7fb0a7 --- /dev/null +++ b/x-pack/test/functional/apps/api_keys/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./api_keys_security')); + }); +} diff --git a/x-pack/test/functional/apps/api_keys/home_page.ts b/x-pack/test/functional/apps/api_keys/home_page.ts index 0c4097a1d5c4..39d8449218ff 100644 --- a/x-pack/test/functional/apps/api_keys/home_page.ts +++ b/x-pack/test/functional/apps/api_keys/home_page.ts @@ -24,10 +24,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); // https://www.elastic.co/guide/en/kibana/7.6/api-keys.html#api-keys-security-privileges - it('Shows required privileges ', async () => { - log.debug('Checking for required privileges method section header'); - const message = await pageObjects.apiKeys.apiKeysPermissionDeniedMessage(); - expect(message).to.be('You need permission to manage API keys'); + it('Hides management link if user is not authorized', async () => { + await testSubjects.missingOrFail('apiKeys'); }); it('Loads the app', async () => { diff --git a/x-pack/test/functional/apps/api_keys/index.ts b/x-pack/test/functional/apps/api_keys/index.ts index 703aae04140f..7a17430dc8f6 100644 --- a/x-pack/test/functional/apps/api_keys/index.ts +++ b/x-pack/test/functional/apps/api_keys/index.ts @@ -10,5 +10,6 @@ export default ({ loadTestFile }: FtrProviderContext) => { describe('API Keys app', function () { this.tags(['ciGroup7']); loadTestFile(require.resolve('./home_page')); + loadTestFile(require.resolve('./feature_controls')); }); }; diff --git a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts index e9fa4ccf8e48..5a8fb207d506 100644 --- a/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts +++ b/x-pack/test/functional/apps/canvas/feature_controls/canvas_security.ts @@ -66,7 +66,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows canvas navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Canvas', 'Stack Management']); + expect(navLinks).to.eql(['Canvas']); }); it(`landing page shows "Create new workpad" button`, async () => { @@ -142,7 +142,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows canvas navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Canvas', 'Stack Management']); + expect(navLinks).to.eql(['Canvas']); }); it(`landing page shows disabled "Create new workpad" button`, async () => { diff --git a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts new file mode 100644 index 000000000000..6b4b9c61151b --- /dev/null +++ b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/ccr_security.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Data" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with ccr_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'ccr_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Data" section with CCR', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(3); + expect(sections[1]).to.eql({ + sectionId: 'data', + sectionLinks: [ + 'index_management', + 'index_lifecycle_management', + 'snapshot_restore', + 'rollup_jobs', + 'transform', + 'cross_cluster_replication', + 'remote_clusters', + ], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.ts new file mode 100644 index 000000000000..e7be2cb48ce3 --- /dev/null +++ b/x-pack/test/functional/apps/cross_cluster_replication/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./ccr_security')); + }); +} diff --git a/x-pack/test/functional/apps/cross_cluster_replication/index.ts b/x-pack/test/functional/apps/cross_cluster_replication/index.ts index 5db6103307af..0e54c0d1c0d1 100644 --- a/x-pack/test/functional/apps/cross_cluster_replication/index.ts +++ b/x-pack/test/functional/apps/cross_cluster_replication/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Cross Cluster Replication app', function () { this.tags(['ciGroup4', 'skipCloud']); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts index 505e35907bd8..46dc0316a5d6 100644 --- a/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts +++ b/x-pack/test/functional/apps/dashboard/feature_controls/dashboard_security.ts @@ -81,9 +81,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await security.user.delete('global_dashboard_all_user'); }); - it('shows dashboard navlink', async () => { + it('only shows the dashboard navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.contain('Dashboard'); + expect(navLinks.map((link) => link.text)).to.eql(['Dashboard']); }); it(`landing page shows "Create new Dashboard" button`, async () => { @@ -287,7 +287,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows dashboard navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.contain('Dashboard'); + expect(navLinks).to.eql(['Dashboard']); }); it(`landing page doesn't show "Create new Dashboard" button`, async () => { @@ -415,7 +415,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows dashboard navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.contain('Dashboard'); + expect(navLinks).to.eql(['Dashboard']); }); it(`landing page doesn't show "Create new Dashboard" button`, async () => { diff --git a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts index 803ff6399a03..807ba6ded88a 100644 --- a/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts +++ b/x-pack/test/functional/apps/dev_tools/feature_controls/dev_tools_security.ts @@ -63,7 +63,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Dev Tools navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Dev Tools', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.eql(['Dev Tools']); }); describe('console', () => { @@ -144,7 +144,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it(`shows 'Dev Tools' navlink`, async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Dev Tools', 'Stack Management']); + expect(navLinks).to.eql(['Dev Tools']); }); describe('console', () => { diff --git a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts index 8be434976280..d94451d023ec 100644 --- a/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts +++ b/x-pack/test/functional/apps/discover/feature_controls/discover_security.ts @@ -82,7 +82,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Discover', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.eql(['Discover']); }); it('shows save button', async () => { @@ -184,7 +184,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.eql(['Discover']); }); it(`doesn't show save button`, async () => { @@ -275,7 +275,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows discover navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.eql(['Discover']); }); it(`doesn't show save button`, async () => { diff --git a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts index 9121028c1440..3b4a1fbdbe0d 100644 --- a/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts +++ b/x-pack/test/functional/apps/graph/feature_controls/graph_security.ts @@ -64,7 +64,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows graph navlink', async () => { const navLinks = await appsMenu.readLinks(); - expect(navLinks.map((link) => link.text)).to.eql(['Graph', 'Stack Management']); + expect(navLinks.map((link) => link.text)).to.eql(['Graph']); }); it('landing page shows "Create new graph" button', async () => { @@ -127,7 +127,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows graph navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Graph', 'Stack Management']); + expect(navLinks).to.eql(['Graph']); }); it('does not show a "Create new Workspace" button', async () => { diff --git a/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/ilm_security.ts b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/ilm_security.ts new file mode 100644 index 000000000000..4cb0d3077aaa --- /dev/null +++ b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/ilm_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Data" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with manage_ilm', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'manage_ilm'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Data" section with ILM', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'data', + sectionLinks: ['index_lifecycle_management'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.ts new file mode 100644 index 000000000000..0bb6476f3668 --- /dev/null +++ b/x-pack/test/functional/apps/index_lifecycle_management/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./ilm_security')); + }); +} diff --git a/x-pack/test/functional/apps/index_lifecycle_management/index.ts b/x-pack/test/functional/apps/index_lifecycle_management/index.ts index f535710814ab..157fb62b7a84 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/index.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Lifecycle Management app', function () { this.tags('ciGroup7'); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/index_management/feature_controls/index.ts b/x-pack/test/functional/apps/index_management/feature_controls/index.ts new file mode 100644 index 000000000000..85398a73ecef --- /dev/null +++ b/x-pack/test/functional/apps/index_management/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./index_management_security')); + }); +} diff --git a/x-pack/test/functional/apps/index_management/feature_controls/index_management_security.ts b/x-pack/test/functional/apps/index_management/feature_controls/index_management_security.ts new file mode 100644 index 000000000000..2019751d9101 --- /dev/null +++ b/x-pack/test/functional/apps/index_management/feature_controls/index_management_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Data" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with index_management_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'index_management_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Data" section with index management', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'data', + sectionLinks: ['index_management', 'transform'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/index_management/index.ts b/x-pack/test/functional/apps/index_management/index.ts index a9bb44d00233..97b23cbf82c3 100644 --- a/x-pack/test/functional/apps/index_management/index.ts +++ b/x-pack/test/functional/apps/index_management/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Index Management app', function () { this.tags('ciGroup3'); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts index cedd96f147c2..4873a11d75ea 100644 --- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts +++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts @@ -10,7 +10,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const security = getService('security'); - const config = getService('config'); const PageObjects = getPageObjects(['common', 'settings', 'security']); const appsMenu = getService('appsMenu'); const testSubjects = getService('testSubjects'); @@ -175,28 +174,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await security.user.delete('no_index_patterns_privileges_user'); }); - it('shows Management navlink', async () => { + it('does not show Management navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Discover', 'Stack Management']); + expect(navLinks).to.eql(['Discover']); }); it(`doesn't show Index Patterns in management side-nav`, async () => { - await PageObjects.settings.navigateTo(); - await testSubjects.existOrFail('managementHome', { - timeout: config.get('timeouts.waitFor'), - }); - await testSubjects.missingOrFail('indexPatterns'); - }); - - it(`does not allow navigation to Index Patterns; redirects to management home`, async () => { - await PageObjects.common.navigateToUrl('management', 'kibana/indexPatterns', { + await PageObjects.common.navigateToActualUrl('management', '', { ensureCurrentUrl: false, shouldLoginIfPrompted: false, - shouldUseHashForSubUrl: false, - }); - await testSubjects.existOrFail('managementHome', { - timeout: config.get('timeouts.waitFor'), }); + await testSubjects.existOrFail('~appNotFoundPageContent'); }); }); }); diff --git a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts index 64154ff6cf3f..552e948f56a9 100644 --- a/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts +++ b/x-pack/test/functional/apps/infra/feature_controls/logs_security.ts @@ -58,7 +58,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows logs navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Overview', 'Logs', 'Stack Management']); + expect(navLinks).to.eql(['Overview', 'Logs']); }); describe('logs landing page without data', () => { @@ -121,7 +121,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows logs navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Overview', 'Logs', 'Stack Management']); + expect(navLinks).to.eql(['Overview', 'Logs']); }); describe('logs landing page without data', () => { diff --git a/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.ts new file mode 100644 index 000000000000..fbaf7648646b --- /dev/null +++ b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./ingest_pipelines_security')); + }); +} diff --git a/x-pack/test/functional/apps/ingest_pipelines/feature_controls/ingest_pipelines_security.ts b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/ingest_pipelines_security.ts new file mode 100644 index 000000000000..bf703a8f60dc --- /dev/null +++ b/x-pack/test/functional/apps/ingest_pipelines/feature_controls/ingest_pipelines_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Ingest" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with ingest_pipelines_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'ingest_pipelines_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Ingest" section with ingest pipelines', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'ingest', + sectionLinks: ['ingest_pipelines'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/ingest_pipelines/index.ts b/x-pack/test/functional/apps/ingest_pipelines/index.ts index 8d2b9ee1dcb6..2a4781c5e216 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/index.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Ingest pipelines app', function () { this.tags('ciGroup3'); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./ingest_pipelines')); }); }; diff --git a/x-pack/test/functional/apps/license_management/feature_controls/index.ts b/x-pack/test/functional/apps/license_management/feature_controls/index.ts new file mode 100644 index 000000000000..5c7c04d4ccde --- /dev/null +++ b/x-pack/test/functional/apps/license_management/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./license_management_security')); + }); +} diff --git a/x-pack/test/functional/apps/license_management/feature_controls/license_management_security.ts b/x-pack/test/functional/apps/license_management/feature_controls/license_management_security.ts new file mode 100644 index 000000000000..59fc287c6cf2 --- /dev/null +++ b/x-pack/test/functional/apps/license_management/feature_controls/license_management_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Stack" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with license_management_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'license_management_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Stack" section with License Management', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(3); + expect(sections[2]).to.eql({ + sectionId: 'stack', + sectionLinks: ['license_management', 'upgrade_assistant'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/license_management/index.ts b/x-pack/test/functional/apps/license_management/index.ts index 6d01b1bb098f..0b090223c18f 100644 --- a/x-pack/test/functional/apps/license_management/index.ts +++ b/x-pack/test/functional/apps/license_management/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('License app', function () { this.tags('ciGroup7'); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/logstash/feature_controls/index.ts b/x-pack/test/functional/apps/logstash/feature_controls/index.ts new file mode 100644 index 000000000000..d3cc7fae94d9 --- /dev/null +++ b/x-pack/test/functional/apps/logstash/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./logstash_security')); + }); +} diff --git a/x-pack/test/functional/apps/logstash/feature_controls/logstash_security.ts b/x-pack/test/functional/apps/logstash/feature_controls/logstash_security.ts new file mode 100644 index 000000000000..8e2609e3b7e8 --- /dev/null +++ b/x-pack/test/functional/apps/logstash/feature_controls/logstash_security.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Ingest" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with logstash_read_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'logstash_read_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Ingest" section with Logstash Pipelines', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'ingest', + sectionLinks: ['pipelines'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/logstash/index.js b/x-pack/test/functional/apps/logstash/index.js index 515674577fb5..3258d948cedf 100644 --- a/x-pack/test/functional/apps/logstash/index.js +++ b/x-pack/test/functional/apps/logstash/index.js @@ -8,6 +8,7 @@ export default function ({ loadTestFile }) { describe('logstash', function () { this.tags(['ciGroup2']); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./pipeline_list')); loadTestFile(require.resolve('./pipeline_create')); }); diff --git a/x-pack/test/functional/apps/management/feature_controls/index.ts b/x-pack/test/functional/apps/management/feature_controls/index.ts new file mode 100644 index 000000000000..8b8226da7dc3 --- /dev/null +++ b/x-pack/test/functional/apps/management/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./management_security')); + }); +} diff --git a/x-pack/test/functional/apps/management/feature_controls/management_security.ts b/x-pack/test/functional/apps/management/feature_controls/management_security.ts new file mode 100644 index 000000000000..cf1a83ca4968 --- /dev/null +++ b/x-pack/test/functional/apps/management/feature_controls/management_security.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + const testSubjects = getService('testSubjects'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('no management privileges', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should not show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.eql(['Dashboard']); + }); + + it('should render the "application not found" view when navigating to management directly', async () => { + await PageObjects.common.navigateToApp('management'); + expect(await testSubjects.exists('appNotFoundPageContent')).to.eql(true); + }); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should only render management entries controllable via Kibana privileges', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(2); + expect(sections[0]).to.eql({ + sectionId: 'insightsAndAlerting', + sectionLinks: ['triggersActions'], + }); + expect(sections[1]).to.eql({ + sectionId: 'kibana', + sectionLinks: ['indexPatterns', 'objects', 'spaces', 'settings'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/management/index.js b/x-pack/test/functional/apps/management/index.ts similarity index 67% rename from x-pack/test/functional/apps/management/index.js rename to x-pack/test/functional/apps/management/index.ts index 19c68a2da9d9..7a461c9963be 100644 --- a/x-pack/test/functional/apps/management/index.js +++ b/x-pack/test/functional/apps/management/index.ts @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function ({ loadTestFile }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { describe('management', function () { this.tags(['ciGroup2']); loadTestFile(require.resolve('./create_index_pattern_wizard')); + loadTestFile(require.resolve('./feature_controls')); }); } diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts index ae9b0f095fc4..e32f14200ad8 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts +++ b/x-pack/test/functional/apps/maps/feature_controls/maps_security.ts @@ -67,7 +67,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows maps navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Maps', 'Stack Management']); + expect(navLinks).to.eql(['Maps']); }); it(`allows a map to be created`, async () => { @@ -170,7 +170,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows Maps navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Maps', 'Stack Management']); + expect(navLinks).to.eql(['Maps']); }); it(`does not show create new button`, async () => { diff --git a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts index 6fd78458a6ce..ab67e567e67a 100644 --- a/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/no_ml_access.ts @@ -55,16 +55,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('should not allow to access the Stack Management ML page', async () => { await ml.testExecution.logTestStep( - 'should load the stack management with the ML menu item being present' + 'should load the stack management with the ML menu item being absent' ); - await ml.navigation.navigateToStackManagement(); - - await ml.testExecution.logTestStep( - 'should display the access denied page in stack management' - ); - await ml.navigation.navigateToStackManagementJobsListPage({ - expectAccessDenied: true, - }); + await ml.navigation.navigateToStackManagement({ expectMlLink: false }); }); }); } diff --git a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts index a358e57f792c..cb964995511e 100644 --- a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts @@ -408,16 +408,9 @@ export default function ({ getService }: FtrProviderContext) { it('should display elements on Stack Management ML page correctly', async () => { await ml.testExecution.logTestStep( - 'should load the stack management with the ML menu item being present' + 'should load the stack management with the ML menu item being absent' ); - await ml.navigation.navigateToStackManagement(); - - await ml.testExecution.logTestStep( - 'should display the access denied page in stack management' - ); - await ml.navigation.navigateToStackManagementJobsListPage({ - expectAccessDenied: true, - }); + await ml.navigation.navigateToStackManagement({ expectMlLink: false }); }); }); } diff --git a/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts b/x-pack/test/functional/apps/remote_clusters/feature_controls/index.ts new file mode 100644 index 000000000000..bfcaef629dc4 --- /dev/null +++ b/x-pack/test/functional/apps/remote_clusters/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./remote_clusters_security')); + }); +} diff --git a/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts new file mode 100644 index 000000000000..b1edc7460716 --- /dev/null +++ b/x-pack/test/functional/apps/remote_clusters/feature_controls/remote_clusters_security.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Stack" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with license_management_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'license_management_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Data" section with Remote Clusters', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(3); + expect(sections[1]).to.eql({ + sectionId: 'data', + sectionLinks: [ + 'index_management', + 'index_lifecycle_management', + 'snapshot_restore', + 'rollup_jobs', + 'transform', + 'remote_clusters', + ], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/remote_clusters/index.ts b/x-pack/test/functional/apps/remote_clusters/index.ts index d91d413e2b7a..0839c2f22af4 100644 --- a/x-pack/test/functional/apps/remote_clusters/index.ts +++ b/x-pack/test/functional/apps/remote_clusters/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext) => { describe('Remote Clusters app', function () { this.tags(['ciGroup4', 'skipCloud']); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./home_page')); }); }; diff --git a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts index 28b8153ea4c2..02b2ec4d4c68 100644 --- a/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts +++ b/x-pack/test/functional/apps/saved_objects_management/feature_controls/saved_objects_management_security.ts @@ -10,14 +10,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const security = getService('security'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects([ - 'common', - 'settings', - 'security', - 'error', - 'header', - 'savedObjects', - ]); + const PageObjects = getPageObjects(['common', 'settings', 'security', 'error', 'savedObjects']); let version: string = ''; describe('feature controls saved objects management', () => { @@ -310,12 +303,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('listing', () => { - it(`doesn't display management section`, async () => { - await PageObjects.settings.navigateTo(); - await testSubjects.existOrFail('managementHome'); // this ensures we've gotten to the management page - await testSubjects.missingOrFail('objects'); - }); - it(`can't navigate to listing page`, async () => { await PageObjects.common.navigateToUrl('management', 'kibana/objects', { ensureCurrentUrl: false, @@ -323,7 +310,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { shouldUseHashForSubUrl: false, }); - await testSubjects.existOrFail('managementHome'); + await testSubjects.existOrFail('appNotFoundPageContent'); }); }); @@ -338,8 +325,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { shouldUseHashForSubUrl: false, } ); - await PageObjects.header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('managementHome'); + await testSubjects.existOrFail('appNotFoundPageContent'); }); }); }); diff --git a/x-pack/test/functional/apps/security/secure_roles_perm.js b/x-pack/test/functional/apps/security/secure_roles_perm.js index 2054a7b0b003..c547657bf880 100644 --- a/x-pack/test/functional/apps/security/secure_roles_perm.js +++ b/x-pack/test/functional/apps/security/secure_roles_perm.js @@ -21,7 +21,6 @@ export default function ({ getService, getPageObjects }) { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); - const retry = getService('retry'); describe('secure roles and permissions', function () { before(async () => { @@ -74,12 +73,9 @@ export default function ({ getService, getPageObjects }) { await PageObjects.security.login('Rashmi', 'changeme'); }); - it('Kibana User navigating to Management gets permission denied', async function () { + it('Kibana User does not have link to user management', async function () { await PageObjects.settings.navigateTo(); - await PageObjects.security.clickElasticsearchUsers(); - await retry.tryForTime(2000, async () => { - await testSubjects.find('permissionDeniedMessage'); - }); + await testSubjects.missingOrFail('users'); }); it('Kibana User navigating to Discover and trying to generate CSV gets - Authorization Error ', async function () { diff --git a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts index a3ade23f5c17..d705140954de 100644 --- a/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts +++ b/x-pack/test/functional/apps/timelion/feature_controls/timelion_security.ts @@ -60,7 +60,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Timelion', 'Stack Management']); + expect(navLinks).to.eql(['Timelion']); }); it(`allows a timelion sheet to be created`, async () => { @@ -112,7 +112,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows timelion navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Timelion', 'Stack Management']); + expect(navLinks).to.eql(['Timelion']); }); it(`does not allow a timelion sheet to be created`, async () => { diff --git a/x-pack/test/functional/apps/transform/cloning.ts b/x-pack/test/functional/apps/transform/cloning.ts index b6ccd68bb209..a147b56d5625 100644 --- a/x-pack/test/functional/apps/transform/cloning.ts +++ b/x-pack/test/functional/apps/transform/cloning.ts @@ -5,12 +5,12 @@ */ import { FtrProviderContext } from '../../ftr_provider_context'; -import { TransformPivotConfig } from '../../../../plugins/transform/public/app/common'; +import { TransformPivotConfig } from '../../../../plugins/transform/common/types/transform'; function getTransformConfig(): TransformPivotConfig { const date = Date.now(); return { - id: `ec_2_${date}`, + id: `ec_cloning_${date}`, source: { index: ['ft_ecommerce'] }, pivot: { group_by: { category: { terms: { field: 'category.keyword' } } }, @@ -32,7 +32,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('ml/ecommerce'); await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); - await transform.api.createAndRunTransform(transformConfig); + await transform.api.createAndRunTransform(transformConfig.id, transformConfig); await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); diff --git a/x-pack/test/functional/apps/transform/creation_index_pattern.ts b/x-pack/test/functional/apps/transform/creation_index_pattern.ts index 4e2b832838b7..13213679a611 100644 --- a/x-pack/test/functional/apps/transform/creation_index_pattern.ts +++ b/x-pack/test/functional/apps/transform/creation_index_pattern.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + import { FtrProviderContext } from '../../ftr_provider_context'; interface GroupByEntry { @@ -141,7 +143,7 @@ export default function ({ getService }: FtrProviderContext) { values: [`Men's Accessories`], }, row: { - status: 'stopped', + status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', }, @@ -239,7 +241,7 @@ export default function ({ getService }: FtrProviderContext) { values: ['AE', 'CO', 'EG', 'FR', 'GB'], }, row: { - status: 'stopped', + status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', }, diff --git a/x-pack/test/functional/apps/transform/creation_saved_search.ts b/x-pack/test/functional/apps/transform/creation_saved_search.ts index 229ff9778236..20d276c2e017 100644 --- a/x-pack/test/functional/apps/transform/creation_saved_search.ts +++ b/x-pack/test/functional/apps/transform/creation_saved_search.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + import { FtrProviderContext } from '../../ftr_provider_context'; interface GroupByEntry { @@ -58,7 +60,7 @@ export default function ({ getService }: FtrProviderContext) { values: ['ASA'], }, row: { - status: 'stopped', + status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', }, diff --git a/x-pack/test/functional/apps/transform/editing.ts b/x-pack/test/functional/apps/transform/editing.ts index 460e7c5b24a9..ac955bde4ad5 100644 --- a/x-pack/test/functional/apps/transform/editing.ts +++ b/x-pack/test/functional/apps/transform/editing.ts @@ -4,13 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import { TransformPivotConfig } from '../../../../plugins/transform/common/types/transform'; +import { TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; + import { FtrProviderContext } from '../../ftr_provider_context'; -import { TransformPivotConfig } from '../../../../plugins/transform/public/app/common'; function getTransformConfig(): TransformPivotConfig { const date = Date.now(); return { - id: `ec_2_${date}`, + id: `ec_editing_${date}`, source: { index: ['ft_ecommerce'] }, pivot: { group_by: { category: { terms: { field: 'category.keyword' } } }, @@ -32,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { await esArchiver.loadIfNeeded('ml/ecommerce'); await transform.testResources.createIndexPatternIfNeeded('ft_ecommerce', 'order_date'); - await transform.api.createAndRunTransform(transformConfig); + await transform.api.createAndRunTransform(transformConfig.id, transformConfig); await transform.testResources.setKibanaTimeZoneToUTC(); await transform.securityUI.loginAsTransformPowerUser(); @@ -52,7 +54,7 @@ export default function ({ getService }: FtrProviderContext) { expected: { messageText: 'updated transform.', row: { - status: 'stopped', + status: TRANSFORM_STATE.STOPPED, mode: 'batch', progress: '100', }, diff --git a/x-pack/test/functional/apps/transform/feature_controls/index.ts b/x-pack/test/functional/apps/transform/feature_controls/index.ts new file mode 100644 index 000000000000..794e6f516d98 --- /dev/null +++ b/x-pack/test/functional/apps/transform/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./transform_security')); + }); +} diff --git a/x-pack/test/functional/apps/transform/feature_controls/transform_security.ts b/x-pack/test/functional/apps/transform/feature_controls/transform_security.ts new file mode 100644 index 000000000000..5d7d8ec3c307 --- /dev/null +++ b/x-pack/test/functional/apps/transform/feature_controls/transform_security.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.security.forceLogout(); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Stack" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with transform_user', () => { + before(async () => { + await security.testUser.setRoles(['global_dashboard_all', 'transform_user'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Data" section with Transform', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(1); + expect(sections[0]).to.eql({ + sectionId: 'data', + sectionLinks: ['transform'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts index a01f3fa5d53a..2837ddb7333e 100644 --- a/x-pack/test/functional/apps/transform/index.ts +++ b/x-pack/test/functional/apps/transform/index.ts @@ -37,5 +37,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./creation_saved_search')); loadTestFile(require.resolve('./cloning')); loadTestFile(require.resolve('./editing')); + loadTestFile(require.resolve('./feature_controls')); }); } diff --git a/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.ts new file mode 100644 index 000000000000..f1c73e39fbc3 --- /dev/null +++ b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/index.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 { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('feature controls', function () { + this.tags(['ciGroup2']); + + loadTestFile(require.resolve('./upgrade_assistant_security')); + }); +} diff --git a/x-pack/test/functional/apps/upgrade_assistant/feature_controls/upgrade_assistant_security.ts b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/upgrade_assistant_security.ts new file mode 100644 index 000000000000..1f541dbe0353 --- /dev/null +++ b/x-pack/test/functional/apps/upgrade_assistant/feature_controls/upgrade_assistant_security.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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'settings', 'security']); + const appsMenu = getService('appsMenu'); + const managementMenu = getService('managementMenu'); + + describe('security', () => { + before(async () => { + await esArchiver.load('empty_kibana'); + await PageObjects.common.navigateToApp('home'); + }); + + after(async () => { + await esArchiver.unload('empty_kibana'); + }); + + describe('global all privileges (aka kibana_admin)', () => { + before(async () => { + await security.testUser.setRoles(['kibana_admin'], true); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should not render the "Stack" section', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = (await managementMenu.getSections()).map((section) => section.sectionId); + expect(sections).to.eql(['insightsAndAlerting', 'kibana']); + }); + }); + + describe('global dashboard all with global_upgrade_assistant_role', () => { + before(async () => { + await security.testUser.setRoles( + ['global_dashboard_all', 'global_upgrade_assistant_role'], + true + ); + }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('should show the Stack Management nav link', async () => { + const links = await appsMenu.readLinks(); + expect(links.map((link) => link.text)).to.contain('Stack Management'); + }); + + it('should render the "Stack" section with Upgrde Assistant', async () => { + await PageObjects.common.navigateToApp('management'); + const sections = await managementMenu.getSections(); + expect(sections).to.have.length(3); + expect(sections[2]).to.eql({ + sectionId: 'stack', + sectionLinks: ['license_management', 'upgrade_assistant'], + }); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/upgrade_assistant/index.ts b/x-pack/test/functional/apps/upgrade_assistant/index.ts index 0e6c52f0812e..131cb6a249c7 100644 --- a/x-pack/test/functional/apps/upgrade_assistant/index.ts +++ b/x-pack/test/functional/apps/upgrade_assistant/index.ts @@ -9,6 +9,7 @@ export default function upgradeCheckup({ loadTestFile }: FtrProviderContext) { describe('Upgrade checkup ', function upgradeAssistantTestSuite() { this.tags('ciGroup4'); + loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./upgrade_assistant')); }); } diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts index 49435df4f1c2..ca84a8e56116 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_security.ts @@ -79,7 +79,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.eql(['Visualize']); }); it(`landing page shows "Create new Visualization" button`, async () => { @@ -210,7 +210,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.eql(['Visualize']); }); it(`landing page shows "Create new Visualization" button`, async () => { @@ -325,7 +325,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('shows visualize navlink', async () => { const navLinks = (await appsMenu.readLinks()).map((link) => link.text); - expect(navLinks).to.eql(['Visualize', 'Stack Management']); + expect(navLinks).to.eql(['Visualize']); }); it(`landing page shows "Create new Visualization" button`, async () => { diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 16e2cd1559fc..d1164c8c8536 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -266,6 +266,16 @@ export default async function ({ readConfigFile }) { }, ], }, + global_dashboard_all: { + kibana: [ + { + feature: { + dashboard: ['all'], + }, + spaces: ['*'], + }, + ], + }, global_maps_all: { kibana: [ { @@ -352,6 +362,65 @@ export default async function ({ readConfigFile }) { }, ], }, + + manage_security: { + elasticsearch: { + cluster: ['manage_security'], + }, + }, + + ccr_user: { + elasticsearch: { + cluster: ['manage', 'manage_ccr'], + }, + }, + + manage_ilm: { + elasticsearch: { + cluster: ['manage_ilm'], + }, + }, + + index_management_user: { + elasticsearch: { + cluster: ['monitor', 'manage_index_templates'], + indices: [ + { + names: ['geo_shapes*'], + privileges: ['all'], + }, + ], + }, + }, + + ingest_pipelines_user: { + elasticsearch: { + cluster: ['manage_pipeline', 'cluster:monitor/nodes/info'], + }, + }, + + license_management_user: { + elasticsearch: { + cluster: ['manage'], + }, + }, + + logstash_read_user: { + elasticsearch: { + indices: [ + { + names: ['.logstash*'], + privileges: ['read'], + }, + ], + }, + }, + + remote_clusters_user: { + elasticsearch: { + cluster: ['manage'], + }, + }, }, defaultRoles: ['superuser'], }, diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json new file mode 100644 index 000000000000..b19e5e2cbf1d --- /dev/null +++ b/x-pack/test/functional/es_archives/endpoint/metadata/destination_index/data.json @@ -0,0 +1,223 @@ +{ + "type": "doc", + "value": { + "id": "M92ScEJT9M9QusfIi3hpEb0AAAAAAAAA", + "index": "metrics-endpoint.metadata_current-default", + "source": { + "HostDetails": { + "@timestamp": 1579881969541, + "Endpoint": { + "policy": { + "applied": { + "id": "00000000-0000-0000-0000-000000000000", + "name": "Default", + "status": "failure" + } + }, + "status": "enrolled" + }, + "agent": { + "id": "3838df35-a095-4af4-8fce-0b6d78793f2e", + "name": "Elastic Endpoint", + "version": "6.8.0" + }, + "elastic": { + "agent": { + "id": "023fa40c-411d-4188-a941-4147bfadd095" + } + }, + "event": { + "action": "endpoint_metadata", + "category": [ + "host" + ], + "created": 1579881969541, + "dataset": "endpoint.metadata", + "id": "32f5fda2-48e4-4fae-b89e-a18038294d16", + "ingested": "2020-09-09T18:25:15.853783Z", + "kind": "metric", + "module": "endpoint", + "type": [ + "info" + ] + }, + "host": { + "hostname": "rezzani-7.example.com", + "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", + "ip": [ + "10.101.149.26", + "2606:a000:ffc0:39:11ef:37b9:3371:578c" + ], + "mac": [ + "e2-6d-f9-0-46-2e" + ], + "name": "rezzani-7.example.com", + "os": { + "Ext": { + "variant": "Windows Pro" + }, + "family": "Windows", + "full": "Windows 10", + "name": "windows 10.0", + "platform": "Windows", + "version": "10.0" + } + } + }, + "agent": { + "id": "3838df35-a095-4af4-8fce-0b6d78793f2e" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "OU3RgCJaNnR90byeDEHutp8AAAAAAAAA", + "index": "metrics-endpoint.metadata_current-default", + "source": { + "HostDetails": { + "@timestamp": 1579881969541, + "Endpoint": { + "policy": { + "applied": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", + "name": "Default", + "status": "failure" + } + }, + "status": "enrolled" + }, + "agent": { + "id": "963b081e-60d1-482c-befd-a5815fa8290f", + "name": "Elastic Endpoint", + "version": "6.6.1" + }, + "elastic": { + "agent": { + "id": "11488bae-880b-4e7b-8d28-aac2aa9de816" + } + }, + "event": { + "action": "endpoint_metadata", + "category": [ + "host" + ], + "created": 1579881969541, + "dataset": "endpoint.metadata", + "id": "32f5fda2-48e4-4fae-b89e-a18038294d14", + "ingested": "2020-09-09T18:25:14.919526Z", + "kind": "metric", + "module": "endpoint", + "type": [ + "info" + ] + }, + "host": { + "architecture": "x86", + "hostname": "cadmann-4.example.com", + "id": "1fb3e58f-6ab0-4406-9d2a-91911207a712", + "ip": [ + "10.192.213.130", + "10.70.28.129" + ], + "mac": [ + "a9-71-6a-cc-93-85", + "f7-31-84-d3-21-68", + "2-95-12-39-ca-71" + ], + "name": "cadmann-4.example.com", + "os": { + "Ext": { + "variant": "Windows Pro" + }, + "family": "Windows", + "full": "Windows 10", + "name": "windows 10.0", + "platform": "Windows", + "version": "10.0" + } + } + }, + "agent": { + "id": "963b081e-60d1-482c-befd-a5815fa8290f" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "YjqDCEuI6JmLeLOSyZx_NhMAAAAAAAAA", + "index": "metrics-endpoint.metadata_current-default", + "source": { + "HostDetails": { + "@timestamp": 1579881969541, + "Endpoint": { + "policy": { + "applied": { + "id": "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", + "name": "Default", + "status": "success" + } + }, + "status": "enrolled" + }, + "agent": { + "id": "b3412d6f-b022-4448-8fee-21cc936ea86b", + "name": "Elastic Endpoint", + "version": "6.0.0" + }, + "elastic": { + "agent": { + "id": "92ac1ce0-e1f7-409e-8af6-f17e97b1fc71" + } + }, + "event": { + "action": "endpoint_metadata", + "category": [ + "host" + ], + "created": 1579881969541, + "dataset": "endpoint.metadata", + "id": "32f5fda2-48e4-4fae-b89e-a18038294d15", + "ingested": "2020-09-09T18:25:15.853404Z", + "kind": "metric", + "module": "endpoint", + "type": [ + "info" + ] + }, + "host": { + "architecture": "x86_64", + "hostname": "thurlow-9.example.com", + "id": "2f735e3d-be14-483b-9822-bad06e9045ca", + "ip": [ + "10.46.229.234" + ], + "mac": [ + "30-8c-45-55-69-b8", + "e5-36-7e-8f-a3-84", + "39-a1-37-20-18-74" + ], + "name": "thurlow-9.example.com", + "os": { + "Ext": { + "variant": "Windows Server" + }, + "family": "Windows", + "full": "Windows Server 2016", + "name": "windows 10.0", + "platform": "Windows", + "version": "10.0" + } + } + }, + "agent": { + "id": "b3412d6f-b022-4448-8fee-21cc936ea86b" + } + } + } +} diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 5c9718539f47..35d0439f6974 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -268,7 +268,7 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { async waitForDFAJobTrainingRecordCountToBePositive(analyticsId: string) { await retry.waitForWithTimeout( `'${analyticsId}' to have training_docs_count > 0`, - 10 * 1000, + 60 * 1000, async () => { const trainingRecordCount = await this.getDFAJobTrainingRecordCount(analyticsId); if (trainingRecordCount > 0) { diff --git a/x-pack/test/functional/services/ml/navigation.ts b/x-pack/test/functional/services/ml/navigation.ts index 9b53e5ce2f7e..e564c03f62d5 100644 --- a/x-pack/test/functional/services/ml/navigation.ts +++ b/x-pack/test/functional/services/ml/navigation.ts @@ -23,10 +23,14 @@ export function MachineLearningNavigationProvider({ }); }, - async navigateToStackManagement() { + async navigateToStackManagement({ expectMlLink = true }: { expectMlLink?: boolean } = {}) { await retry.tryForTime(60 * 1000, async () => { await PageObjects.common.navigateToApp('management'); - await testSubjects.existOrFail('jobsListLink', { timeout: 2000 }); + if (expectMlLink) { + await testSubjects.existOrFail('jobsListLink', { timeout: 2000 }); + } else { + await testSubjects.missingOrFail('jobsListLink', { timeout: 2000 }); + } }); }, @@ -84,22 +88,14 @@ export function MachineLearningNavigationProvider({ await this.navigateToArea('~mlMainTab & ~settings', 'mlPageSettings'); }, - async navigateToStackManagementJobsListPage({ - expectAccessDenied = false, - }: { - expectAccessDenied?: boolean; - } = {}) { + async navigateToStackManagementJobsListPage() { // clicks the jobsListLink and loads the jobs list page await testSubjects.click('jobsListLink'); await retry.tryForTime(60 * 1000, async () => { - if (expectAccessDenied === true) { - await testSubjects.existOrFail('mlPageAccessDenied'); - } else { - // verify that the overall page is present - await testSubjects.existOrFail('mlPageStackManagementJobsList'); - // verify that the default tab with the anomaly detection jobs list got loaded - await testSubjects.existOrFail('ml-jobs-list'); - } + // verify that the overall page is present + await testSubjects.existOrFail('mlPageStackManagementJobsList'); + // verify that the default tab with the anomaly detection jobs list got loaded + await testSubjects.existOrFail('ml-jobs-list'); }); }, diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index 697020fafb19..d97db93c31b3 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -5,13 +5,17 @@ */ import expect from '@kbn/expect'; +import type { PutTransformsRequestSchema } from '../../../../plugins/transform/common/api_schemas/transforms'; +import { TransformState, TRANSFORM_STATE } from '../../../../plugins/transform/common/constants'; +import type { TransformStats } from '../../../../plugins/transform/common/types/transform_stats'; + import { FtrProviderContext } from '../../ftr_provider_context'; -import { TRANSFORM_STATE } from '../../../../plugins/transform/common'; -import { - TransformPivotConfig, - TransformStats, -} from '../../../../plugins/transform/public/app/common'; +export async function asyncForEach(array: any[], callback: Function) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array); + } +} export function TransformAPIProvider({ getService }: FtrProviderContext) { const es = getService('legacyEs'); @@ -35,7 +39,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { await this.waitForIndicesToExist(indices, `expected ${indices} to be created`); }, - async deleteIndices(indices: string) { + async deleteIndices(indices: string, skipWaitForIndicesNotToExist?: boolean) { log.debug(`Deleting indices: '${indices}'...`); if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { log.debug(`Indices '${indices}' don't exist. Nothing to delete.`); @@ -49,7 +53,13 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { .to.have.property('acknowledged') .eql(true, 'Response for delete request should be acknowledged'); - await this.waitForIndicesNotToExist(indices, `expected indices '${indices}' to be deleted`); + // Check for the option to skip the check if the indices are deleted. + // For example, we might want to clear the .transform-* indices but they + // will be automatically regenerated making tests flaky without the option + // to skip this check. + if (!skipWaitForIndicesNotToExist) { + await this.waitForIndicesNotToExist(indices, `expected indices '${indices}' to be deleted`); + } }, async waitForIndicesToExist(indices: string, errorMsg?: string) { @@ -73,7 +83,26 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { }, async cleanTransformIndices() { - await this.deleteIndices('.transform-*'); + // Delete all transforms using the API since we mustn't just delete + // all `.transform-*` indices since this might result in orphaned ES tasks. + const { + body: { transforms }, + } = await esSupertest.get(`/_transform/`).expect(200); + const transformIds = transforms.map((t: { id: string }) => t.id); + + await asyncForEach(transformIds, async (transformId: string) => { + await esSupertest + .post(`/_transform/${transformId}/_stop?force=true&wait_for_completion`) + .expect(200); + await this.waitForTransformState(transformId, TRANSFORM_STATE.STOPPED); + + await esSupertest.delete(`/_transform/${transformId}`).expect(200); + await this.waitForTransformNotToExist(transformId); + }); + + // Delete all transform related notifications to clear messages tabs + // in the transforms list expanded rows. + await this.deleteIndices('.transform-notifications-*'); }, async getTransformStats(transformId: string): Promise { @@ -90,12 +119,12 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { return statsResponse.transforms[0]; }, - async getTransformState(transformId: string): Promise { + async getTransformState(transformId: string): Promise { const stats = await this.getTransformStats(transformId); return stats.state; }, - async waitForTransformState(transformId: string, expectedState: TRANSFORM_STATE) { + async waitForTransformState(transformId: string, expectedState: TransformState) { await retry.waitForWithTimeout( `transform state to be ${expectedState}`, 2 * 60 * 1000, @@ -110,6 +139,23 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { ); }, + async waitForTransformStateNotToBe(transformId: string, notExpectedState: TransformState) { + await retry.waitForWithTimeout( + `transform state not to be ${notExpectedState}`, + 2 * 60 * 1000, + async () => { + const state = await this.getTransformState(transformId); + if (state !== notExpectedState) { + return true; + } else { + throw new Error( + `expected transform state to not be ${notExpectedState} but got ${state}` + ); + } + } + ); + }, + async waitForBatchTransformToComplete(transformId: string) { await retry.waitForWithTimeout(`batch transform to complete`, 2 * 60 * 1000, async () => { const stats = await this.getTransformStats(transformId); @@ -127,8 +173,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { return await esSupertest.get(`/_transform/${transformId}`).expect(expectedCode); }, - async createTransform(transformConfig: TransformPivotConfig) { - const transformId = transformConfig.id; + async createTransform(transformId: string, transformConfig: PutTransformsRequestSchema) { log.debug(`Creating transform with id '${transformId}'...`); await esSupertest.put(`/_transform/${transformId}`).send(transformConfig).expect(200); @@ -147,6 +192,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { } }); }, + async waitForTransformNotToExist(transformId: string, errorMsg?: string) { await retry.waitForWithTimeout(`'${transformId}' to exist`, 5 * 1000, async () => { if (await this.getTransform(transformId, 404)) { @@ -162,15 +208,15 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { await esSupertest.post(`/_transform/${transformId}/_start`).expect(200); }, - async createAndRunTransform(transformConfig: TransformPivotConfig) { - await this.createTransform(transformConfig); - await this.startTransform(transformConfig.id); + async createAndRunTransform(transformId: string, transformConfig: PutTransformsRequestSchema) { + await this.createTransform(transformId, transformConfig); + await this.startTransform(transformId); if (transformConfig.sync === undefined) { // batch mode - await this.waitForBatchTransformToComplete(transformConfig.id); + await this.waitForBatchTransformToComplete(transformId); } else { // continuous mode - await this.waitForTransformState(transformConfig.id, TRANSFORM_STATE.STARTED); + await this.waitForTransformStateNotToBe(transformId, TRANSFORM_STATE.STOPPED); } }, }; diff --git a/x-pack/test/functional/services/transform/transform_table.ts b/x-pack/test/functional/services/transform/transform_table.ts index 77e52b642261..cc360379f32c 100644 --- a/x-pack/test/functional/services/transform/transform_table.ts +++ b/x-pack/test/functional/services/transform/transform_table.ts @@ -174,7 +174,7 @@ export function TransformTableProvider({ getService }: FtrProviderContext) { await testSubjects.existOrFail('transformMessagesTab'); await testSubjects.click('transformMessagesTab'); await testSubjects.existOrFail('~transformMessagesTabContent'); - await retry.tryForTime(5000, async () => { + await retry.tryForTime(30 * 1000, async () => { const actualText = await testSubjects.getVisibleText('~transformMessagesTabContent'); expect(actualText.includes(expectedText)).to.eql( true, diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts index dd81c860e9fa..5c42c1978a0b 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts @@ -21,7 +21,7 @@ export class AlertingFixturePlugin implements Plugin { diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts index 0b5656004492..2e3c55f029d2 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts @@ -8,12 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; -import { - createRequest, - expectResponses, - getUrlPrefix, - getTestTitle, -} from '../lib/saved_object_test_utils'; +import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; import { ExpectResponseBody, TestCase, TestDefinition, TestSuite } from '../lib/types'; export interface BulkUpdateTestDefinition extends TestDefinition { @@ -21,6 +16,7 @@ export interface BulkUpdateTestDefinition extends TestDefinition { } export type BulkUpdateTestSuite = TestSuite; export interface BulkUpdateTestCase extends TestCase { + namespace?: string; // used to define individual "object namespace" strings, e.g., bulkUpdate across multiple namespaces failure?: 404; // only used for permitted response case } @@ -30,6 +26,12 @@ const NEW_ATTRIBUTE_VAL = `Updated attribute value ${Date.now()}`; const DOES_NOT_EXIST = Object.freeze({ type: 'dashboard', id: 'does-not-exist' }); export const TEST_CASES = Object.freeze({ ...CASES, DOES_NOT_EXIST }); +const createRequest = ({ type, id, namespace }: BulkUpdateTestCase) => ({ + type, + id, + ...(namespace && { namespace }), // individual "object namespace" string +}); + export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { const expectForbidden = expectResponses.forbiddenTypes('bulk_update'); const expectResponseBody = ( diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts index 90f72e0b3444..1e11d1fc6111 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts @@ -39,7 +39,18 @@ const createTestCases = (spaceId: string) => { ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail404() }]; const allTypes = normalTypes.concat(hiddenType); - return { normalTypes, hiddenType, allTypes }; + // an "object namespace" string can be specified for individual objects (to bulkUpdate across namespaces) + const withObjectNamespaces = [ + { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, + CASES.NAMESPACE_AGNOSTIC, // any namespace would work and would make no difference + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + return { normalTypes, hiddenType, allTypes, withObjectNamespaces }; }; export default function ({ getService }: FtrProviderContext) { @@ -51,26 +62,42 @@ export default function ({ getService }: FtrProviderContext) { supertest ); const createTests = (spaceId: string) => { - const { normalTypes, hiddenType, allTypes } = createTestCases(spaceId); + const { normalTypes, hiddenType, allTypes, withObjectNamespaces } = createTestCases(spaceId); // use singleRequest to reduce execution time and/or test combined cases + const authorizedCommon = [ + createTestDefinitions(normalTypes, false, { singleRequest: true }), + createTestDefinitions(hiddenType, true), + createTestDefinitions(allTypes, true, { + singleRequest: true, + responseBodyOverride: expectForbidden(['hiddentype']), + }), + ].flat(); return { - unauthorized: createTestDefinitions(allTypes, true), - authorized: [ - createTestDefinitions(normalTypes, false, { singleRequest: true }), - createTestDefinitions(hiddenType, true), - createTestDefinitions(allTypes, true, { - singleRequest: true, - responseBodyOverride: expectForbidden(['hiddentype']), - }), + unauthorized: [ + createTestDefinitions(allTypes, true), + createTestDefinitions(withObjectNamespaces, true, { singleRequest: true }), + ].flat(), + authorizedAtSpace: [ + authorizedCommon, + createTestDefinitions(withObjectNamespaces, true, { singleRequest: true }), + ].flat(), + authorizedAllSpaces: [ + authorizedCommon, + createTestDefinitions(withObjectNamespaces, false, { singleRequest: true }), + ].flat(), + superuser: [ + createTestDefinitions(allTypes, false, { singleRequest: true }), + createTestDefinitions(withObjectNamespaces, false, { singleRequest: true }), ].flat(), - superuser: createTestDefinitions(allTypes, false, { singleRequest: true }), }; }; describe('_bulk_update', () => { getTestScenarios().securityAndSpaces.forEach(({ spaceId, users }) => { const suffix = ` within the ${spaceId} space`; - const { unauthorized, authorized, superuser } = createTests(spaceId); + const { unauthorized, authorizedAtSpace, authorizedAllSpaces, superuser } = createTests( + spaceId + ); const _addTests = (user: TestUser, tests: BulkUpdateTestDefinition[]) => { addTests(`${user.description}${suffix}`, { user, spaceId, tests }); }; @@ -85,8 +112,11 @@ export default function ({ getService }: FtrProviderContext) { ].forEach((user) => { _addTests(user, unauthorized); }); - [users.dualAll, users.allGlobally, users.allAtSpace].forEach((user) => { - _addTests(user, authorized); + [users.allAtSpace].forEach((user) => { + _addTests(user, authorizedAtSpace); + }); + [users.dualAll, users.allGlobally].forEach((user) => { + _addTests(user, authorizedAllSpaces); }); _addTests(users.superuser, superuser); }); diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts index d42eb25b81cf..39ceb5a70d1b 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SPACES } from '../../common/lib/spaces'; import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; import { TestUser } from '../../common/lib/types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -13,6 +14,11 @@ import { BulkUpdateTestDefinition, } from '../../common/suites/bulk_update'; +const { + DEFAULT: { spaceId: DEFAULT_SPACE_ID }, + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; const { fail404 } = testCaseFailures; const createTestCases = () => { @@ -30,7 +36,19 @@ const createTestCases = () => { ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail404() }]; const allTypes = normalTypes.concat(hiddenType); - return { normalTypes, hiddenType, allTypes }; + // an "object namespace" string can be specified for individual objects (to bulkUpdate across namespaces) + // even if the Spaces plugin is disabled, this should work, as `namespace` is handled by the Core API + const withObjectNamespaces = [ + { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, + CASES.NAMESPACE_AGNOSTIC, // any namespace would work and would make no difference + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + return { normalTypes, hiddenType, allTypes, withObjectNamespaces }; }; export default function ({ getService }: FtrProviderContext) { @@ -42,10 +60,13 @@ export default function ({ getService }: FtrProviderContext) { supertest ); const createTests = () => { - const { normalTypes, hiddenType, allTypes } = createTestCases(); + const { normalTypes, hiddenType, allTypes, withObjectNamespaces } = createTestCases(); // use singleRequest to reduce execution time and/or test combined cases return { - unauthorized: createTestDefinitions(allTypes, true), + unauthorized: [ + createTestDefinitions(allTypes, true), + createTestDefinitions(withObjectNamespaces, true, { singleRequest: true }), + ].flat(), authorized: [ createTestDefinitions(normalTypes, false, { singleRequest: true }), createTestDefinitions(hiddenType, true), @@ -53,8 +74,12 @@ export default function ({ getService }: FtrProviderContext) { singleRequest: true, responseBodyOverride: expectForbidden(['hiddentype']), }), + createTestDefinitions(withObjectNamespaces, false, { singleRequest: true }), + ].flat(), + superuser: [ + createTestDefinitions(allTypes, false, { singleRequest: true }), + createTestDefinitions(withObjectNamespaces, false, { singleRequest: true }), ].flat(), - superuser: createTestDefinitions(allTypes, false, { singleRequest: true }), }; }; diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts index 93e44e357918..b51ec303fadf 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts @@ -16,22 +16,37 @@ const { } = SPACES; const { fail404 } = testCaseFailures; -const createTestCases = (spaceId: string) => [ +const createTestCases = (spaceId: string) => { // for each outcome, if failure !== undefined then we expect to receive // an error; otherwise, we expect to receive a success result - { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, - { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, - { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, - { - ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, - ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), - }, - { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, - { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, - CASES.NAMESPACE_AGNOSTIC, - { ...CASES.HIDDEN, ...fail404() }, - { ...CASES.DOES_NOT_EXIST, ...fail404() }, -]; + const normal = [ + { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, + { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + { + ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), + }, + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.NAMESPACE_AGNOSTIC, + { ...CASES.HIDDEN, ...fail404() }, + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + + // an "object namespace" string can be specified for individual objects (to bulkUpdate across namespaces) + const withObjectNamespaces = [ + { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, + { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, + CASES.NAMESPACE_AGNOSTIC, // any namespace would work and would make no difference + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + return { normal, withObjectNamespaces }; +}; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -39,8 +54,11 @@ export default function ({ getService }: FtrProviderContext) { const { addTests, createTestDefinitions } = bulkUpdateTestSuiteFactory(esArchiver, supertest); const createTests = (spaceId: string) => { - const testCases = createTestCases(spaceId); - return createTestDefinitions(testCases, false, { singleRequest: true }); + const { normal, withObjectNamespaces } = createTestCases(spaceId); + return [ + createTestDefinitions(normal, false, { singleRequest: true }), + createTestDefinitions(withObjectNamespaces, false, { singleRequest: true }), + ].flat(); }; describe('_bulk_update', () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index ebd5ff0afee7..b0b8d1410800 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { deleteMetadataCurrentStream, deleteMetadataStream, + deleteAllDocsFromMetadataCurrentIndex, } from '../../../security_solution_endpoint_api_int/apis/data_stream_helper'; export default ({ getPageObjects, getService }: FtrProviderContext) => { @@ -27,6 +28,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'IP Address', 'Version', 'Last Active', + 'Actions', ], [ 'rezzani-7.example.com', @@ -37,6 +39,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '10.101.149.26, 2606:a000:ffc0:39:11ef:37b9:3371:578c', '6.8.0', 'Jan 24, 2020 @ 16:06:09.541', + '', ], [ 'cadmann-4.example.com', @@ -47,6 +50,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '10.192.213.130, 10.70.28.129', '6.6.1', 'Jan 24, 2020 @ 16:06:09.541', + '', ], [ 'thurlow-9.example.com', @@ -57,6 +61,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '10.46.229.234', '6.0.0', 'Jan 24, 2020 @ 16:06:09.541', + '', ], ]; @@ -68,11 +73,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await deleteMetadataStream(getService); await deleteMetadataCurrentStream(getService); + await deleteAllDocsFromMetadataCurrentIndex(getService); await pageObjects.endpoint.navigateToEndpointList(); }); after(async () => { await deleteMetadataStream(getService); await deleteMetadataCurrentStream(getService); + await deleteAllDocsFromMetadataCurrentIndex(getService); }); it('finds no data in list and prompts onboarding to add policy', async () => { @@ -80,8 +87,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('finds data after load and polling', async () => { - await esArchiver.load('endpoint/metadata/api_feature', { useCreate: true }); - await pageObjects.endpoint.waitForTableToHaveData('endpointListTable', 120000); + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); + await pageObjects.endpoint.waitForTableToHaveData('endpointListTable', 1100); const tableData = await pageObjects.endpointPageUtils.tableData('endpointListTable'); expect(tableData).to.eql(expectedData); }); @@ -89,13 +96,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('when there is data,', () => { before(async () => { - await esArchiver.load('endpoint/metadata/api_feature', { useCreate: true }); - await sleep(120000); + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); await pageObjects.endpoint.navigateToEndpointList(); }); after(async () => { await deleteMetadataStream(getService); await deleteMetadataCurrentStream(getService); + await deleteAllDocsFromMetadataCurrentIndex(getService); }); it('finds page title', async () => { @@ -212,6 +219,95 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); + describe('displays the correct table data for the kql queries', () => { + before(async () => { + await esArchiver.load('endpoint/metadata/destination_index', { useCreate: true }); + await pageObjects.endpoint.navigateToEndpointList(); + }); + after(async () => { + await deleteMetadataStream(getService); + await deleteMetadataCurrentStream(getService); + await deleteAllDocsFromMetadataCurrentIndex(getService); + }); + it('for the kql query: na, table shows an empty list', async () => { + await testSubjects.setValue('adminSearchBar', 'na'); + await (await testSubjects.find('querySubmitButton')).click(); + const expectedDataFromQuery = [ + [ + 'Hostname', + 'Agent Status', + 'Integration', + 'Configuration Status', + 'Operating System', + 'IP Address', + 'Version', + 'Last Active', + 'Actions', + ], + ['No items found'], + ]; + + await pageObjects.endpoint.waitForTableToNotHaveData('endpointListTable'); + const tableData = await pageObjects.endpointPageUtils.tableData('endpointListTable'); + expect(tableData).to.eql(expectedDataFromQuery); + }); + + it('for the kql query: HostDetails.Endpoint.policy.applied.id : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A", table shows 2 items', async () => { + await testSubjects.setValue('adminSearchBar', ' '); + await (await testSubjects.find('querySubmitButton')).click(); + + const endpointListTableTotal = await testSubjects.getVisibleText('endpointListTableTotal'); + + await testSubjects.setValue( + 'adminSearchBar', + 'HostDetails.Endpoint.policy.applied.id : "C2A9093E-E289-4C0A-AA44-8C32A414FA7A" ' + ); + await (await testSubjects.find('querySubmitButton')).click(); + const expectedDataFromQuery = [ + [ + 'Hostname', + 'Agent Status', + 'Integration', + 'Configuration Status', + 'Operating System', + 'IP Address', + 'Version', + 'Last Active', + 'Actions', + ], + [ + 'cadmann-4.example.com', + 'Error', + 'Default', + 'Failure', + 'windows 10.0', + '10.192.213.130, 10.70.28.129', + '6.6.1', + 'Jan 24, 2020 @ 16:06:09.541', + '', + ], + [ + 'thurlow-9.example.com', + 'Error', + 'Default', + 'Success', + 'windows 10.0', + '10.46.229.234', + '6.0.0', + 'Jan 24, 2020 @ 16:06:09.541', + '', + ], + ]; + + await pageObjects.endpoint.waitForVisibleTextToChange( + 'endpointListTableTotal', + endpointListTableTotal + ); + const tableData = await pageObjects.endpointPageUtils.tableData('endpointListTable'); + expect(tableData).to.eql(expectedDataFromQuery); + }); + }); + describe.skip('when there is no data,', () => { before(async () => { // clear out the data and reload the page diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts index 82d844aae801..bf7ed711b75a 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/alerts.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { eventId } from '../../../../plugins/security_solution/common/endpoint/models/event'; +import { + eventIDSafeVersion, + timestampSafeVersion, +} from '../../../../plugins/security_solution/common/endpoint/models/event'; import { ResolverRelatedAlerts } from '../../../../plugins/security_solution/common/endpoint/types'; import { FtrProviderContext } from '../../ftr_provider_context'; import { @@ -69,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should allow alerts to be filtered', async () => { - const filter = `not event.id:"${tree.origin.relatedAlerts[0].event.id}"`; + const filter = `not event.id:"${tree.origin.relatedAlerts[0].event?.id}"`; const { body }: { body: ResolverRelatedAlerts } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/alerts`) .set('kbn-xsrf', 'xxx') @@ -84,7 +87,7 @@ export default function ({ getService }: FtrProviderContext) { // should not find the alert that we excluded in the filter expect( body.alerts.find((bodyAlert) => { - return eventId(bodyAlert) === tree.origin.relatedAlerts[0].event.id; + return eventIDSafeVersion(bodyAlert) === tree.origin.relatedAlerts[0].event?.id; }) ).to.not.be.ok(); }); @@ -135,14 +138,16 @@ export default function ({ getService }: FtrProviderContext) { .expect(200); const sortedAsc = [...tree.origin.relatedAlerts].sort((event1, event2) => { // this sorts the events by timestamp in ascending order - const diff = event1['@timestamp'] - event2['@timestamp']; + const diff = (timestampSafeVersion(event1) ?? 0) - (timestampSafeVersion(event2) ?? 0); + const event1ID = eventIDSafeVersion(event1) ?? 0; + const event2ID = eventIDSafeVersion(event2) ?? 0; // if the timestamps are the same, fallback to the event.id sorted in // ascending order if (diff === 0) { - if (event1.event.id < event2.event.id) { + if (event1ID < event2ID) { return -1; } - if (event1.event.id > event2.event.id) { + if (event1ID > event2ID) { return 1; } return 0; @@ -152,7 +157,7 @@ export default function ({ getService }: FtrProviderContext) { expect(body.alerts.length).to.eql(4); for (let i = 0; i < body.alerts.length; i++) { - expect(eventId(body.alerts[i])).to.equal(sortedAsc[i].event.id); + expect(eventIDSafeVersion(body.alerts[i])).to.equal(sortedAsc[i].event?.id); } }); }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts index 2dec3c755a93..49e24ff67fa7 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts @@ -5,14 +5,17 @@ */ import expect from '@kbn/expect'; import { SearchResponse } from 'elasticsearch'; -import { entityId } from '../../../../plugins/security_solution/common/endpoint/models/event'; +import { + entityIDSafeVersion, + timestampSafeVersion, +} from '../../../../plugins/security_solution/common/endpoint/models/event'; import { eventsIndexPattern } from '../../../../plugins/security_solution/common/endpoint/constants'; import { ChildrenPaginationBuilder } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/utils/children_pagination'; import { ChildrenQuery } from '../../../../plugins/security_solution/server/endpoint/routes/resolver/queries/children'; import { - ResolverTree, - ResolverEvent, - ResolverChildren, + SafeResolverTree, + SafeResolverEvent, + SafeResolverChildren, } from '../../../../plugins/security_solution/common/endpoint/types'; import { FtrProviderContext } from '../../ftr_provider_context'; import { @@ -20,6 +23,7 @@ import { EndpointDocGenerator, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; import { InsertedEvents } from '../../services/resolver'; +import { createAncestryArray } from './common'; export default function resolverAPIIntegrationTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -40,20 +44,20 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC // Origin -> infoEvent -> startEvent -> execEvent origin = generator.generateEvent(); infoEvent = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), eventType: ['info'], }); startEvent = generator.generateEvent({ - parentEntityID: infoEvent.process.entity_id, - ancestry: [infoEvent.process.entity_id, origin.process.entity_id], + parentEntityID: entityIDSafeVersion(infoEvent), + ancestry: createAncestryArray([infoEvent, origin]), eventType: ['start'], }); execEvent = generator.generateEvent({ - parentEntityID: startEvent.process.entity_id, - ancestry: [startEvent.process.entity_id, infoEvent.process.entity_id], + parentEntityID: entityIDSafeVersion(startEvent), + ancestry: createAncestryArray([startEvent, infoEvent]), eventType: ['change'], }); genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); @@ -64,13 +68,13 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC }); it('finds all the children of the origin', async () => { - const { body }: { body: ResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process.entity_id}?children=100`) + const { body }: { body: SafeResolverTree } = await supertest + .get(`/api/endpoint/resolver/${origin.process?.entity_id}?children=100`) .expect(200); expect(body.children.childNodes.length).to.be(3); - expect(body.children.childNodes[0].entityID).to.be(infoEvent.process.entity_id); - expect(body.children.childNodes[1].entityID).to.be(startEvent.process.entity_id); - expect(body.children.childNodes[2].entityID).to.be(execEvent.process.entity_id); + expect(body.children.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); + expect(body.children.childNodes[1].entityID).to.be(startEvent.process?.entity_id); + expect(body.children.childNodes[2].entityID).to.be(execEvent.process?.entity_id); }); }); @@ -86,23 +90,23 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC // Origin -> (infoEvent, startEvent, execEvent are all for the same node) origin = generator.generateEvent(); startEvent = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), eventType: ['start'], }); infoEvent = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], - entityID: startEvent.process.entity_id, + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), + entityID: entityIDSafeVersion(startEvent), eventType: ['info'], }); execEvent = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), eventType: ['change'], - entityID: startEvent.process.entity_id, + entityID: entityIDSafeVersion(startEvent), }); genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); }); @@ -117,12 +121,12 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC eventsIndexPattern ); // [1] here gets the body portion of the array - const [, query] = childrenQuery.buildMSearch(origin.process.entity_id); - const { body } = await es.search>({ body: query }); + const [, query] = childrenQuery.buildMSearch(entityIDSafeVersion(origin) ?? ''); + const { body } = await es.search>({ body: query }); expect(body.hits.hits.length).to.be(1); const event = body.hits.hits[0]._source; - expect(entityId(event)).to.be(startEvent.process.entity_id); + expect(entityIDSafeVersion(event)).to.be(startEvent.process?.entity_id); expect(event.event?.type).to.eql(['start']); }); }); @@ -139,25 +143,25 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC // Origin -> (infoEvent, startEvent, execEvent are all for the same node) origin = generator.generateEvent(); startEvent = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), eventType: ['start'], }); infoEvent = generator.generateEvent({ - timestamp: startEvent['@timestamp'] + 100, - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], - entityID: startEvent.process.entity_id, + timestamp: (timestampSafeVersion(startEvent) ?? 0) + 100, + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), + entityID: entityIDSafeVersion(startEvent), eventType: ['info'], }); execEvent = generator.generateEvent({ - timestamp: infoEvent['@timestamp'] + 100, - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + timestamp: (timestampSafeVersion(infoEvent) ?? 0) + 100, + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), eventType: ['change'], - entityID: startEvent.process.entity_id, + entityID: entityIDSafeVersion(startEvent), }); genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); }); @@ -167,37 +171,37 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC }); it('retrieves the same node three times', async () => { - let { body }: { body: ResolverChildren } = await supertest - .get(`/api/endpoint/resolver/${origin.process.entity_id}/children?children=1`) + let { body }: { body: SafeResolverChildren } = await supertest + .get(`/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1`) .expect(200); expect(body.childNodes.length).to.be(1); expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(startEvent.process.entity_id); - expect(body.childNodes[0].lifecycle[0].event?.type).to.eql(startEvent.event.type); + expect(body.childNodes[0].entityID).to.be(startEvent.process?.entity_id); + expect(body.childNodes[0].lifecycle[0].event?.type).to.eql(startEvent.event?.type); ({ body } = await supertest .get( - `/api/endpoint/resolver/${origin.process.entity_id}/children?children=1&afterChild=${body.nextChild}` + `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` ) .expect(200)); expect(body.childNodes.length).to.be(1); expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(infoEvent.process.entity_id); - expect(body.childNodes[0].lifecycle[1].event?.type).to.eql(infoEvent.event.type); + expect(body.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); + expect(body.childNodes[0].lifecycle[1].event?.type).to.eql(infoEvent.event?.type); ({ body } = await supertest .get( - `/api/endpoint/resolver/${origin.process.entity_id}/children?children=1&afterChild=${body.nextChild}` + `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` ) .expect(200)); expect(body.childNodes.length).to.be(1); expect(body.nextChild).to.not.be(null); - expect(body.childNodes[0].entityID).to.be(infoEvent.process.entity_id); - expect(body.childNodes[0].lifecycle[2].event?.type).to.eql(execEvent.event.type); + expect(body.childNodes[0].entityID).to.be(infoEvent.process?.entity_id); + expect(body.childNodes[0].lifecycle[2].event?.type).to.eql(execEvent.event?.type); ({ body } = await supertest .get( - `/api/endpoint/resolver/${origin.process.entity_id}/children?children=1&afterChild=${body.nextChild}` + `/api/endpoint/resolver/${origin.process?.entity_id}/children?children=1&afterChild=${body.nextChild}` ) .expect(200)); expect(body.childNodes.length).to.be(0); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts index 92d14fb94a2d..2c59863099ae 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/common.ts @@ -6,14 +6,15 @@ import _ from 'lodash'; import expect from '@kbn/expect'; import { - ResolverChildNode, - ResolverLifecycleNode, - ResolverEvent, + SafeResolverChildNode, + SafeResolverLifecycleNode, + SafeResolverEvent, ResolverNodeStats, } from '../../../../plugins/security_solution/common/endpoint/types'; import { - parentEntityId, - eventId, + parentEntityIDSafeVersion, + entityIDSafeVersion, + eventIDSafeVersion, } from '../../../../plugins/security_solution/common/endpoint/models/event'; import { Event, @@ -23,13 +24,33 @@ import { categoryMapping, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; +/** + * Creates the ancestry array based on an array of events. The order of the ancestry array will match the order + * of the events passed in. + * + * @param events an array of generated events + */ +export const createAncestryArray = (events: Event[]) => { + const ancestry: string[] = []; + for (const event of events) { + const entityID = entityIDSafeVersion(event); + if (entityID) { + ancestry.push(entityID); + } + } + return ancestry; +}; + /** * Check that the given lifecycle is in the resolver tree's corresponding map * * @param node a lifecycle node containing the start and end events for a node * @param nodeMap a map of entity_ids to nodes to look for the passed in `node` */ -const expectLifecycleNodeInMap = (node: ResolverLifecycleNode, nodeMap: Map) => { +const expectLifecycleNodeInMap = ( + node: SafeResolverLifecycleNode, + nodeMap: Map +) => { const genNode = nodeMap.get(node.entityID); expect(genNode).to.be.ok(); compareArrays(genNode!.lifecycle, node.lifecycle, true); @@ -44,7 +65,7 @@ const expectLifecycleNodeInMap = (node: ResolverLifecycleNode, nodeMap: Map { @@ -52,7 +73,7 @@ export const verifyAncestry = ( const groupedAncestors = _.groupBy(ancestors, (ancestor) => ancestor.entityID); // group by parent entity_id const groupedAncestorsParent = _.groupBy(ancestors, (ancestor) => - parentEntityId(ancestor.lifecycle[0]) + parentEntityIDSafeVersion(ancestor.lifecycle[0]) ); // make sure there aren't any nodes with the same entity_id expect(Object.keys(groupedAncestors).length).to.eql(ancestors.length); @@ -69,7 +90,7 @@ export const verifyAncestry = ( let foundParents = 0; let node = ancestors[0]; for (let i = 0; i < ancestors.length; i++) { - const parentID = parentEntityId(node.lifecycle[0]); + const parentID = parentEntityIDSafeVersion(node.lifecycle[0]); if (parentID !== undefined) { const nextNode = groupedAncestors[parentID]; if (!nextNode) { @@ -95,12 +116,12 @@ export const verifyAncestry = ( * * @param ancestors an array of ancestor nodes */ -export const retrieveDistantAncestor = (ancestors: ResolverLifecycleNode[]) => { +export const retrieveDistantAncestor = (ancestors: SafeResolverLifecycleNode[]) => { // group the ancestors by their entity_id mapped to a lifecycle node const groupedAncestors = _.groupBy(ancestors, (ancestor) => ancestor.entityID); let node = ancestors[0]; for (let i = 0; i < ancestors.length; i++) { - const parentID = parentEntityId(node.lifecycle[0]); + const parentID = parentEntityIDSafeVersion(node.lifecycle[0]); if (parentID !== undefined) { const nextNode = groupedAncestors[parentID]; if (nextNode) { @@ -122,7 +143,7 @@ export const retrieveDistantAncestor = (ancestors: ResolverLifecycleNode[]) => { * @param childrenPerParent an optional number to compare that there are a certain number of children for each parent */ export const verifyChildren = ( - children: ResolverChildNode[], + children: SafeResolverChildNode[], tree: Tree, numberOfParents?: number, childrenPerParent?: number @@ -132,7 +153,9 @@ export const verifyChildren = ( // make sure each child is unique expect(Object.keys(groupedChildren).length).to.eql(children.length); if (numberOfParents !== undefined) { - const groupParent = _.groupBy(children, (child) => parentEntityId(child.lifecycle[0])); + const groupParent = _.groupBy(children, (child) => + parentEntityIDSafeVersion(child.lifecycle[0]) + ); expect(Object.keys(groupParent).length).to.eql(numberOfParents); if (childrenPerParent !== undefined) { Object.values(groupParent).forEach((childNodes) => @@ -155,7 +178,7 @@ export const verifyChildren = ( */ export const compareArrays = ( expected: Event[], - toTest: ResolverEvent[], + toTest: SafeResolverEvent[], lengthCheck: boolean = false ) => { if (lengthCheck) { @@ -168,7 +191,7 @@ export const compareArrays = ( // we're only checking that the event ids are the same here. The reason we can't check the entire document // is because ingest pipelines are used to add fields to the document when it is received by elasticsearch, // therefore it will not be the same as the document created by the generator - return eventId(toTestEvent) === eventId(arrEvent); + return eventIDSafeVersion(toTestEvent) === eventIDSafeVersion(arrEvent); }) ).to.be.ok(); }); @@ -212,7 +235,7 @@ export const verifyStats = ( * @param categories the related event info used when generating the resolver tree */ export const verifyLifecycleStats = ( - nodes: ResolverLifecycleNode[], + nodes: SafeResolverLifecycleNode[], categories: RelatedEventInfo[], relatedAlerts: number ) => { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts index cb6c49e17c71..e6d5e8fccd00 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; +import { entityIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event'; import { eventsIndexPattern } from '../../../../plugins/security_solution/common/endpoint/constants'; import { - ResolverTree, + SafeResolverTree, ResolverEntityIndex, } from '../../../../plugins/security_solution/common/endpoint/types'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -15,19 +16,26 @@ import { Event, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; import { InsertedEvents } from '../../services/resolver'; +import { createAncestryArray } from './common'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const resolver = getService('resolverGenerator'); const generator = new EndpointDocGenerator('resolver'); + const setEntityIDEmptyString = (event: Event) => { + if (event.process?.entity_id) { + event.process.entity_id = ''; + } + }; + describe('Resolver handling of entity ids', () => { describe('entity api', () => { let origin: Event; let genData: InsertedEvents; before(async () => { origin = generator.generateEvent({ parentEntityID: 'a' }); - origin.process.entity_id = ''; + setEntityIDEmptyString(origin); genData = await resolver.insertEvents([origin]); }); @@ -57,16 +65,16 @@ export default function ({ getService }: FtrProviderContext) { // should not be returned by the backend. origin = generator.generateEvent({ entityID: 'a' }); childNoEntityID = generator.generateEvent({ - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), }); // force it to be empty - childNoEntityID.process.entity_id = ''; + setEntityIDEmptyString(childNoEntityID); childWithEntityID = generator.generateEvent({ entityID: 'b', - parentEntityID: origin.process.entity_id, - ancestry: [origin.process.entity_id], + parentEntityID: entityIDSafeVersion(origin), + ancestry: createAncestryArray([origin]), }); events = [origin, childNoEntityID, childWithEntityID]; genData = await resolver.insertEvents(events); @@ -77,11 +85,11 @@ export default function ({ getService }: FtrProviderContext) { }); it('does not find children without a process entity_id', async () => { - const { body }: { body: ResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process.entity_id}`) + const { body }: { body: SafeResolverTree } = await supertest + .get(`/api/endpoint/resolver/${origin.process?.entity_id}`) .expect(200); expect(body.children.childNodes.length).to.be(1); - expect(body.children.childNodes[0].entityID).to.be(childWithEntityID.process.entity_id); + expect(body.children.childNodes[0].entityID).to.be(childWithEntityID.process?.entity_id); }); }); @@ -101,21 +109,21 @@ export default function ({ getService }: FtrProviderContext) { }); ancestor1 = generator.generateEvent({ entityID: '1', - parentEntityID: ancestor2.process.entity_id, - ancestry: [ancestor2.process.entity_id], + parentEntityID: entityIDSafeVersion(ancestor2), + ancestry: createAncestryArray([ancestor2]), }); // we'll insert an event that doesn't have an entity id so if the backend does search for it, it should be // returned and our test should fail ancestorNoEntityID = generator.generateEvent({ - ancestry: [ancestor2.process.entity_id], + ancestry: createAncestryArray([ancestor2]), }); - ancestorNoEntityID.process.entity_id = ''; + setEntityIDEmptyString(ancestorNoEntityID); origin = generator.generateEvent({ entityID: 'a', - parentEntityID: ancestor1.process.entity_id, - ancestry: ['', ancestor2.process.entity_id], + parentEntityID: entityIDSafeVersion(ancestor1), + ancestry: ['', ...createAncestryArray([ancestor2])], }); events = [origin, ancestor1, ancestor2, ancestorNoEntityID]; @@ -127,11 +135,11 @@ export default function ({ getService }: FtrProviderContext) { }); it('does not query for ancestors that have an empty string for the entity_id', async () => { - const { body }: { body: ResolverTree } = await supertest - .get(`/api/endpoint/resolver/${origin.process.entity_id}`) + const { body }: { body: SafeResolverTree } = await supertest + .get(`/api/endpoint/resolver/${origin.process?.entity_id}`) .expect(200); expect(body.ancestry.ancestors.length).to.be(1); - expect(body.ancestry.ancestors[0].entityID).to.be(ancestor2.process.entity_id); + expect(body.ancestry.ancestors[0].entityID).to.be(ancestor2.process?.entity_id); }); }); }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts index c0e4e466c7b6..4e248f52ec29 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { eventId } from '../../../../plugins/security_solution/common/endpoint/models/event'; -import { ResolverRelatedEvents } from '../../../../plugins/security_solution/common/endpoint/types'; +import { eventIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event'; +import { SafeResolverRelatedEvents } from '../../../../plugins/security_solution/common/endpoint/types'; import { FtrProviderContext } from '../../ftr_provider_context'; import { Tree, @@ -59,7 +59,7 @@ export default function ({ getService }: FtrProviderContext) { const cursor = 'eyJ0aW1lc3RhbXAiOjE1ODE0NTYyNTUwMDAsImV2ZW50SUQiOiI5NDA0MyJ9'; it('should return details for the root node', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -69,7 +69,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns no values when there is no more data', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest // after is set to the document id of the last event so there shouldn't be any more after it .post( `/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=${cursor}` @@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return the first page of information when the cursor is invalid', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post( `/api/endpoint/resolver/${entityID}/events?legacyEndpointID=${endpointID}&afterEvent=blah` ) @@ -93,7 +93,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return no results for an invalid endpoint ID', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${entityID}/events?legacyEndpointID=foo`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -120,7 +120,7 @@ export default function ({ getService }: FtrProviderContext) { describe('endpoint events', () => { it('should not find any events', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/5555/events`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -129,7 +129,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return details for the root node', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/events`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -140,7 +140,7 @@ export default function ({ getService }: FtrProviderContext) { it('should allow for the events to be filtered', async () => { const filter = `event.category:"${RelatedEventCategory.Driver}"`; - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/events`) .set('kbn-xsrf', 'xxx') .send({ @@ -156,7 +156,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return paginated results for the root node', async () => { - let { body }: { body: ResolverRelatedEvents } = await supertest + let { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/events?events=2`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -185,7 +185,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return the first page of information when the cursor is invalid', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/events?afterEvent=blah`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -195,7 +195,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should sort the events in descending order', async () => { - const { body }: { body: ResolverRelatedEvents } = await supertest + const { body }: { body: SafeResolverRelatedEvents } = await supertest .post(`/api/endpoint/resolver/${tree.origin.id}/events`) .set('kbn-xsrf', 'xxx') .expect(200); @@ -204,8 +204,8 @@ export default function ({ getService }: FtrProviderContext) { // the last element in the array so let's reverse it const relatedEvents = tree.origin.relatedEvents.reverse(); for (let i = 0; i < body.events.length; i++) { - expect(body.events[i].event?.category).to.equal(relatedEvents[i].event.category); - expect(eventId(body.events[i])).to.equal(relatedEvents[i].event.id); + expect(body.events[i].event?.category).to.equal(relatedEvents[i].event?.category); + expect(eventIDSafeVersion(body.events[i])).to.equal(relatedEvents[i].event?.id); } }); }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts index 957d559087f5..837af6a940f5 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/tree.ts @@ -5,12 +5,12 @@ */ import expect from '@kbn/expect'; import { - ResolverAncestry, - ResolverChildren, - ResolverTree, - LegacyEndpointEvent, + SafeResolverAncestry, + SafeResolverChildren, + SafeResolverTree, + SafeLegacyEndpointEvent, } from '../../../../plugins/security_solution/common/endpoint/types'; -import { parentEntityId } from '../../../../plugins/security_solution/common/endpoint/models/event'; +import { parentEntityIDSafeVersion } from '../../../../plugins/security_solution/common/endpoint/models/event'; import { FtrProviderContext } from '../../ftr_provider_context'; import { Tree, @@ -71,7 +71,7 @@ export default function ({ getService }: FtrProviderContext) { const entityID = '94042'; it('should return details for the root node', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get( `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=5` ) @@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should have a populated next parameter', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get( `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=0` ) @@ -91,7 +91,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should handle an ancestors param request', async () => { - let { body }: { body: ResolverAncestry } = await supertest + let { body }: { body: SafeResolverAncestry } = await supertest .get( `/api/endpoint/resolver/${entityID}/ancestry?legacyEndpointID=${endpointID}&ancestors=0` ) @@ -110,14 +110,14 @@ export default function ({ getService }: FtrProviderContext) { describe('endpoint events', () => { it('should return the origin node at the front of the array', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=9`) .expect(200); expect(body.ancestors[0].entityID).to.eql(tree.origin.id); }); it('should return details for the root node', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=9`) .expect(200); // the tree we generated had 5 ancestors + 1 origin node @@ -128,7 +128,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('should handle an invalid id', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get(`/api/endpoint/resolver/alskdjflasj/ancestry`) .expect(200); expect(body.ancestors).to.be.empty(); @@ -136,18 +136,20 @@ export default function ({ getService }: FtrProviderContext) { }); it('should have a populated next parameter', async () => { - const { body }: { body: ResolverAncestry } = await supertest + const { body }: { body: SafeResolverAncestry } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=2`) .expect(200); // it should have 2 ancestors + 1 origin expect(body.ancestors.length).to.eql(3); verifyAncestry(body.ancestors, tree, false); const distantGrandparent = retrieveDistantAncestor(body.ancestors); - expect(body.nextAncestor).to.eql(parentEntityId(distantGrandparent.lifecycle[0])); + expect(body.nextAncestor).to.eql( + parentEntityIDSafeVersion(distantGrandparent.lifecycle[0]) + ); }); it('should handle multiple ancestor requests', async () => { - let { body }: { body: ResolverAncestry } = await supertest + let { body }: { body: SafeResolverAncestry } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/ancestry?ancestors=3`) .expect(200); expect(body.ancestors.length).to.eql(4); @@ -171,7 +173,7 @@ export default function ({ getService }: FtrProviderContext) { const entityID = '94041'; it('returns child process lifecycle events', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}`) .expect(200); expect(body.childNodes.length).to.eql(1); @@ -179,12 +181,12 @@ export default function ({ getService }: FtrProviderContext) { expect( // for some reason the ts server doesn't think `endgame` exists even though we're using ResolverEvent // here, so to avoid it complaining we'll just force it - (body.childNodes[0].lifecycle[0] as LegacyEndpointEvent).endgame.unique_pid + (body.childNodes[0].lifecycle[0] as SafeLegacyEndpointEvent).endgame.unique_pid ).to.eql(94042); }); it('returns multiple levels of child process lifecycle events', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/93802/children?legacyEndpointID=${endpointID}&children=10`) .expect(200); expect(body.childNodes.length).to.eql(10); @@ -193,12 +195,12 @@ export default function ({ getService }: FtrProviderContext) { expect( // for some reason the ts server doesn't think `endgame` exists even though we're using ResolverEvent // here, so to avoid it complaining we'll just force it - (body.childNodes[0].lifecycle[0] as LegacyEndpointEvent).endgame.unique_pid + (body.childNodes[0].lifecycle[0] as SafeLegacyEndpointEvent).endgame.unique_pid ).to.eql(93932); }); it('returns no values when there is no more data', async () => { - let { body }: { body: ResolverChildren } = await supertest + let { body }: { body: SafeResolverChildren } = await supertest .get( // there should only be a single child for this node `/api/endpoint/resolver/94041/children?legacyEndpointID=${endpointID}&children=1` @@ -216,7 +218,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns the first page of information when the cursor is invalid', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get( `/api/endpoint/resolver/${entityID}/children?legacyEndpointID=${endpointID}&afterChild=blah` ) @@ -236,7 +238,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns empty events without a matching entity id', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/5555/children`) .expect(200); expect(body.nextChild).to.eql(null); @@ -244,7 +246,7 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns empty events with an invalid endpoint id', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${entityID}/children?legacyEndpointID=foo`) .expect(200); expect(body.nextChild).to.eql(null); @@ -254,7 +256,7 @@ export default function ({ getService }: FtrProviderContext) { describe('endpoint events', () => { it('returns all children for the origin', async () => { - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/children?children=100`) .expect(200); // there are 2 levels in the children part of the tree and 3 nodes for each = @@ -269,7 +271,7 @@ export default function ({ getService }: FtrProviderContext) { // this gets a node should have 3 children which were created in succession so that the timestamps // are ordered correctly to be retrieved in a single call const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id; - const { body }: { body: ResolverChildren } = await supertest + const { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=3`) .expect(200); expect(body.childNodes.length).to.eql(3); @@ -281,7 +283,7 @@ export default function ({ getService }: FtrProviderContext) { // this gets a node should have 3 children which were created in succession so that the timestamps // are ordered correctly to be retrieved in a single call const distantChildEntityID = Array.from(tree.childrenLevels[0].values())[0].id; - let { body }: { body: ResolverChildren } = await supertest + let { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${distantChildEntityID}/children?children=1`) .expect(200); expect(body.childNodes.length).to.eql(1); @@ -308,7 +310,7 @@ export default function ({ getService }: FtrProviderContext) { it('gets all children in two queries', async () => { // should get all the children of the origin - let { body }: { body: ResolverChildren } = await supertest + let { body }: { body: SafeResolverChildren } = await supertest .get(`/api/endpoint/resolver/${tree.origin.id}/children?children=3`) .expect(200); expect(body.childNodes.length).to.eql(3); @@ -334,7 +336,7 @@ export default function ({ getService }: FtrProviderContext) { const endpointID = '5a0c957f-b8e7-4538-965e-57e8bb86ad3a'; it('returns ancestors, events, children, and current process lifecycle', async () => { - const { body }: { body: ResolverTree } = await supertest + const { body }: { body: SafeResolverTree } = await supertest .get(`/api/endpoint/resolver/93933?legacyEndpointID=${endpointID}`) .expect(200); expect(body.ancestry.nextAncestor).to.equal(null); @@ -348,7 +350,7 @@ export default function ({ getService }: FtrProviderContext) { describe('endpoint events', () => { it('returns a tree', async () => { - const { body }: { body: ResolverTree } = await supertest + const { body }: { body: SafeResolverTree } = await supertest .get( `/api/endpoint/resolver/${tree.origin.id}?children=100&ancestors=5&events=5&alerts=5` ) diff --git a/x-pack/test/security_solution_endpoint_api_int/services/resolver.ts b/x-pack/test/security_solution_endpoint_api_int/services/resolver.ts index 7e4d4177affa..c5855281f55c 100644 --- a/x-pack/test/security_solution_endpoint_api_int/services/resolver.ts +++ b/x-pack/test/security_solution_endpoint_api_int/services/resolver.ts @@ -9,6 +9,7 @@ import { EndpointDocGenerator, Event, } from '../../../plugins/security_solution/common/endpoint/generate_data'; +import { firstNonNullValue } from '../../../plugins/security_solution/common/endpoint/models/ecs_safety_helpers'; import { FtrProviderContext } from '../ftr_provider_context'; export const processEventsIndex = 'logs-endpoint.events.process-default'; @@ -87,7 +88,7 @@ export function ResolverGeneratorProvider({ getService }: FtrProviderContext) { const tree = generator.generateTree(options); const body = tree.allEvents.reduce((array: Array, doc) => { let index = eventsIndex; - if (doc.event.kind === 'alert') { + if (firstNonNullValue(doc.event?.kind) === 'alert') { index = alertsIndex; } /** diff --git a/x-pack/test/ui_capabilities/common/fixtures/plugins/foo_plugin/server/index.ts b/x-pack/test/ui_capabilities/common/fixtures/plugins/foo_plugin/server/index.ts index 5c80b4283a69..a950b4fc3d70 100644 --- a/x-pack/test/ui_capabilities/common/fixtures/plugins/foo_plugin/server/index.ts +++ b/x-pack/test/ui_capabilities/common/fixtures/plugins/foo_plugin/server/index.ts @@ -14,7 +14,7 @@ interface SetupDeps { class FooPlugin implements Plugin { setup(core: CoreSetup, plugins: SetupDeps) { - plugins.features.registerFeature({ + plugins.features.registerKibanaFeature({ id: 'foo', name: 'Foo', icon: 'upArrow', diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts index d9c27d67ae32..dde99e7409de 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts @@ -13,6 +13,8 @@ import { UserAtSpaceScenarios } from '../scenarios'; export default function catalogueTests({ getService }: FtrProviderContext) { const uiCapabilitiesService: UICapabilitiesService = getService('uiCapabilities'); + const esFeatureExceptions = ['security', 'rollup_jobs', 'reporting', 'transform', 'watcher']; + describe('catalogue', () => { UserAtSpaceScenarios.forEach((scenario) => { it(`${scenario.id}`, async () => { @@ -35,13 +37,14 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'dual_privileges_all at everything_space': { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('catalogue'); - // everything except ml and monitoring is enabled + // everything except ml, monitoring, and ES features are enabled const expected = mapValues( uiCapabilities.value!.catalogue, (enabled, catalogueId) => catalogueId !== 'ml' && catalogueId !== 'ml_file_data_visualizer' && - catalogueId !== 'monitoring' + catalogueId !== 'monitoring' && + !esFeatureExceptions.includes(catalogueId) ); expect(uiCapabilities.value!.catalogue).to.eql(expected); break; @@ -52,7 +55,8 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'everything_space_read at everything_space': { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('catalogue'); - // everything except ml and monitoring and enterprise search is enabled + // everything except spaces, ml, monitoring, the enterprise search suite, and ES features are enabled + // (easier to say: all "proper" Kibana features are enabled) const exceptions = [ 'ml', 'ml_file_data_visualizer', @@ -60,6 +64,8 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearch', 'appSearch', 'workplaceSearch', + 'spaces', + ...esFeatureExceptions, ]; const expected = mapValues( uiCapabilities.value!.catalogue, @@ -68,10 +74,36 @@ export default function catalogueTests({ getService }: FtrProviderContext) { expect(uiCapabilities.value!.catalogue).to.eql(expected); break; } - // the nothing_space has no features enabled, so even if we have - // privileges to perform these actions, we won't be able to - case 'superuser at nothing_space': + // the nothing_space has no Kibana features enabled, so even if we have + // privileges to perform these actions, we won't be able to. + // Note that ES features may still be enabled if the user has privileges, since + // they cannot be disabled at the space level at this time. + case 'superuser at nothing_space': { + expect(uiCapabilities.success).to.be(true); + expect(uiCapabilities.value).to.have.property('catalogue'); + // everything is disabled except for the es feature exceptions and spaces management + const expected = mapValues( + uiCapabilities.value!.catalogue, + (enabled, catalogueId) => + esFeatureExceptions.includes(catalogueId) || catalogueId === 'spaces' + ); + expect(uiCapabilities.value!.catalogue).to.eql(expected); + break; + } + // the nothing_space has no Kibana features enabled, so even if we have + // privileges to perform these actions, we won't be able to. case 'global_all at nothing_space': + case 'dual_privileges_all at nothing_space': { + // everything is disabled except for spaces management + const expected = mapValues( + uiCapabilities.value!.catalogue, + (enabled, catalogueId) => catalogueId === 'spaces' + ); + expect(uiCapabilities.value!.catalogue).to.eql(expected); + break; + } + // the nothing_space has no Kibana features enabled, so even if we have + // privileges to perform these actions, we won't be able to. case 'global_read at nothing_space': case 'dual_privileges_all at nothing_space': case 'dual_privileges_read at nothing_space': @@ -88,7 +120,10 @@ export default function catalogueTests({ getService }: FtrProviderContext) { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('catalogue'); // everything is disabled - const expected = mapValues(uiCapabilities.value!.catalogue, () => false); + const expected = mapValues( + uiCapabilities.value!.catalogue, + (enabled, catalogueId) => false + ); expect(uiCapabilities.value!.catalogue).to.eql(expected); break; } diff --git a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts index 7852167fcc1c..1f19228b2d95 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts @@ -13,6 +13,8 @@ import { UserScenarios } from '../scenarios'; export default function catalogueTests({ getService }: FtrProviderContext) { const uiCapabilitiesService: UICapabilitiesService = getService('uiCapabilities'); + const esFeatureExceptions = ['security', 'rollup_jobs', 'reporting', 'transform', 'watcher']; + describe('catalogue', () => { UserScenarios.forEach((scenario) => { it(`${scenario.fullName}`, async () => { @@ -35,13 +37,14 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'dual_privileges_all': { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('catalogue'); - // everything except ml and monitoring is enabled + // everything except ml, monitoring, and ES features are enabled const expected = mapValues( uiCapabilities.value!.catalogue, (enabled, catalogueId) => catalogueId !== 'ml' && + catalogueId !== 'monitoring' && catalogueId !== 'ml_file_data_visualizer' && - catalogueId !== 'monitoring' + !esFeatureExceptions.includes(catalogueId) ); expect(uiCapabilities.value!.catalogue).to.eql(expected); break; @@ -58,6 +61,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearch', 'appSearch', 'workplaceSearch', + ...esFeatureExceptions, ]; const expected = mapValues( uiCapabilities.value!.catalogue, diff --git a/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts b/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts index 2ef510840342..baae3286ddb5 100644 --- a/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/spaces_only/tests/catalogue.ts @@ -13,6 +13,8 @@ import { SpaceScenarios } from '../scenarios'; export default function catalogueTests({ getService }: FtrProviderContext) { const uiCapabilitiesService: UICapabilitiesService = getService('uiCapabilities'); + const esFeatureExceptions = ['security', 'rollup_jobs', 'reporting', 'transform', 'watcher']; + describe('catalogue', () => { SpaceScenarios.forEach((scenario) => { it(`${scenario.name}`, async () => { @@ -29,8 +31,12 @@ export default function catalogueTests({ getService }: FtrProviderContext) { case 'nothing_space': { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('catalogue'); - // everything is disabled - const expected = mapValues(uiCapabilities.value!.catalogue, () => false); + // everything is disabled except for ES features and spaces management + const expected = mapValues( + uiCapabilities.value!.catalogue, + (enabled, catalogueId) => + esFeatureExceptions.includes(catalogueId) || catalogueId === 'spaces' + ); expect(uiCapabilities.value!.catalogue).to.eql(expected); break; } diff --git a/yarn.lock b/yarn.lock index 105c5e3cba5a..ddecaf17f7bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4123,11 +4123,6 @@ "@types/node" "*" "@types/webpack" "*" -"@types/lodash@^3.10.1": - version "3.10.3" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-3.10.3.tgz#aaddec6a3c93bf03b402db3acf5d4c77bce8bdff" - integrity sha512-b9zScBKmB/RJqETbxu3YRya61vJOik89/lR+NdxjZAFMDcMSjwX6IhQoP4terJkhsa9TE1C+l6XwxCkhhsaZXg== - "@types/lodash@^4.14.116", "@types/lodash@^4.14.159": version "4.14.159" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.159.tgz#61089719dc6fdd9c5cb46efc827f2571d1517065" @@ -5706,11 +5701,6 @@ ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" - integrity sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs= - ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -8041,15 +8031,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" - integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= - dependencies: - camelcase "^4.1.0" - map-obj "^2.0.0" - quick-lru "^1.0.0" - camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -8074,7 +8055,7 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= @@ -8739,15 +8720,6 @@ cliui@^3.0.3, cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" - integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -10347,7 +10319,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: +decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -10548,13 +10520,13 @@ defined@^1.0.0, defined@~1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= -del-cli@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/del-cli/-/del-cli-3.0.0.tgz#327a15d4c18d6b7e5c849a53ef0d17901bc28197" - integrity sha512-J4HDC2mpcN5aopya4VdkyiFXZaqAoo7ua9VpKbciX3DDUSbtJbPMc3ivggJsAAgS6EqonmbenIiMhBGtJPW9FA== +del-cli@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/del-cli/-/del-cli-3.0.1.tgz#2d27ff260204b5104cadeda86f78f180a4ebe89a" + integrity sha512-BLHItGr82rUbHhjMu41d+vw9Md49i81jmZSV00HdTq4t+RTHywmEht/23mNFpUl2YeLYJZJyGz4rdlMAyOxNeg== dependencies: del "^5.1.0" - meow "^5.0.0" + meow "^6.1.1" del@^2.0.2: version "2.2.2" @@ -11860,17 +11832,6 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" -eslint-formatter-pretty@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-1.3.0.tgz#985d9e41c1f8475f4a090c5dbd2dfcf2821d607e" - integrity sha512-5DY64Y1rYCm7cfFDHEGUn54bvCnK+wSUVF07N8oXeqUJFSd+gnYOTXbzelQ1HurESluY6gnEQPmXOIkB4Wa+gA== - dependencies: - ansi-escapes "^2.0.0" - chalk "^2.1.0" - log-symbols "^2.0.0" - plur "^2.1.2" - string-width "^2.0.0" - eslint-formatter-pretty@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-4.0.0.tgz#dc15f3bf4fb51b7ba5fbedb77f57ba8841140ce2" @@ -12331,19 +12292,6 @@ execa@^0.1.1: object-assign "^4.0.1" strip-eof "^1.0.0" -execa@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" - integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== - dependencies: - cross-spawn "^6.0.0" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.4.0.tgz#4eb6467a36a095fabb2970ff9d5e3fb7bce6ebc3" @@ -14190,7 +14138,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^9.1.0, globby@^9.2.0: +globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== @@ -16202,11 +16150,6 @@ iron@5.x.x: cryptiles "4.x.x" hoek "5.x.x" -irregular-plurals@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" - integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= - irregular-plurals@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.2.0.tgz#b19c490a0723798db51b235d7e39add44dab0822" @@ -18753,7 +18696,7 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= -lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.2.0: +lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= @@ -19023,11 +18966,6 @@ lodash@^3.10.1: resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= -"lodash@npm:@elastic/lodash@3.10.1-kibana4": - version "3.10.1-kibana4" - resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana4.tgz#d491228fd659b4a1b0dfa08ba9c67a4979b9746d" - integrity sha512-geQqXd9ZedRCL+kq5cpeahYWYaYRV0BMXhCwzq4DpnGCVs430FTMS3Wcot3XChZZhCvkwHm15bpNjB312vPxaA== - log-ok@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/log-ok/-/log-ok-0.1.1.tgz#bea3dd36acd0b8a7240d78736b5b97c65444a334" @@ -19036,7 +18974,7 @@ log-ok@^0.1.1: ansi-green "^0.1.1" success-symbol "^0.1.0" -log-symbols@2.2.0, log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0: +log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== @@ -19338,11 +19276,6 @@ map-obj@^1.0.0, map-obj@^1.0.1: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= -map-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" - integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= - map-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" @@ -19646,20 +19579,22 @@ meow@^3.0.0, meow@^3.3.0, meow@^3.7.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" - integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== +meow@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.1.tgz#1ad64c4b76b2a24dfb2f635fddcadf320d251467" + integrity sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg== dependencies: - camelcase-keys "^4.0.0" - decamelize-keys "^1.0.0" - loud-rejection "^1.0.0" - minimist-options "^3.0.1" - normalize-package-data "^2.3.4" - read-pkg-up "^3.0.0" - redent "^2.0.0" - trim-newlines "^2.0.0" - yargs-parser "^10.0.0" + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "^4.0.2" + normalize-package-data "^2.5.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.13.1" + yargs-parser "^18.1.3" meow@^7.0.1: version "7.0.1" @@ -19907,14 +19842,6 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimist-options@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" - integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -20109,15 +20036,15 @@ mocha@^7.1.1: yargs-parser "13.1.2" yargs-unparser "1.6.0" -mochawesome-merge@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mochawesome-merge/-/mochawesome-merge-2.0.1.tgz#c690433acc78fd769effe4db1a107508351e2dc5" - integrity sha512-QRYok/9y9MJ4zlWGajC/OV6BxjUGyv1AYX3DBOPSbpzk09p2dFBWV1QYSN/dHu7bo/q44ZGmOBHO8ZnAyI+Yug== +mochawesome-merge@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mochawesome-merge/-/mochawesome-merge-4.1.0.tgz#25a514460c6e106e2c8399daaec2d085b6e89b56" + integrity sha512-cDMzSmYu1dRKcr+ZrjjUEuXSiirU8LTG6R8hrAPlZ7zy1EeL7LLpi+a156obxzqh8quTWmYxKtUbTF2PQt0l7A== dependencies: fs-extra "^7.0.1" - minimatch "^3.0.4" + glob "^7.1.6" uuid "^3.3.2" - yargs "^12.0.5" + yargs "^15.3.1" mochawesome-report-generator@^4.0.0: version "4.0.1" @@ -21556,15 +21483,6 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.0.1.tgz#3b014fbf01d87f60a1e5348d80fe870dc82c4620" - integrity sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw== - dependencies: - execa "^0.10.0" - lcid "^2.0.0" - mem "^4.0.0" - os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" @@ -22404,13 +22322,6 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" -plur@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" - integrity sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo= - dependencies: - irregular-plurals "^1.0.0" - plur@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" @@ -23123,11 +23034,6 @@ queue@6.0.1: dependencies: inherits "~2.0.3" -quick-lru@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" - integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= - quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -24149,14 +24055,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -24386,14 +24284,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -redent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" - integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= - dependencies: - indent-string "^3.0.0" - strip-indent "^2.0.0" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -27023,11 +26913,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -28122,11 +28007,6 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= -trim-newlines@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" - integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= - trim-newlines@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" @@ -28233,19 +28113,6 @@ tsd@^0.13.1: read-pkg-up "^7.0.0" update-notifier "^4.1.0" -tsd@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.7.4.tgz#d9aba567f1394641821a6800dcee60746c87bd03" - integrity sha512-cqr1s2GHtVkU3L/4BXDaeJOjFEuZ7iOVC+hwmyx4G7Eo26mSXCFNnwFm4EasK/MW2HdY3AQWux+AjYzDYLzZow== - dependencies: - eslint-formatter-pretty "^1.3.0" - globby "^9.1.0" - meow "^5.0.0" - path-exists "^3.0.0" - read-pkg-up "^4.0.0" - typescript "^3.0.1" - update-notifier "^2.5.0" - tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -28412,7 +28279,7 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.0.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: +typescript@4.0.2, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: version "4.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== @@ -30240,11 +30107,6 @@ window-size@^0.1.4: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY= -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= - windows-release@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" @@ -30614,7 +30476,7 @@ y18n@^3.2.0, y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= -"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: +y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== @@ -30665,21 +30527,6 @@ yargs-parser@5.0.0-security.0: camelcase "^3.0.0" object.assign "^4.1.0" -yargs-parser@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" - integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== - dependencies: - camelcase "^4.1.0" - -yargs-parser@^11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" - integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^18.1.1, yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -30688,14 +30535,6 @@ yargs-parser@^18.1.1, yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" - integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= - dependencies: - camelcase "^3.0.0" - lodash.assign "^4.0.6" - yargs-unparser@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" @@ -30738,45 +30577,7 @@ yargs@13.3.2, yargs@^13.2.2, yargs@^13.3.0, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@4.8.1: - version "4.8.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" - integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= - dependencies: - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - lodash.assign "^4.0.3" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.1" - which-module "^1.0.0" - window-size "^0.2.0" - y18n "^3.2.1" - yargs-parser "^2.4.1" - -yargs@^12.0.5: - version "12.0.5" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" - integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== - dependencies: - cliui "^4.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^1.0.1" - os-locale "^3.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1 || ^4.0.0" - yargs-parser "^11.1.1" - -yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.0: +yargs@^15.0.2, yargs@^15.1.0, yargs@^15.3.1, yargs@^15.4.0, yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==