diff --git a/.ci/teamcity/default/jest.sh b/.ci/teamcity/default/jest.sh index 93ca7f76f3a21..b900d1b6d6b4e 100755 --- a/.ci/teamcity/default/jest.sh +++ b/.ci/teamcity/default/jest.sh @@ -6,7 +6,5 @@ source "$(dirname "${0}")/../util.sh" export JOB=kibana-default-jest -cd "$XPACK_DIR" - checks-reporter-with-killswitch "Jest Unit Tests" \ - node scripts/jest --bail --debug + node scripts/jest x-pack --ci --verbose --maxWorkers=5 diff --git a/.ci/teamcity/oss/jest.sh b/.ci/teamcity/oss/jest.sh index 3ba9ab0c31c57..0dee07d00d2be 100755 --- a/.ci/teamcity/oss/jest.sh +++ b/.ci/teamcity/oss/jest.sh @@ -7,4 +7,4 @@ source "$(dirname "${0}")/../util.sh" export JOB=kibana-oss-jest checks-reporter-with-killswitch "OSS Jest Unit Tests" \ - node scripts/jest --ci --verbose + node scripts/jest --config jest.config.oss.js --ci --verbose --maxWorkers=5 diff --git a/.ci/teamcity/oss/jest_integration.sh b/.ci/teamcity/oss/jest_integration.sh index 1a23c46c8a2c2..4c51d2ff29888 100755 --- a/.ci/teamcity/oss/jest_integration.sh +++ b/.ci/teamcity/oss/jest_integration.sh @@ -7,4 +7,4 @@ source "$(dirname "${0}")/../util.sh" export JOB=kibana-oss-jest-integration checks-reporter-with-killswitch "OSS Jest Integration Tests" \ - node scripts/jest_integration --verbose + node scripts/jest_integration --ci --verbose diff --git a/docs/developer/contributing/development-functional-tests.asciidoc b/docs/developer/contributing/development-functional-tests.asciidoc index 580a5a000f391..f149e9de7aaba 100644 --- a/docs/developer/contributing/development-functional-tests.asciidoc +++ b/docs/developer/contributing/development-functional-tests.asciidoc @@ -6,7 +6,7 @@ We use functional tests to make sure the {kib} UI works as expected. It replaces [discrete] === Running functional tests -The `FunctionalTestRunner` is very bare bones and gets most of its functionality from its config file, located at {blob}test/functional/config.js[test/functional/config.js]. If you’re writing a plugin outside the {kib} repo, you will have your own config file. +The `FunctionalTestRunner` is very bare bones and gets most of its functionality from its config file, located at {blob}test/functional/config.js[test/functional/config.js] or {blob}x-pack/test/functional/config.js[x-pack/test/functional/config.js]. If you’re writing a plugin outside the {kib} repo, you will have your own config file. See <> for more info. There are three ways to run the tests depending on your goals: diff --git a/docs/developer/contributing/development-tests.asciidoc b/docs/developer/contributing/development-tests.asciidoc index 4cf667195153d..647dc8b3f3b26 100644 --- a/docs/developer/contributing/development-tests.asciidoc +++ b/docs/developer/contributing/development-tests.asciidoc @@ -1,8 +1,6 @@ [[development-tests]] == Testing -To ensure that your changes will not break other functionality, please run the test suite and build (<>) before submitting your Pull Request. - [discrete] === Running specific {kib} tests @@ -13,63 +11,57 @@ invoke them: |=== |Test runner |Test location |Runner command (working directory is {kib} root) -|Jest |`src/**/*.test.js` `src/**/*.test.ts` -|`yarn test:jest -t regexp [test path]` +|Jest |`**/*.test.{js,mjs,ts,tsx}` +|`yarn test:jest [test path]` -|Jest (integration) |`**/integration_tests/**/*.test.js` -|`yarn test:jest_integration -t regexp [test path]` +|Jest (integration) |`**/integration_tests/**/*.test.{js,mjs,ts,tsx}` +|`yarn test:jest_integration [test path]` |Mocha -|`src/**/__tests__/**/*.js` `!src/**/public/__tests__/*.js` `packages/kbn-dev-utils/src/**/__tests__/**/*.js` `tasks/**/__tests__/**/*.js` +|`**/__tests__/**/*.js` |`node scripts/mocha --grep=regexp [test path]` |Functional -|`test/*integration/**/config.js` `test/*functional/**/config.js` `test/accessibility/config.js` -|`yarn test:ftr:server --config test/[directory]/config.js``yarn test:ftr:runner --config test/[directory]/config.js --grep=regexp` +|`test/**/config.js` `x-pack/test/**/config.js` +|`node scripts/functional_tests_server --config [directory]/config.js``node scripts/functional_test_runner_ --config [directory]/config.js --grep=regexp` |=== -For X-Pack tests located in `x-pack/` see -link:{kib-repo}tree/{branch}/x-pack/README.md#testing[X-Pack Testing] - Test runner arguments: - Where applicable, the optional arguments -`-t=regexp` or `--grep=regexp` will only run tests or test suites +`--grep=regexp` will only run tests or test suites whose descriptions matches the regular expression. - `[test path]` is the relative path to the test file. -Examples: - Run the entire elasticsearch_service test suite: -`yarn test:jest src/core/server/elasticsearch/elasticsearch_service.test.ts` -- Run the jest test case whose description matches -`stops both admin and data clients`: -`yarn test:jest -t 'stops both admin and data clients' src/core/server/elasticsearch/elasticsearch_service.test.ts` -- Run the api integration test case whose description matches the given -string: ``` yarn test:ftr:server –config test/api_integration/config.js -yarn test:ftr:runner –config test/api_integration/config +=== Unit Testing -[discrete] -=== Cross-browser compatibility +Kibana primarily uses Jest for unit testing. Each plugin or package defines a `jest.config.js` that extends link:{kib-repo}tree/{branch}/packages/kbn-test/jest-preset.js[a preset] provided by the link:{kib-repo}tree/{branch}/packages/kbn-test[`@kbn/test`] package. Unless you intend to run all unit tests within the project, it's most efficient to provide the Jest configuration file for the plugin or package you're testing. -**Testing IE on OS X** +[source,bash] +---- +yarn jest --config src/plugins/dashboard/jest.config.js +---- -**Note:** IE11 is not supported from 7.9 onwards. +A script is available to provide a better user experience when testing while navigating throughout the repository. To run the tests within your current working directory, use `yarn test:jest`. Like the Jest CLI, you can also supply a path to determine which tests to run. + +[source,bash] +---- +kibana/src/plugins/dashboard/server$ yarn test:jest #or +kibana/src/plugins/dashboard$ yarn test:jest server #or +kibana$ yarn test:jest src/plugins/dashboard/server +---- + +Any additional options supplied to `test:jest` will be passed onto the Jest CLI with the resulting Jest command always being outputted. + +[source,bash] +---- +kibana/src/plugins/dashboard/server$ yarn test:jest --coverage + +# is equivelant to + +yarn jest --coverage --verbose --config /home/tyler/elastic/kibana/src/plugins/dashboard/jest.config.js server +---- + +NOTE: There are still a handful of legacy tests that use the Mocha test runner. For those tests, use `node scripts/mocha --grep=regexp [test path]`. Tests using Mocha are located within `__tests__` directories. -* http://www.vmware.com/products/fusion/fusion-evaluation.html[Download -VMWare Fusion]. -* https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/#downloads[Download -IE virtual machines] for VMWare. -* Open VMWare and go to Window > Virtual Machine Library. Unzip the -virtual machine and drag the .vmx file into your Virtual Machine -Library. -* Right-click on the virtual machine you just added to your library and -select "`Snapshots…`", and then click the "`Take`" button in the modal -that opens. You can roll back to this snapshot when the VM expires in 90 -days. -* In System Preferences > Sharing, change your computer name to be -something simple, e.g. "`computer`". -* Run {kib} with `yarn start --host=computer.local` (substituting -your computer name). -* Now you can run your VM, open the browser, and navigate to -`http://computer.local:5601` to test {kib}. -* Alternatively you can use browserstack [discrete] === Running browser automation tests @@ -93,4 +85,30 @@ include::development-functional-tests.asciidoc[leveloffset=+1] include::development-unit-tests.asciidoc[leveloffset=+1] -include::development-accessibility-tests.asciidoc[leveloffset=+1] \ No newline at end of file +include::development-accessibility-tests.asciidoc[leveloffset=+1] + +[discrete] +=== Cross-browser compatibility + +**Testing IE on OS X** + +**Note:** IE11 is not supported from 7.9 onwards. + +* http://www.vmware.com/products/fusion/fusion-evaluation.html[Download +VMWare Fusion]. +* https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/#downloads[Download +IE virtual machines] for VMWare. +* Open VMWare and go to Window > Virtual Machine Library. Unzip the +virtual machine and drag the .vmx file into your Virtual Machine +Library. +* Right-click on the virtual machine you just added to your library and +select "`Snapshots…`", and then click the "`Take`" button in the modal +that opens. You can roll back to this snapshot when the VM expires in 90 +days. +* In System Preferences > Sharing, change your computer name to be +something simple, e.g. "`computer`". +* Run {kib} with `yarn start --host=computer.local` (substituting +your computer name). +* Now you can run your VM, open the browser, and navigate to +`http://computer.local:5601` to test {kib}. +* Alternatively you can use browserstack \ No newline at end of file diff --git a/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.callback.md b/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.callback.md new file mode 100644 index 0000000000000..8ebc9068aa612 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.callback.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [AppLeaveConfirmAction](./kibana-plugin-core-public.appleaveconfirmaction.md) > [callback](./kibana-plugin-core-public.appleaveconfirmaction.callback.md) + +## AppLeaveConfirmAction.callback property + +Signature: + +```typescript +callback?: () => void; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.md b/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.md index 969d5ddd44c3e..8650cd9868940 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.md +++ b/docs/development/core/public/kibana-plugin-core-public.appleaveconfirmaction.md @@ -18,6 +18,7 @@ export interface AppLeaveConfirmAction | Property | Type | Description | | --- | --- | --- | +| [callback](./kibana-plugin-core-public.appleaveconfirmaction.callback.md) | () => void | | | [text](./kibana-plugin-core-public.appleaveconfirmaction.text.md) | string | | | [title](./kibana-plugin-core-public.appleaveconfirmaction.title.md) | string | | | [type](./kibana-plugin-core-public.appleaveconfirmaction.type.md) | AppLeaveActionType.confirm | | diff --git a/docs/development/core/public/kibana-plugin-core-public.appleavehandler.md b/docs/development/core/public/kibana-plugin-core-public.appleavehandler.md index a5f8336f6424a..d86f7b7a1a5f9 100644 --- a/docs/development/core/public/kibana-plugin-core-public.appleavehandler.md +++ b/docs/development/core/public/kibana-plugin-core-public.appleavehandler.md @@ -11,5 +11,5 @@ See [AppMountParameters](./kibana-plugin-core-public.appmountparameters.md) for Signature: ```typescript -export declare type AppLeaveHandler = (factory: AppLeaveActionFactory) => AppLeaveAction; +export declare type AppLeaveHandler = (factory: AppLeaveActionFactory, nextAppId?: string) => AppLeaveAction; ``` 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 fde40cca38fa2..522c01124de82 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 @@ -101,12 +101,7 @@ readonly links: { readonly dateMath: string; }; readonly management: Record; - readonly ml: { - readonly guide: string; - readonly anomalyDetection: string; - readonly anomalyDetectionJobs: string; - readonly dataFrameAnalytics: string; - }; + readonly ml: Record; readonly visualize: Record; }; ``` 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 46437f7ccdc21..2bb885cba434f 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 guide: string;
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 eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: {
readonly guide: string;
readonly anomalyDetection: string;
readonly anomalyDetectionJobs: string;
readonly dataFrameAnalytics: string;
};
readonly visualize: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
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 eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly visualize: Record<string, string>;
} | | diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 6cd848e963431..8b50fc38167d3 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -453,11 +453,11 @@ deprecation warning at startup. This setting cannot end in a slash (`/`). | `server.cors.enabled:` | experimental[] Set to `true` to allow cross-origin API calls. *Default:* `false` -| `server.cors.credentials:` +| `server.cors.allowCredentials:` | experimental[] Set to `true` to allow browser code to access response body whenever request performed with user credentials. *Default:* `false` -| `server.cors.origin:` - | experimental[] List of origins permitted to access resources. You must specify explicit hostnames and not use `*` for `server.cors.origin` when `server.cors.credentials: true`. *Default:* "*" +| `server.cors.allowOrigin:` + | experimental[] List of origins permitted to access resources. You must specify explicit hostnames and not use `server.cors.allowOrigin: ["*"]` when `server.cors.allowCredentials: true`. *Default:* ["*"] | `server.compression.referrerWhitelist:` | Specifies an array of trusted hostnames, such as the {kib} host, or a reverse diff --git a/package.json b/package.json index 4fb88706be16f..9ee9df67b8aea 100644 --- a/package.json +++ b/package.json @@ -43,14 +43,12 @@ "preinstall": "node ./preinstall_check", "kbn": "node scripts/kbn", "es": "node scripts/es", - "test": "grunt test", "test:jest": "node scripts/jest", "test:jest_integration": "node scripts/jest_integration", "test:mocha": "node scripts/mocha", "test:ftr": "node scripts/functional_tests", "test:ftr:server": "node scripts/functional_tests_server", "test:ftr:runner": "node scripts/functional_test_runner", - "test:coverage": "grunt test:coverage", "checkLicenses": "node scripts/check_licenses --dev", "build": "node scripts/build --all-platforms", "start": "node scripts/kibana --dev", @@ -108,7 +106,7 @@ "@elastic/datemath": "link:packages/elastic-datemath", "@elastic/elasticsearch": "7.10.0", "@elastic/ems-client": "7.11.0", - "@elastic/eui": "30.5.1", + "@elastic/eui": "30.6.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", diff --git a/packages/kbn-test/src/index.ts b/packages/kbn-test/src/index.ts index 54b064f5cd49e..a88820eb281cc 100644 --- a/packages/kbn-test/src/index.ts +++ b/packages/kbn-test/src/index.ts @@ -62,3 +62,5 @@ export * from './functional_test_runner'; export { getUrl } from './jest/utils/get_url'; export { runCheckJestConfigsCli } from './jest/run_check_jest_configs_cli'; + +export { runJest } from './jest/run'; diff --git a/packages/kbn-test/src/jest/run.test.ts b/packages/kbn-test/src/jest/run.test.ts new file mode 100644 index 0000000000000..5be033baade6a --- /dev/null +++ b/packages/kbn-test/src/jest/run.test.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 { commonBasePath } from './run'; + +describe('commonBasePath', () => { + it('returns a common path', () => { + expect(commonBasePath(['foo/bar/baz', 'foo/bar/quux', 'foo/bar'])).toBe('foo/bar'); + }); + + it('handles an empty array', () => { + expect(commonBasePath([])).toBe(''); + }); + + it('handles no common path', () => { + expect(commonBasePath(['foo', 'bar'])).toBe(''); + }); + + it('matches full paths', () => { + expect(commonBasePath(['foo/bar', 'foo/bar_baz'])).toBe('foo'); + }); +}); diff --git a/packages/kbn-test/src/jest/run.ts b/packages/kbn-test/src/jest/run.ts new file mode 100644 index 0000000000000..3283b6c8901fa --- /dev/null +++ b/packages/kbn-test/src/jest/run.ts @@ -0,0 +1,110 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Run Jest tests +// +// Provides Jest with `--config` to the first jest.config.js file found in the current +// directory, or while going up in the directory chain. If the current working directory +// is nested under the config path, a pattern will be provided to Jest to only run the +// tests within that directory. +// +// Any additional options passed will be forwarded to Jest. +// +// See all cli options in https://facebook.github.io/jest/docs/cli.html + +import { resolve, relative, sep as osSep } from 'path'; +import { existsSync } from 'fs'; +import { run } from 'jest'; +import { buildArgv } from 'jest-cli/build/cli'; +import { ToolingLog } from '@kbn/dev-utils'; + +// yarn test:jest src/core/server/saved_objects +// yarn test:jest src/core/public/core_system.test.ts +// :kibana/src/core/server/saved_objects yarn test:jest + +export function runJest(configName = 'jest.config.js') { + const argv = buildArgv(process.argv); + + const log = new ToolingLog({ + level: argv.verbose ? 'verbose' : 'info', + writeTo: process.stdout, + }); + + if (!argv.config) { + const cwd = process.env.INIT_CWD || process.cwd(); + const testFiles = argv._.splice(2).map((p) => resolve(cwd, p)); + const commonTestFiles = commonBasePath(testFiles); + const testFilesProvided = testFiles.length > 0; + + log.verbose('cwd:', cwd); + log.verbose('testFiles:', testFiles.join(', ')); + log.verbose('commonTestFiles:', commonTestFiles); + + let configPath; + + // sets the working directory to the cwd or the common + // base directory of the provided test files + let wd = testFilesProvided ? commonTestFiles : cwd; + + configPath = resolve(wd, configName); + + while (!existsSync(configPath)) { + wd = resolve(wd, '..'); + configPath = resolve(wd, configName); + } + + log.verbose(`no config provided, found ${configPath}`); + process.argv.push('--config', configPath); + + if (!testFilesProvided) { + log.verbose(`no test files provided, setting to current directory`); + process.argv.push(relative(wd, cwd)); + } + + log.info('yarn jest', process.argv.slice(2).join(' ')); + } + + if (process.env.NODE_ENV == null) { + process.env.NODE_ENV = 'test'; + } + + run(); +} + +/** + * Finds the common basePath by sorting the array + * and comparing the first and last element + */ +export function commonBasePath(paths: string[] = [], sep = osSep) { + if (paths.length === 0) return ''; + + paths = paths.concat().sort(); + + const first = paths[0].split(sep); + const last = paths[paths.length - 1].split(sep); + + const length = first.length; + let i = 0; + + while (i < length && first[i] === last[i]) { + i++; + } + + return first.slice(0, i).join(sep); +} diff --git a/scripts/jest.js b/scripts/jest.js index 90f8da10f4c90..cb31d7785898d 100755 --- a/scripts/jest.js +++ b/scripts/jest.js @@ -17,27 +17,4 @@ * under the License. */ -// # Run Jest tests -// -// All args will be forwarded directly to Jest, e.g. to watch tests run: -// -// node scripts/jest --watch -// -// or to build code coverage: -// -// node scripts/jest --coverage -// -// See all cli options in https://facebook.github.io/jest/docs/cli.html - -if (process.argv.indexOf('--config') === -1) { - // append correct jest.config if none is provided - var configPath = require('path').resolve(__dirname, '../jest.config.oss.js'); - process.argv.push('--config', configPath); - console.log('Running Jest with --config', configPath); -} - -if (process.env.NODE_ENV == null) { - process.env.NODE_ENV = 'test'; -} - -require('jest').run(); +require('@kbn/test').runJest(); diff --git a/scripts/jest_integration.js b/scripts/jest_integration.js index f07d28939ef0c..1df79781fe26d 100755 --- a/scripts/jest_integration.js +++ b/scripts/jest_integration.js @@ -17,29 +17,6 @@ * under the License. */ -// # Run Jest integration tests -// -// All args will be forwarded directly to Jest, e.g. to watch tests run: -// -// node scripts/jest_integration --watch -// -// or to build code coverage: -// -// node scripts/jest_integration --coverage -// -// See all cli options in https://facebook.github.io/jest/docs/cli.html - process.argv.push('--runInBand'); -if (process.argv.indexOf('--config') === -1) { - // append correct jest.config if none is provided - var configPath = require('path').resolve(__dirname, '../jest.config.integration.js'); - process.argv.push('--config', configPath); - console.log('Running Jest with --config', configPath); -} - -if (process.env.NODE_ENV == null) { - process.env.NODE_ENV = 'test'; -} - -require('jest').run(); +require('@kbn/test').runJest('jest.config.integration.js'); diff --git a/src/core/public/application/application_leave.test.ts b/src/core/public/application/application_leave.test.ts index b560bbc0cbc25..9d0da6fe0096d 100644 --- a/src/core/public/application/application_leave.test.ts +++ b/src/core/public/application/application_leave.test.ts @@ -35,7 +35,18 @@ describe('getLeaveAction', () => { type: AppLeaveActionType.default, }); }); + + it('returns the default action provided by the handle and nextAppId', () => { + expect(getLeaveAction((actions) => actions.default(), 'futureAppId')).toEqual({ + type: AppLeaveActionType.default, + }); + }); + it('returns the confirm action provided by the handler', () => { + expect(getLeaveAction((actions) => actions.confirm('some message'), 'futureAppId')).toEqual({ + type: AppLeaveActionType.confirm, + text: 'some message', + }); expect(getLeaveAction((actions) => actions.confirm('some message'))).toEqual({ type: AppLeaveActionType.confirm, text: 'some message', @@ -45,5 +56,14 @@ describe('getLeaveAction', () => { text: 'another message', title: 'a title', }); + const callback = jest.fn(); + expect( + getLeaveAction((actions) => actions.confirm('another message', 'a title', callback)) + ).toEqual({ + type: AppLeaveActionType.confirm, + text: 'another message', + title: 'a title', + callback, + }); }); }); diff --git a/src/core/public/application/application_leave.tsx b/src/core/public/application/application_leave.tsx index 7b69d70d3f6f6..e6170daaff0a0 100644 --- a/src/core/public/application/application_leave.tsx +++ b/src/core/public/application/application_leave.tsx @@ -26,8 +26,8 @@ import { } from './types'; const appLeaveActionFactory: AppLeaveActionFactory = { - confirm(text: string, title?: string) { - return { type: AppLeaveActionType.confirm, text, title }; + confirm(text: string, title?: string, callback?: () => void) { + return { type: AppLeaveActionType.confirm, text, title, callback }; }, default() { return { type: AppLeaveActionType.default }; @@ -38,9 +38,9 @@ export function isConfirmAction(action: AppLeaveAction): action is AppLeaveConfi return action.type === AppLeaveActionType.confirm; } -export function getLeaveAction(handler?: AppLeaveHandler): AppLeaveAction { +export function getLeaveAction(handler?: AppLeaveHandler, nextAppId?: string): AppLeaveAction { if (!handler) { return appLeaveActionFactory.default(); } - return handler(appLeaveActionFactory); + return handler(appLeaveActionFactory, nextAppId); } diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index cd186f87b3a87..912ab40cbe1db 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -755,6 +755,19 @@ describe('#start()', () => { `); }); + it('should call private function shouldNavigate with overlays and the nextAppId', async () => { + service.setup(setupDeps); + const shouldNavigateSpy = jest.spyOn(service as any, 'shouldNavigate'); + + const { navigateToApp } = await service.start(startDeps); + + await navigateToApp('myTestApp'); + expect(shouldNavigateSpy).toHaveBeenCalledWith(startDeps.overlays, 'myTestApp'); + + await navigateToApp('myOtherApp'); + expect(shouldNavigateSpy).toHaveBeenCalledWith(startDeps.overlays, 'myOtherApp'); + }); + describe('when `replace` option is true', () => { it('use `history.replace` instead of `history.push`', async () => { service.setup(setupDeps); diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index 4d54d4831698b..67281170957c6 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -244,7 +244,9 @@ export class ApplicationService { ) => { const currentAppId = this.currentAppId$.value; const navigatingToSameApp = currentAppId === appId; - const shouldNavigate = navigatingToSameApp ? true : await this.shouldNavigate(overlays); + const shouldNavigate = navigatingToSameApp + ? true + : await this.shouldNavigate(overlays, appId); if (shouldNavigate) { if (path === undefined) { @@ -332,18 +334,24 @@ export class ApplicationService { this.currentActionMenu$.next(currentActionMenu); }; - private async shouldNavigate(overlays: OverlayStart): Promise { + private async shouldNavigate(overlays: OverlayStart, nextAppId: string): Promise { const currentAppId = this.currentAppId$.value; if (currentAppId === undefined) { return true; } - const action = getLeaveAction(this.appInternalStates.get(currentAppId)?.leaveHandler); + const action = getLeaveAction( + this.appInternalStates.get(currentAppId)?.leaveHandler, + nextAppId + ); if (isConfirmAction(action)) { const confirmed = await overlays.openConfirm(action.text, { title: action.title, 'data-test-subj': 'appLeaveConfirmModal', }); if (!confirmed) { + if (action.callback) { + setTimeout(action.callback, 0); + } return false; } } diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index d9f326c7a59ab..c161a7f166541 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -578,7 +578,10 @@ export interface AppMountParameters { * * @public */ -export type AppLeaveHandler = (factory: AppLeaveActionFactory) => AppLeaveAction; +export type AppLeaveHandler = ( + factory: AppLeaveActionFactory, + nextAppId?: string +) => AppLeaveAction; /** * Possible type of actions on application leave. @@ -614,6 +617,7 @@ export interface AppLeaveConfirmAction { type: AppLeaveActionType.confirm; text: string; title?: string; + callback?: () => void; } /** @@ -636,8 +640,10 @@ export interface AppLeaveActionFactory { * * @param text The text to display in the confirmation message * @param title (optional) title to display in the confirmation message + * @param callback (optional) to know that the user want to stay on the page + * so we can show to the user the right UX for him to saved his/her/their changes */ - confirm(text: string, title?: string): AppLeaveConfirmAction; + confirm(text: string, title?: string, callback?: () => void): AppLeaveConfirmAction; /** * Returns a default action, resulting on executing the default behavior when * the user tries to leave an application diff --git a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 201f2e5f8f14b..c836686ec602b 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -1974,6 +1974,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = ` > + + +`; + +exports[`MonitorTags component render against summary 1`] = ` + + + + + +`; + +exports[`MonitorTags component renders against ping 1`] = ` + + + + + +`; diff --git a/x-pack/plugins/uptime/public/components/common/__tests__/monitor_tags.test.tsx b/x-pack/plugins/uptime/public/components/common/__tests__/monitor_tags.test.tsx new file mode 100644 index 0000000000000..a49f492221e3a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/__tests__/monitor_tags.test.tsx @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, fireEvent, screen } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { MonitorTags } from '../monitor_tags'; +import * as hooks from '../../../hooks/use_url_params'; +import { renderWithRouter, shallowWithRouter } from '../../../lib'; + +describe('MonitorTags component', () => { + const summaryPing = { + monitor_id: 'android-homepage', + state: { + timestamp: '2020-12-09T17:08:22.200Z', + monitor: { name: 'Android Homepage', type: 'http' }, + url: { + scheme: 'https', + domain: 'www.android.com', + port: 443, + full: 'https://www.android.com', + }, + summary: { up: 1, down: 0, status: 'up' }, + summaryPings: [ + { + docId: 'jBR5SHYBjTkd_7K7sM41', + timestamp: '2020-12-09T17:08:22.200Z', + '@timestamp': '2020-12-09T17:08:22.200Z', + observer: { + geo: { name: 'Europe', location: '51.5074, -0.1278' }, + }, + tls: { + server: { + hash: { + sha1: 'be647fa3de52eba57c89ac297c05604c4af69372', + sha256: '19321783f8f923a0220cee1599a58203faf4c401ab5728c730ab05a44d9e7a9c', + }, + x509: { + subject: { + common_name: 'www.android.com', + distinguished_name: + 'CN=www.android.com,O=Google LLC,L=Mountain View,ST=California,C=US', + }, + serial_number: '264575002113234958015854475703440562297', + signature_algorithm: 'SHA256-RSA', + public_key_algorithm: 'ECDSA', + public_key_curve: 'P-256', + not_before: '2020-11-03T07:38:14.000Z', + not_after: '2021-01-26T07:38:14.000Z', + issuer: { + common_name: 'GTS CA 1O1', + distinguished_name: 'CN=GTS CA 1O1,O=Google Trust Services,C=US', + }, + }, + }, + certificate_not_valid_before: '2020-11-03T07:38:14.000Z', + certificate_not_valid_after: '2021-01-26T07:38:14.000Z', + established: true, + cipher: 'TLS-AES-128-GCM-SHA256', + }, + http: { + response: { + headers: { + Vary: 'Accept-Encoding', + 'X-Content-Type-Options': 'nosniff', + 'Content-Length': '176335', + 'Last-Modified': 'Thu, 03 Dec 2020 21:45:00 GMT', + 'Accept-Ranges': 'bytes', + Date: 'Wed, 09 Dec 2020 17:08:22 GMT', + 'X-Xss-Protection': '0', + 'Cache-Control': 'private, max-age=0', + Server: 'sffe', + 'Content-Type': 'text/html', + Expires: 'Wed, 09 Dec 2020 17:08:22 GMT', + 'Alt-Svc': + 'h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"', + 'Cross-Origin-Resource-Policy': 'cross-origin', + }, + status_code: 200, + body: { + hash: '8367430abf690d75f3c0277b31a16cfcc622e2b315e338f803ae850127d37f48', + bytes: 176335, + }, + }, + }, + resolve: { ip: '172.217.16.206', rtt: { us: 7025 } }, + url: { + scheme: 'https', + domain: 'www.android.com', + port: 443, + full: 'https://www.android.com', + }, + summary: { up: 1, down: 0 }, + ecs: { version: '1.6.0' }, + agent: { + type: 'heartbeat', + version: '8.0.0', + ephemeral_id: 'a4d0d3eb-d162-4cc7-b14d-eaaad8b3d224', + id: '1f122196-6a5e-4bd4-8288-ef084e2ec982', + name: 'Elastic-Mac', + }, + tcp: { rtt: { connect: { us: 30719 } } }, + monitor: { + check_group: '12a21140-3a41-11eb-ae8d-a683e72ee74d', + ip: '172.217.16.206', + status: 'up', + duration: { us: 247752 }, + id: 'android-homepage', + name: 'Android Homepage', + type: 'http', + timespan: { gte: '2020-12-09T17:08:22.200Z', lt: '2020-12-09T17:08:52.200Z' }, + }, + tags: ['org:google'], + event: { dataset: 'uptime' }, + }, + ], + tls: { + certificate_not_valid_before: '2020-11-03T07:38:14.000Z', + certificate_not_valid_after: '2021-01-26T07:38:14.000Z', + established: true, + rtt: { handshake: { us: 39344 } }, + version_protocol: 'tls', + version: '1.3', + cipher: 'TLS-AES-128-GCM-SHA256', + }, + observer: { geo: { name: ['Europe'] } }, + }, + minInterval: 75000, + }; + + beforeAll(() => { + jest.spyOn(hooks, 'useGetUrlParams').mockReturnValue({} as any); + jest.spyOn(hooks, 'useUrlParams').mockReturnValue([ + () => { + return {}; + }, + () => {}, + ] as any); + }); + + it('render against summary', () => { + const component = shallowWithRouter(); + expect(component).toMatchSnapshot(); + }); + + it('renders against ping', () => { + const component = shallowWithRouter(); + expect(component).toMatchSnapshot(); + }); + + it('it shows expand tag on too many tags', () => { + summaryPing.state.summaryPings[0].tags = ['red', 'green', 'blue', 'black', 'purple', 'yellow']; + const component = renderWithRouter(); + expect(component).toMatchSnapshot(); + const tags = component.find('.euiBadge'); + expect(tags).toHaveLength(6); + }); + + it('expand tag show tags on click', () => { + summaryPing.state.summaryPings[0].tags = ['red', 'green', 'blue', 'black', 'purple', 'yellow']; + render(, { wrapper: MemoryRouter }); + + fireEvent.click(screen.getByText('+1')); + + expect(screen.getByText('yellow')).toHaveTextContent('yellow'); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/common/header/page_header.tsx b/x-pack/plugins/uptime/public/components/common/header/page_header.tsx index 63bcb6663619d..7d093efd31be0 100644 --- a/x-pack/plugins/uptime/public/components/common/header/page_header.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/page_header.tsx @@ -11,7 +11,12 @@ import { useRouteMatch } from 'react-router-dom'; import { UptimeDatePicker } from '../uptime_date_picker'; import { SyntheticsCallout } from '../../overview/synthetics_callout'; import { PageTabs } from './page_tabs'; -import { CERTIFICATES_ROUTE, MONITOR_ROUTE, SETTINGS_ROUTE } from '../../../../common/constants'; +import { + CERTIFICATES_ROUTE, + MONITOR_ROUTE, + SETTINGS_ROUTE, + STEP_DETAIL_ROUTE, +} from '../../../../common/constants'; import { CertRefreshBtn } from '../../certificates/cert_refresh_btn'; import { ToggleAlertFlyoutButton } from '../../overview/alerts/alerts_containers'; @@ -34,11 +39,12 @@ const StyledPicker = styled(EuiFlexItem)` export const PageHeader = () => { const isCertRoute = useRouteMatch(CERTIFICATES_ROUTE); const isSettingsRoute = useRouteMatch(SETTINGS_ROUTE); + const isStepDetailRoute = useRouteMatch(STEP_DETAIL_ROUTE); const DatePickerComponent = () => isCertRoute ? ( - ) : ( + ) : isStepDetailRoute ? null : ( diff --git a/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx b/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx new file mode 100644 index 0000000000000..506f5ef39a420 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/monitor_tags.tsx @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { EuiBadge, EuiBadgeGroup, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useHistory } from 'react-router-dom'; +import { Ping } from '../../../common/runtime_types/ping'; +import { MonitorSummary } from '../../../common/runtime_types/monitor'; +import { useFilterUpdate } from '../../hooks/use_filter_update'; +import { useGetUrlParams } from '../../hooks'; +import { parseCurrentFilters } from '../overview/monitor_list/columns/monitor_name_col'; +import { EXPAND_TAGS_LABEL } from '../overview/monitor_list/columns/translations'; +import { OVERVIEW_ROUTE } from '../../../common/constants'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; + +interface Props { + ping?: Ping | null; + summary?: MonitorSummary; +} + +const getTagsFromSummary = (summary: MonitorSummary) => { + let tags = new Set(); + summary.state.summaryPings.forEach((ping) => { + tags = new Set([...tags, ...(ping?.tags ?? [])]); + }); + + return [...tags]; +}; + +const getTagsFromPing = (ping: Ping) => { + return ping?.tags ?? []; +}; + +const getFilterLabel = (tag: string) => { + return i18n.translate('xpack.uptime.monitorList.tags.filter', { + defaultMessage: 'Filter all monitors with tag {tag}', + values: { + tag, + }, + }); +}; + +export const MonitorTags = ({ ping, summary }: Props) => { + const history = useHistory(); + + const { + services: { docLinks }, + } = useKibana(); + + const [toDisplay, setToDisplay] = useState(5); + + let tags: string[]; + + if (summary) { + // summary in case of monitor list + tags = getTagsFromSummary(summary!); + } else { + tags = getTagsFromPing(ping!); + } + + const tagsToDisplay = tags.slice(0, toDisplay); + + const { absoluteDateRangeStart, absoluteDateRangeEnd, ...params } = useGetUrlParams(); + + const currFilters = parseCurrentFilters(params.filters); + + const [filterType, setFilterType] = useState(currFilters.get('tags') ?? []); + + useFilterUpdate('tags', filterType); + + if (tags.length === 0) { + return summary ? null : ( + + Set tags + + ); + } + + return ( + + {tagsToDisplay.map((tag) => + // filtering only makes sense in monitor list, where we have summary + summary ? ( + { + setFilterType([tag]); + }} + onClickAriaLabel={getFilterLabel(tag)} + color="hollow" + className="eui-textTruncate" + style={{ maxWidth: 120 }} + > + {tag} + + ) : ( + + {tag} + + ) + )} + {tags.length > toDisplay && ( + { + setToDisplay(tags.length); + }} + onClickAriaLabel={EXPAND_TAGS_LABEL} + > + +{tags.length - 5} + + )} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx b/x-pack/plugins/uptime/public/components/common/step_detail_link.tsx new file mode 100644 index 0000000000000..a8e4c90f2d29a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/common/step_detail_link.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, { FC } from 'react'; +import { EuiLink } from '@elastic/eui'; +import { Link } from 'react-router-dom'; + +interface StepDetailLinkProps { + /** + * The ID of the check group (the journey run) + */ + checkGroupId: string; + /** + * The index of the step + */ + stepIndex: number; +} + +export const StepDetailLink: FC = ({ children, checkGroupId, stepIndex }) => { + const to = `/journey/${checkGroupId}/step/${stepIndex}`; + + return ( + + + {children} + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/common/translations.ts b/x-pack/plugins/uptime/public/components/common/translations.ts index cbab5d1d4f210..abc4764bd5227 100644 --- a/x-pack/plugins/uptime/public/components/common/translations.ts +++ b/x-pack/plugins/uptime/public/components/common/translations.ts @@ -10,6 +10,10 @@ export const URL_LABEL = i18n.translate('xpack.uptime.monitorList.table.url.name defaultMessage: 'Url', }); +export const TAGS_LABEL = i18n.translate('xpack.uptime.monitorList.table.tags.name', { + defaultMessage: 'Tags', +}); + export const STATUS_UP_LABEL = i18n.translate('xpack.uptime.monitorList.statusColumn.upLabel', { defaultMessage: 'Up', }); diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap index d722ed34388ed..9a6fca21147cd 100644 --- a/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap @@ -72,6 +72,33 @@ Array [ class="euiDescriptionList__description c1" data-test-subj="monitor-page-title" /> +
+ Tags +
+
+ + Set tags + + + (opens in a new tab or window) + + +
, ] `; diff --git a/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx index 704a79462efc3..fc9cd0f960c24 100644 --- a/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/status_details/status_bar/status_bar.tsx @@ -19,10 +19,11 @@ import * as labels from '../translations'; import { StatusByLocations } from './status_by_location'; import { useStatusBar } from './use_status_bar'; import { MonitorIDLabel, OverallAvailability } from '../translations'; -import { URL_LABEL } from '../../../common/translations'; +import { TAGS_LABEL, URL_LABEL } from '../../../common/translations'; import { MonitorLocations } from '../../../../../common/runtime_types/monitor'; import { formatAvailabilityValue } from '../availability_reporting/availability_reporting'; import { MonitorRedirects } from './monitor_redirects'; +import { MonitorTags } from '../../../common/monitor_tags'; export const MonListTitle = styled(EuiDescriptionListTitle)` &&& { @@ -79,6 +80,10 @@ export const MonitorStatusBar: React.FC = () => { )} + {TAGS_LABEL} + + + diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_journey.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_journey.test.tsx index d6f422b5c7094..030b1a49009ef 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_journey.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_journey.test.tsx @@ -206,6 +206,7 @@ describe('ExecutedJourney component', () => { direction="column" > { } /> { let step: Ping; @@ -34,8 +34,11 @@ describe('ExecutedStep', () => { }); it('renders correct step heading', () => { - expect(mountWithIntl().find('EuiText')) - .toMatchInlineSnapshot(` + expect( + mountWithRouter().find( + 'EuiText' + ) + ).toMatchInlineSnapshot(`
{ `); }); + it('renders a link to the step detail view', () => { + expect( + mountWithRouter().find( + 'StepDetailLink' + ) + ).toMatchInlineSnapshot(` + + + + + + `); + }); + it('supplies status badge correct status', () => { step.synthetics = { payload: { status: 'THE_STATUS' }, }; - expect(shallowWithIntl().find('StatusBadge')) - .toMatchInlineSnapshot(` + expect( + mountWithRouter().find( + 'StatusBadge' + ) + ).toMatchInlineSnapshot(` + > + + + + + + + + `); }); @@ -86,8 +171,11 @@ describe('ExecutedStep', () => { }, }; - expect(shallowWithIntl().find('CodeBlockAccordion')) - .toMatchInlineSnapshot(` + expect( + mountWithRouter().find( + 'CodeBlockAccordion' + ) + ).toMatchInlineSnapshot(` Array [ { language="javascript" overflowHeight={360} > - const someVar = "the var" + +
+
+ +
+
+ +
+
+ + +
+
+                              
+                                const someVar = "the var"
+                              
+                            
+
+
+
+
+
+
+
+
+
, { language="html" overflowHeight={360} > - There was an error executing the step. + +
+
+ +
+
+ +
+
+ + +
+
+                              
+                                There was an error executing the step.
+                              
+                            
+
+
+
+
+
+
+
+
+
, { language="html" overflowHeight={360} > - some.stack.trace.string + +
+
+ +
+
+ +
+
+ + +
+
+                              
+                                some.stack.trace.string
+                              
+                            
+
+
+
+
+
+
+
+
+
, ] `); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx index 0c47e4c73e976..a9748524d1bb3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx @@ -78,7 +78,7 @@ export const ExecutedJourney: FC = ({ journey }) => { {journey.steps.filter(isStepEnd).map((step, index) => ( - + ))} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx index 5966851973af2..01a599f8e8a60 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx @@ -12,80 +12,104 @@ import { CodeBlockAccordion } from './code_block_accordion'; import { StepScreenshotDisplay } from './step_screenshot_display'; import { StatusBadge } from './status_badge'; import { Ping } from '../../../../common/runtime_types'; +import { StepDetailLink } from '../../common/step_detail_link'; const CODE_BLOCK_OVERFLOW_HEIGHT = 360; interface ExecutedStepProps { step: Ping; index: number; + checkGroup: string; } -export const ExecutedStep: FC = ({ step, index }) => ( - <> -
-
- - - - - +export const ExecutedStep: FC = ({ step, index, checkGroup }) => { + return ( + <> +
+
+ {step.synthetics?.step?.index && checkGroup ? ( + + + + + + + + ) : ( + + + + + + )} +
+ +
+ +
+ +
+ + + + + + + {step.synthetics?.payload?.source} + + + {step.synthetics?.error?.message} + + + {step.synthetics?.error?.stack} + + + +
- -
- -
- -
- - - - - - - {step.synthetics?.payload?.source} - - - {step.synthetics?.error?.message} - - - {step.synthetics?.error?.stack} - - - -
-
- -); + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx new file mode 100644 index 0000000000000..fd68edef3226b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail.tsx @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiTitle, + EuiButtonEmpty, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import moment from 'moment'; +import { WaterfallChartContainer } from './waterfall/waterfall_chart_container'; + +export const PREVIOUS_CHECK_BUTTON_TEXT = i18n.translate( + 'xpack.uptime.synthetics.stepDetail.previousCheckButtonText', + { + defaultMessage: 'Previous check', + } +); + +export const NEXT_CHECK_BUTTON_TEXT = i18n.translate( + 'xpack.uptime.synthetics.stepDetail.nextCheckButtonText', + { + defaultMessage: 'Next check', + } +); + +interface Props { + checkGroup: string; + stepName?: string; + stepIndex: number; + totalSteps: number; + hasPreviousStep: boolean; + hasNextStep: boolean; + handlePreviousStep: () => void; + handleNextStep: () => void; + handleNextRun: () => void; + handlePreviousRun: () => void; + previousCheckGroup?: string; + nextCheckGroup?: string; + checkTimestamp?: string; + dateFormat: string; +} + +export const StepDetail: React.FC = ({ + dateFormat, + stepName, + checkGroup, + stepIndex, + totalSteps, + hasPreviousStep, + hasNextStep, + handlePreviousStep, + handleNextStep, + handlePreviousRun, + handleNextRun, + previousCheckGroup, + nextCheckGroup, + checkTimestamp, +}) => { + return ( + <> + + + + + +

{stepName}

+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + {PREVIOUS_CHECK_BUTTON_TEXT} + + + + {moment(checkTimestamp).format(dateFormat)} + + + + {NEXT_CHECK_BUTTON_TEXT} + + + + +
+ + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx new file mode 100644 index 0000000000000..58cf8d6e492da --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText, EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect, useCallback, useMemo } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import moment from 'moment'; +import { useBreadcrumbs } from '../../../../hooks/use_breadcrumbs'; +import { getJourneySteps } from '../../../../state/actions/journey'; +import { journeySelector } from '../../../../state/selectors'; +import { useUiSetting$ } from '../../../../../../../../src/plugins/kibana_react/public'; +import { StepDetail } from './step_detail'; + +export const NO_STEP_DATA = i18n.translate('xpack.uptime.synthetics.stepDetail.noData', { + defaultMessage: 'No data could be found for this step', +}); + +interface Props { + checkGroup: string; + stepIndex: number; +} + +export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) => { + const dispatch = useDispatch(); + const history = useHistory(); + + const [dateFormat] = useUiSetting$('dateFormat'); + + useEffect(() => { + if (checkGroup) { + dispatch(getJourneySteps({ checkGroup })); + } + }, [dispatch, checkGroup]); + + const journeys = useSelector(journeySelector); + const journey = journeys[checkGroup ?? '']; + + const { activeStep, hasPreviousStep, hasNextStep } = useMemo(() => { + return { + hasPreviousStep: stepIndex > 1 ? true : false, + activeStep: journey?.steps?.find((step) => step.synthetics?.step?.index === stepIndex), + hasNextStep: journey && journey.steps && stepIndex < journey.steps.length ? true : false, + }; + }, [stepIndex, journey]); + + useBreadcrumbs([ + ...(activeStep?.monitor?.name ? [{ text: activeStep?.monitor?.name }] : []), + ...(journey?.details?.timestamp + ? [{ text: moment(journey?.details?.timestamp).format(dateFormat) }] + : []), + ]); + + const handleNextStep = useCallback(() => { + history.push(`/journey/${checkGroup}/step/${stepIndex + 1}`); + }, [history, checkGroup, stepIndex]); + + const handlePreviousStep = useCallback(() => { + history.push(`/journey/${checkGroup}/step/${stepIndex - 1}`); + }, [history, checkGroup, stepIndex]); + + const handleNextRun = useCallback(() => { + history.push(`/journey/${journey?.details?.next?.checkGroup}/step/1`); + }, [history, journey?.details?.next?.checkGroup]); + + const handlePreviousRun = useCallback(() => { + history.push(`/journey/${journey?.details?.previous?.checkGroup}/step/1`); + }, [history, journey?.details?.previous?.checkGroup]); + + return ( + <> + + {(!journey || journey.loading) && ( + + + + + + )} + {journey && !activeStep && !journey.loading && ( + + + +

{NO_STEP_DATA}

+
+
+
+ )} + {journey && activeStep && !journey.loading && ( + + )} +
+ + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts new file mode 100644 index 0000000000000..fff14376667b2 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { colourPalette } from './data_formatting'; + +describe('Palettes', () => { + it('A colour palette comprising timing and mime type colours is correctly generated', () => { + expect(colourPalette).toEqual({ + blocked: '#b9a888', + connect: '#da8b45', + dns: '#54b399', + font: '#aa6556', + html: '#f3b3a6', + media: '#d6bf57', + other: '#e7664c', + receive: '#54b399', + script: '#9170b8', + send: '#d36086', + ssl: '#edc5a2', + stylesheet: '#ca8eae', + wait: '#b0c9e0', + }); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts new file mode 100644 index 0000000000000..7c6e176315b5b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts @@ -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 { euiPaletteColorBlind } from '@elastic/eui'; + +import { + NetworkItems, + NetworkItem, + FriendlyTimingLabels, + FriendlyMimetypeLabels, + MimeType, + MimeTypesMap, + Timings, + TIMING_ORDER, + SidebarItems, + LegendItems, +} from './types'; +import { WaterfallData } from '../../waterfall'; +import { NetworkEvent } from '../../../../../../common/runtime_types'; + +export const extractItems = (data: NetworkEvent[]): NetworkItems => { + // NOTE: This happens client side as the "payload" property is mapped + // in such a way it can't be queried (or sorted on) via ES. + return data.sort((a: NetworkItem, b: NetworkItem) => { + return a.requestSentTime - b.requestSentTime; + }); +}; + +const formatValueForDisplay = (value: number, points: number = 3) => { + return Number(value).toFixed(points); +}; + +const getColourForMimeType = (mimeType?: string) => { + const key = mimeType && MimeTypesMap[mimeType] ? MimeTypesMap[mimeType] : MimeType.Other; + return colourPalette[key]; +}; + +export const getSeriesAndDomain = (items: NetworkItems) => { + const getValueForOffset = (item: NetworkItem) => { + return item.requestSentTime; + }; + + // The earliest point in time a request is sent or started. This will become our notion of "0". + const zeroOffset = items.reduce((acc, item) => { + const offsetValue = getValueForOffset(item); + return offsetValue < acc ? offsetValue : acc; + }, Infinity); + + const getValue = (timings: NetworkEvent['timings'], timing: Timings) => { + if (!timings) return; + + // SSL is a part of the connect timing + if (timing === Timings.Connect && timings.ssl > 0) { + return timings.connect - timings.ssl; + } else { + return timings[timing]; + } + }; + + const series = items.reduce((acc, item, index) => { + if (!item.timings) return acc; + + const offsetValue = getValueForOffset(item); + + let currentOffset = offsetValue - zeroOffset; + + TIMING_ORDER.forEach((timing) => { + const value = getValue(item.timings, timing); + const colour = + timing === Timings.Receive ? getColourForMimeType(item.mimeType) : colourPalette[timing]; + if (value && value >= 0) { + const y = currentOffset + value; + + acc.push({ + x: index, + y0: currentOffset, + y, + config: { + colour, + tooltipProps: { + value: `${FriendlyTimingLabels[timing]}: ${formatValueForDisplay( + y - currentOffset + )}ms`, + colour, + }, + }, + }); + currentOffset = y; + } + }); + return acc; + }, []); + + const yValues = series.map((serie) => serie.y); + const domain = { min: 0, max: Math.max(...yValues) }; + return { series, domain }; +}; + +export const getSidebarItems = (items: NetworkItems): SidebarItems => { + return items.map((item) => { + const { url, status, method } = item; + return { url, status, method }; + }); +}; + +export const getLegendItems = (): LegendItems => { + let timingItems: LegendItems = []; + Object.values(Timings).forEach((timing) => { + // The "receive" timing is mapped to a mime type colour, so we don't need to show this in the legend + if (timing === Timings.Receive) { + return; + } + timingItems = [ + ...timingItems, + { name: FriendlyTimingLabels[timing], colour: TIMING_PALETTE[timing] }, + ]; + }); + + let mimeTypeItems: LegendItems = []; + Object.values(MimeType).forEach((mimeType) => { + mimeTypeItems = [ + ...mimeTypeItems, + { name: FriendlyMimetypeLabels[mimeType], colour: MIME_TYPE_PALETTE[mimeType] }, + ]; + }); + return [...timingItems, ...mimeTypeItems]; +}; + +// Timing colour palette +type TimingColourPalette = { + [K in Timings]: string; +}; + +const SAFE_PALETTE = euiPaletteColorBlind({ rotations: 2 }); + +const buildTimingPalette = (): TimingColourPalette => { + const palette = Object.values(Timings).reduce>((acc, value) => { + switch (value) { + case Timings.Blocked: + acc[value] = SAFE_PALETTE[6]; + break; + case Timings.Dns: + acc[value] = SAFE_PALETTE[0]; + break; + case Timings.Connect: + acc[value] = SAFE_PALETTE[7]; + break; + case Timings.Ssl: + acc[value] = SAFE_PALETTE[17]; + break; + case Timings.Send: + acc[value] = SAFE_PALETTE[2]; + break; + case Timings.Wait: + acc[value] = SAFE_PALETTE[11]; + break; + case Timings.Receive: + acc[value] = SAFE_PALETTE[0]; + break; + } + return acc; + }, {}); + + return palette as TimingColourPalette; +}; + +const TIMING_PALETTE = buildTimingPalette(); + +// MimeType colour palette +type MimeTypeColourPalette = { + [K in MimeType]: string; +}; + +const buildMimeTypePalette = (): MimeTypeColourPalette => { + const palette = Object.values(MimeType).reduce>((acc, value) => { + switch (value) { + case MimeType.Html: + acc[value] = SAFE_PALETTE[19]; + break; + case MimeType.Script: + acc[value] = SAFE_PALETTE[3]; + break; + case MimeType.Stylesheet: + acc[value] = SAFE_PALETTE[4]; + break; + case MimeType.Media: + acc[value] = SAFE_PALETTE[5]; + break; + case MimeType.Font: + acc[value] = SAFE_PALETTE[8]; + break; + case MimeType.Other: + acc[value] = SAFE_PALETTE[9]; + break; + } + return acc; + }, {}); + + return palette as MimeTypeColourPalette; +}; + +const MIME_TYPE_PALETTE = buildMimeTypePalette(); + +type ColourPalette = TimingColourPalette & MimeTypeColourPalette; + +export const colourPalette: ColourPalette = { ...TIMING_PALETTE, ...MIME_TYPE_PALETTE }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts similarity index 86% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts rename to x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts index 1dd58b4f86db3..738929741ddaf 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/types.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import { NetworkEvent } from '../../../../../../common/runtime_types'; export enum Timings { Blocked = 'blocked', @@ -33,7 +34,7 @@ export const FriendlyTimingLabels = { } ), [Timings.Ssl]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.ssl', { - defaultMessage: 'SSL', + defaultMessage: 'TLS', }), [Timings.Send]: i18n.translate('xpack.uptime.synthetics.waterfallChart.labels.timings.send', { defaultMessage: 'Sending request', @@ -144,21 +145,7 @@ export const MimeTypesMap: Record = { 'application/font-sfnt': MimeType.Font, }; -export interface NetworkItem { - timestamp: string; - method: string; - url: string; - status: number; - mimeType?: string; - // NOTE: This is the time the request was actually issued. timing.request_time might be later if the request was queued. - requestSentTime: number; - responseReceivedTime: number; - // NOTE: Denotes the earlier figure out of request sent time and request start time (part of timings). This can vary based on queue times, and - // also whether an entry actually has timings available. - // Ref: https://github.com/ChromeDevTools/devtools-frontend/blob/ed2a064ac194bfae4e25c4748a9fa3513b3e9f7d/front_end/network/RequestTimingView.js#L154 - earliestRequestTime: number; - timings: CalculatedTimings | null; -} +export type NetworkItem = NetworkEvent; export type NetworkItems = NetworkItem[]; // NOTE: A number will always be present if the property exists, but that number might be -1, which represents no value. diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx new file mode 100644 index 0000000000000..7657ca7f9c64a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.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 { EuiFlexGroup, EuiFlexItem, EuiText, EuiLoadingChart } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { getNetworkEvents } from '../../../../../state/actions/network_events'; +import { networkEventsSelector } from '../../../../../state/selectors'; +import { WaterfallChartWrapper } from './waterfall_chart_wrapper'; +import { extractItems } from './data_formatting'; + +export const NO_DATA_TEXT = i18n.translate('xpack.uptime.synthetics.stepDetail.waterfallNoData', { + defaultMessage: 'No waterfall data could be found for this step', +}); + +interface Props { + checkGroup: string; + stepIndex: number; +} + +export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex }) => { + const dispatch = useDispatch(); + + useEffect(() => { + if (checkGroup && stepIndex) { + dispatch( + getNetworkEvents({ + checkGroup, + stepIndex, + }) + ); + } + }, [dispatch, stepIndex, checkGroup]); + + const _networkEvents = useSelector(networkEventsSelector); + const networkEvents = _networkEvents[checkGroup ?? '']?.[stepIndex]; + + return ( + <> + {!networkEvents || + (networkEvents.loading && ( + + + + + + ))} + {networkEvents && !networkEvents.loading && networkEvents.events.length === 0 && ( + + + +

{NO_DATA_TEXT}

+
+
+
+ )} + {networkEvents && !networkEvents.loading && networkEvents.events.length > 0 && ( + + )} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx similarity index 91% rename from x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx rename to x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx index 434b44a94b79f..b10c3844f3002 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/waterfall_chart_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx @@ -13,7 +13,7 @@ import { WaterfallChart, MiddleTruncatedText, RenderItem, -} from '../../../waterfall'; +} from '../../waterfall'; const renderSidebarItem: RenderItem = (item, index) => { const { status } = item; @@ -27,7 +27,7 @@ const renderSidebarItem: RenderItem = (item, index) => { return ( <> - {!isErrorStatusCode(status) ? ( + {!status || !isErrorStatusCode(status) ? ( ) : ( @@ -47,9 +47,12 @@ const renderLegendItem: RenderItem = (item) => { return {item.name}; }; -export const WaterfallChartWrapper = () => { - // TODO: Will be sourced via an API - const [networkData] = useState([]); +interface Props { + data: NetworkItems; +} + +export const WaterfallChartWrapper: React.FC = ({ data }) => { + const [networkData] = useState(data); const { series, domain } = useMemo(() => { return getSeriesAndDomain(networkData); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts index ac650c5ef0ddd..95ec298e2e349 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/constants.ts @@ -10,3 +10,6 @@ export const BAR_HEIGHT = 32; export const MAIN_GROW_SIZE = 8; // Flex grow value export const SIDEBAR_GROW_SIZE = 2; +// Axis height +// NOTE: This isn't a perfect solution - changes in font size etc within charts could change the ideal height here. +export const FIXED_AXIS_HEIGHT = 32; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx index 9ff544fc1946b..c551561d5ad4f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/sidebar.tsx @@ -27,7 +27,11 @@ export const Sidebar: React.FC = ({ items, height, render }) => { - + {items.map((item, index) => { return ( diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts index 25f5e5f8f5cc9..320e415585ca3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/styles.ts @@ -6,9 +6,7 @@ import { EuiPanel, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { euiStyled } from '../../../../../../../observability/public'; - -// NOTE: This isn't a perfect solution - changes in font size etc within charts could change the ideal height here. -const FIXED_AXIS_HEIGHT = 33; +import { FIXED_AXIS_HEIGHT } from './constants'; interface WaterfallChartOuterContainerProps { height?: number; @@ -24,6 +22,7 @@ export const WaterfallChartFixedTopContainer = euiStyled.div` position: sticky; top: 0; z-index: ${(props) => props.theme.eui.euiZLevel4}; + border-bottom: ${(props) => `1px solid ${props.theme.eui.euiColorLightShade}`}; `; export const WaterfallChartFixedTopContainerSidebarCover = euiStyled(EuiPanel)` diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx index de4be0ea34b2c..d92e26335a6bd 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart.tsx @@ -33,7 +33,7 @@ import { WaterfallChartTooltip, } from './styles'; import { WaterfallData } from '../types'; -import { BAR_HEIGHT, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE } from './constants'; +import { BAR_HEIGHT, MAIN_GROW_SIZE, SIDEBAR_GROW_SIZE, FIXED_AXIS_HEIGHT } from './constants'; import { Sidebar } from './sidebar'; import { Legend } from './legend'; @@ -77,7 +77,8 @@ const getUniqueBars = (data: WaterfallData) => { }, new Set()); }; -const getChartHeight = (data: WaterfallData): number => getUniqueBars(data).size * BAR_HEIGHT; +const getChartHeight = (data: WaterfallData): number => + getUniqueBars(data).size * BAR_HEIGHT + FIXED_AXIS_HEIGHT; export const WaterfallChart = ({ tickFormat, @@ -85,7 +86,7 @@ export const WaterfallChart = ({ barStyleAccessor, renderSidebarItem, renderLegendItem, - maxHeight = 600, + maxHeight = 800, }: WaterfallChartProps) => { const { data, sidebarItems, legendItems } = useWaterfallContext(); @@ -108,10 +109,10 @@ export const WaterfallChart = ({ <> - + {shouldRenderSidebar && ( - + )} @@ -130,10 +131,13 @@ export const WaterfallChart = ({ tickFormat={tickFormat} domain={domain} showGridLines={true} + style={{ + axisLine: { + visible: false, + }, + }} /> - ''} /> - - + {shouldRenderSidebar && ( )} @@ -169,10 +173,13 @@ export const WaterfallChart = ({ tickFormat={tickFormat} domain={domain} showGridLines={true} + style={{ + axisLine: { + visible: false, + }, + }} /> - ''} /> - seconds * 1000; - -// describe('getTimings', () => { -// it('Calculates timings for network events correctly', () => { -// // NOTE: Uses these timings as the file protocol events don't have timing information -// const eventOneTimings = getTimings( -// TEST_DATA[0].synthetics.payload.response.timing!, -// toMillis(TEST_DATA[0].synthetics.payload.start), -// toMillis(TEST_DATA[0].synthetics.payload.end) -// ); -// expect(eventOneTimings).toEqual({ -// blocked: 162.4549999999106, -// connect: -1, -// dns: -1, -// receive: 0.5629999989271255, -// send: 0.5149999999999864, -// ssl: undefined, -// wait: 28.494, -// }); - -// const eventFourTimings = getTimings( -// TEST_DATA[3].synthetics.payload.response.timing!, -// toMillis(TEST_DATA[3].synthetics.payload.start), -// toMillis(TEST_DATA[3].synthetics.payload.end) -// ); -// expect(eventFourTimings).toEqual({ -// blocked: 1.8559999997466803, -// connect: 25.52200000000002, -// dns: 4.683999999999999, -// receive: 0.6780000009983667, -// send: 0.6490000000000009, -// ssl: 130.541, -// wait: 27.245000000000005, -// }); -// }); -// }); - -// describe('getSeriesAndDomain', () => { -// let seriesAndDomain: any; -// let NetworkItems: any; - -// beforeAll(() => { -// NetworkItems = extractItems(TEST_DATA); -// seriesAndDomain = getSeriesAndDomain(NetworkItems); -// }); - -// it('Correctly calculates the domain', () => { -// expect(seriesAndDomain.domain).toEqual({ max: 218.34699999913573, min: 0 }); -// }); - -// it('Correctly calculates the series', () => { -// expect(seriesAndDomain.series).toEqual([ -// { -// config: { colour: '#f3b3a6', tooltipProps: { colour: '#f3b3a6', value: '3.635ms' } }, -// x: 0, -// y: 3.6349999997764826, -// y0: 0, -// }, -// { -// config: { -// colour: '#b9a888', -// tooltipProps: { colour: '#b9a888', value: 'Queued / Blocked: 1.856ms' }, -// }, -// x: 1, -// y: 27.889999999731778, -// y0: 26.0339999999851, -// }, -// { -// config: { colour: '#54b399', tooltipProps: { colour: '#54b399', value: 'DNS: 4.684ms' } }, -// x: 1, -// y: 32.573999999731775, -// y0: 27.889999999731778, -// }, -// { -// config: { -// colour: '#da8b45', -// tooltipProps: { colour: '#da8b45', value: 'Connecting: 25.522ms' }, -// }, -// x: 1, -// y: 58.095999999731795, -// y0: 32.573999999731775, -// }, -// { -// config: { colour: '#edc5a2', tooltipProps: { colour: '#edc5a2', value: 'SSL: 130.541ms' } }, -// x: 1, -// y: 188.63699999973178, -// y0: 58.095999999731795, -// }, -// { -// config: { -// colour: '#d36086', -// tooltipProps: { colour: '#d36086', value: 'Sending request: 0.649ms' }, -// }, -// x: 1, -// y: 189.28599999973179, -// y0: 188.63699999973178, -// }, -// { -// config: { -// colour: '#b0c9e0', -// tooltipProps: { colour: '#b0c9e0', value: 'Waiting (TTFB): 27.245ms' }, -// }, -// x: 1, -// y: 216.5309999997318, -// y0: 189.28599999973179, -// }, -// { -// config: { -// colour: '#ca8eae', -// tooltipProps: { colour: '#ca8eae', value: 'Content downloading: 0.678ms' }, -// }, -// x: 1, -// y: 217.20900000073016, -// y0: 216.5309999997318, -// }, -// { -// config: { -// colour: '#b9a888', -// tooltipProps: { colour: '#b9a888', value: 'Queued / Blocked: 162.455ms' }, -// }, -// x: 2, -// y: 188.77500000020862, -// y0: 26.320000000298023, -// }, -// { -// config: { -// colour: '#d36086', -// tooltipProps: { colour: '#d36086', value: 'Sending request: 0.515ms' }, -// }, -// x: 2, -// y: 189.2900000002086, -// y0: 188.77500000020862, -// }, -// { -// config: { -// colour: '#b0c9e0', -// tooltipProps: { colour: '#b0c9e0', value: 'Waiting (TTFB): 28.494ms' }, -// }, -// x: 2, -// y: 217.7840000002086, -// y0: 189.2900000002086, -// }, -// { -// config: { -// colour: '#9170b8', -// tooltipProps: { colour: '#9170b8', value: 'Content downloading: 0.563ms' }, -// }, -// x: 2, -// y: 218.34699999913573, -// y0: 217.7840000002086, -// }, -// { -// config: { colour: '#9170b8', tooltipProps: { colour: '#9170b8', value: '12.139ms' } }, -// x: 3, -// y: 46.15699999965727, -// y0: 34.01799999922514, -// }, -// { -// config: { colour: '#9170b8', tooltipProps: { colour: '#9170b8', value: '8.453ms' } }, -// x: 4, -// y: 43.506999999284744, -// y0: 35.053999999538064, -// }, -// ]); -// }); -// }); - -describe('Palettes', () => { - it('A colour palette comprising timing and mime type colours is correctly generated', () => { - expect(colourPalette).toEqual({ - blocked: '#b9a888', - connect: '#da8b45', - dns: '#54b399', - font: '#aa6556', - html: '#f3b3a6', - media: '#d6bf57', - other: '#e7664c', - receive: '#54b399', - script: '#9170b8', - send: '#d36086', - ssl: '#edc5a2', - stylesheet: '#ca8eae', - wait: '#b0c9e0', - }); - }); -}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts deleted file mode 100644 index 9c66ea638c942..0000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/consumers/synthetics/data_formatting.ts +++ /dev/null @@ -1,336 +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 { euiPaletteColorBlind } from '@elastic/eui'; - -import { - PayloadTimings, - CalculatedTimings, - NetworkItems, - FriendlyTimingLabels, - FriendlyMimetypeLabels, - MimeType, - MimeTypesMap, - Timings, - TIMING_ORDER, - SidebarItems, - LegendItems, -} from './types'; -import { WaterfallData } from '../../../waterfall'; - -const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000); - -// The timing calculations here are based off several sources: -// https://github.com/ChromeDevTools/devtools-frontend/blob/2fe91adefb2921b4deb2b4b125370ef9ccdb8d1b/front_end/sdk/HARLog.js#L307 -// and -// https://chromium.googlesource.com/chromium/blink.git/+/master/Source/devtools/front_end/sdk/HAREntry.js#131 -// and -// https://github.com/cyrus-and/chrome-har-capturer/blob/master/lib/har.js#L195 -// Order of events: request_start = 0, [proxy], [dns], [connect [ssl]], [send], receive_headers_end - -export const getTimings = ( - timings: PayloadTimings, - requestSentTime: number, - responseReceivedTime: number -): CalculatedTimings => { - if (!timings) return { blocked: -1, dns: -1, connect: -1, send: 0, wait: 0, receive: 0, ssl: -1 }; - - const getLeastNonNegative = (values: number[]) => - values.reduce((best, value) => (value >= 0 && value < best ? value : best), Infinity); - const getOptionalTiming = (_timings: PayloadTimings, key: keyof PayloadTimings) => - _timings[key] >= 0 ? _timings[key] : -1; - - // NOTE: Request sent and request start can differ due to queue times - const requestStartTime = microToMillis(timings.request_time); - - // Queued - const queuedTime = requestSentTime < requestStartTime ? requestStartTime - requestSentTime : -1; - - // Blocked - // "blocked" represents both queued time + blocked/stalled time + proxy time (ie: anything before the request was actually started). - let blocked = queuedTime; - - const blockedStart = getLeastNonNegative([ - timings.dns_start, - timings.connect_start, - timings.send_start, - ]); - - if (blockedStart !== Infinity) { - blocked += blockedStart; - } - - // Proxy - // Proxy is part of blocked, but it can be quirky in that blocked can be -1 even though there are proxy timings. This can happen with - // protocols like Quic. - if (timings.proxy_end !== -1) { - const blockedProxy = timings.proxy_end - timings.proxy_start; - - if (blockedProxy && blockedProxy > blocked) { - blocked = blockedProxy; - } - } - - // DNS - const dnsStart = timings.dns_end >= 0 ? blockedStart : 0; - const dnsEnd = getOptionalTiming(timings, 'dns_end'); - const dns = dnsEnd - dnsStart; - - // SSL - const sslStart = getOptionalTiming(timings, 'ssl_start'); - const sslEnd = getOptionalTiming(timings, 'ssl_end'); - let ssl; - - if (sslStart >= 0 && sslEnd >= 0) { - ssl = timings.ssl_end - timings.ssl_start; - } - - // Connect - let connect = -1; - if (timings.connect_start >= 0) { - connect = timings.send_start - timings.connect_start; - } - - // Send - const send = timings.send_end - timings.send_start; - - // Wait - const wait = timings.receive_headers_end - timings.send_end; - - // Receive - const receive = responseReceivedTime - (requestStartTime + timings.receive_headers_end); - - // SSL connection is a part of the overall connection time - if (connect && ssl) { - connect = connect - ssl; - } - - return { blocked, dns, connect, send, wait, receive, ssl }; -}; - -// TODO: Switch to real API data, and type data as the payload response (if server response isn't preformatted) -export const extractItems = (data: any): NetworkItems => { - const items = data - .map((entry: any) => { - const requestSentTime = microToMillis(entry.synthetics.payload.start); - const responseReceivedTime = microToMillis(entry.synthetics.payload.end); - const requestStartTime = - entry.synthetics.payload.response && entry.synthetics.payload.response.timing - ? microToMillis(entry.synthetics.payload.response.timing.request_time) - : null; - - return { - timestamp: entry['@timestamp'], - method: entry.synthetics.payload.method, - url: entry.synthetics.payload.url, - status: entry.synthetics.payload.status, - mimeType: entry.synthetics.payload?.response?.mime_type, - requestSentTime, - responseReceivedTime, - earliestRequestTime: requestStartTime - ? Math.min(requestSentTime, requestStartTime) - : requestSentTime, - timings: - entry.synthetics.payload.response && entry.synthetics.payload.response.timing - ? getTimings( - entry.synthetics.payload.response.timing, - requestSentTime, - responseReceivedTime - ) - : null, - }; - }) - .sort((a: any, b: any) => { - return a.earliestRequestTime - b.earliestRequestTime; - }); - - return items; -}; - -const formatValueForDisplay = (value: number, points: number = 3) => { - return Number(value).toFixed(points); -}; - -const getColourForMimeType = (mimeType?: string) => { - const key = mimeType && MimeTypesMap[mimeType] ? MimeTypesMap[mimeType] : MimeType.Other; - return colourPalette[key]; -}; - -export const getSeriesAndDomain = (items: NetworkItems) => { - // The earliest point in time a request is sent or started. This will become our notion of "0". - const zeroOffset = items.reduce((acc, item) => { - const { earliestRequestTime } = item; - return earliestRequestTime < acc ? earliestRequestTime : acc; - }, Infinity); - - const series = items.reduce((acc, item, index) => { - const { earliestRequestTime } = item; - - // Entries without timings should be handled differently: - // https://github.com/ChromeDevTools/devtools-frontend/blob/ed2a064ac194bfae4e25c4748a9fa3513b3e9f7d/front_end/network/RequestTimingView.js#L140 - // If there are no concrete timings just plot one block via start and end - if (!item.timings || item.timings === null) { - const duration = item.responseReceivedTime - item.earliestRequestTime; - const colour = getColourForMimeType(item.mimeType); - return [ - ...acc, - { - x: index, - y0: item.earliestRequestTime - zeroOffset, - y: item.responseReceivedTime - zeroOffset, - config: { - colour, - tooltipProps: { - value: `${formatValueForDisplay(duration)}ms`, - colour, - }, - }, - }, - ]; - } - - let currentOffset = earliestRequestTime - zeroOffset; - - TIMING_ORDER.forEach((timing) => { - const value = item.timings![timing]; - const colour = - timing === Timings.Receive ? getColourForMimeType(item.mimeType) : colourPalette[timing]; - if (value && value >= 0) { - const y = currentOffset + value; - - acc.push({ - x: index, - y0: currentOffset, - y, - config: { - colour, - tooltipProps: { - value: `${FriendlyTimingLabels[timing]}: ${formatValueForDisplay( - y - currentOffset - )}ms`, - colour, - }, - }, - }); - currentOffset = y; - } - }); - return acc; - }, []); - - const yValues = series.map((serie) => serie.y); - const domain = { min: 0, max: Math.max(...yValues) }; - return { series, domain }; -}; - -export const getSidebarItems = (items: NetworkItems): SidebarItems => { - return items.map((item) => { - const { url, status, method } = item; - return { url, status, method }; - }); -}; - -export const getLegendItems = (): LegendItems => { - let timingItems: LegendItems = []; - Object.values(Timings).forEach((timing) => { - // The "receive" timing is mapped to a mime type colour, so we don't need to show this in the legend - if (timing === Timings.Receive) { - return; - } - timingItems = [ - ...timingItems, - { name: FriendlyTimingLabels[timing], colour: TIMING_PALETTE[timing] }, - ]; - }); - - let mimeTypeItems: LegendItems = []; - Object.values(MimeType).forEach((mimeType) => { - mimeTypeItems = [ - ...mimeTypeItems, - { name: FriendlyMimetypeLabels[mimeType], colour: MIME_TYPE_PALETTE[mimeType] }, - ]; - }); - return [...timingItems, ...mimeTypeItems]; -}; - -// Timing colour palette -type TimingColourPalette = { - [K in Timings]: string; -}; - -const SAFE_PALETTE = euiPaletteColorBlind({ rotations: 2 }); - -const buildTimingPalette = (): TimingColourPalette => { - const palette = Object.values(Timings).reduce>((acc, value) => { - switch (value) { - case Timings.Blocked: - acc[value] = SAFE_PALETTE[6]; - break; - case Timings.Dns: - acc[value] = SAFE_PALETTE[0]; - break; - case Timings.Connect: - acc[value] = SAFE_PALETTE[7]; - break; - case Timings.Ssl: - acc[value] = SAFE_PALETTE[17]; - break; - case Timings.Send: - acc[value] = SAFE_PALETTE[2]; - break; - case Timings.Wait: - acc[value] = SAFE_PALETTE[11]; - break; - case Timings.Receive: - acc[value] = SAFE_PALETTE[0]; - break; - } - return acc; - }, {}); - - return palette as TimingColourPalette; -}; - -const TIMING_PALETTE = buildTimingPalette(); - -// MimeType colour palette -type MimeTypeColourPalette = { - [K in MimeType]: string; -}; - -const buildMimeTypePalette = (): MimeTypeColourPalette => { - const palette = Object.values(MimeType).reduce>((acc, value) => { - switch (value) { - case MimeType.Html: - acc[value] = SAFE_PALETTE[19]; - break; - case MimeType.Script: - acc[value] = SAFE_PALETTE[3]; - break; - case MimeType.Stylesheet: - acc[value] = SAFE_PALETTE[4]; - break; - case MimeType.Media: - acc[value] = SAFE_PALETTE[5]; - break; - case MimeType.Font: - acc[value] = SAFE_PALETTE[8]; - break; - case MimeType.Other: - acc[value] = SAFE_PALETTE[9]; - break; - } - return acc; - }, {}); - - return palette as MimeTypeColourPalette; -}; - -const MIME_TYPE_PALETTE = buildMimeTypePalette(); - -type ColourPalette = TimingColourPalette & MimeTypeColourPalette; - -export const colourPalette: ColourPalette = { ...TIMING_PALETTE, ...MIME_TYPE_PALETTE }; diff --git a/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap index 89433f8bc57c4..b959d822ac73e 100644 --- a/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap @@ -379,6 +379,7 @@ exports[`EmptyState component does not render empty state with appropriate base element="a" fill={true} href="/app/home#/tutorial/uptimeMonitors" + isDisabled={false} rel="noreferrer" >
+
+ + Tags + +
+ + @@ -1030,7 +1047,7 @@ exports[`MonitorList component renders the monitor list 1`] = ` @@ -1046,7 +1063,7 @@ exports[`MonitorList component renders the monitor list 1`] = `
+ +
+ Tags +
+
+ @@ -1436,7 +1466,7 @@ exports[`MonitorList component renders the monitor list 1`] = `
+ +
+ Tags +
+
+ diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx index 9c744e5221d31..3953d35cc73db 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/monitor_name_col.tsx @@ -17,7 +17,7 @@ interface Props { summary: MonitorSummary; } -const parseCurrentFilters = (filters: string) => { +export const parseCurrentFilters = (filters: string) => { let parsedFilters: Map; try { parsedFilters = new Map(JSON.parse(filters)); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/translations.ts b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/translations.ts index 421072ab603c2..1737d778be34c 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/translations.ts +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/columns/translations.ts @@ -13,3 +13,7 @@ export const ENABLE_STATUS_ALERT = i18n.translate('xpack.uptime.monitorList.enab export const DISABLE_STATUS_ALERT = i18n.translate('xpack.uptime.monitorList.disableDownAlert', { defaultMessage: 'Disable status alert', }); + +export const EXPAND_TAGS_LABEL = i18n.translate('xpack.uptime.monitorList.tags.expand', { + defaultMessage: 'Click to view remaining tags', +}); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx index 1732602b39d8d..10699dd34343e 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx @@ -27,10 +27,11 @@ import { MonitorListProps } from './monitor_list_container'; import { MonitorList } from '../../../state/reducers/monitor_list'; import { CertStatusColumn } from './columns/cert_status_column'; import { MonitorListHeader } from './monitor_list_header'; -import { URL_LABEL } from '../../common/translations'; +import { TAGS_LABEL, URL_LABEL } from '../../common/translations'; import { EnableMonitorAlert } from './columns/enable_alert'; import { STATUS_ALERT_COLUMN } from './translations'; import { MonitorNameColumn } from './columns/monitor_name_col'; +import { MonitorTags } from '../../common/monitor_tags'; interface Props extends MonitorListProps { pageSize: number; @@ -101,13 +102,20 @@ export const MonitorListComponent: ({ align: 'left' as const, field: 'state.url.full', name: URL_LABEL, - width: '40%', + width: '30%', render: (url: string) => ( {url} ), }, + { + align: 'left' as const, + field: 'state.monitor.name', + name: TAGS_LABEL, + width: '12%', + render: (_name: string, summary: MonitorSummary) => , + }, { align: 'left' as const, field: 'state.tls.server.x509', diff --git a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts index 9b4a441fe5ade..21665e93dd274 100644 --- a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts @@ -14,6 +14,7 @@ export enum UptimePage { Monitor = 'Monitor', Settings = 'Settings', Certificates = 'Certificates', + StepDetail = 'StepDetail', NotFound = '__not-found__', } diff --git a/x-pack/plugins/uptime/public/pages/index.ts b/x-pack/plugins/uptime/public/pages/index.ts index cea47d6ccf79c..cb95fb8558cfb 100644 --- a/x-pack/plugins/uptime/public/pages/index.ts +++ b/x-pack/plugins/uptime/public/pages/index.ts @@ -5,5 +5,6 @@ */ export { MonitorPage } from './monitor'; +export { StepDetailPage } from './step_detail_page'; export { SettingsPage } from './settings'; export { NotFoundPage } from './not_found'; diff --git a/x-pack/plugins/uptime/public/pages/step_detail_page.tsx b/x-pack/plugins/uptime/public/pages/step_detail_page.tsx new file mode 100644 index 0000000000000..5bacad7e9a2d2 --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/step_detail_page.tsx @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { useParams } from 'react-router-dom'; +import { useTrackPageview } from '../../../observability/public'; +import { useInitApp } from '../hooks/use_init_app'; +import { StepDetailContainer } from '../components/monitor/synthetics/step_detail/step_detail_container'; + +export const StepDetailPage: React.FC = () => { + useInitApp(); + const { checkGroupId, stepIndex } = useParams<{ checkGroupId: string; stepIndex: string }>(); + useTrackPageview({ app: 'uptime', path: 'stepDetail' }); + useTrackPageview({ app: 'uptime', path: 'stepDetail', delay: 15000 }); + + return ; +}; diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index 9b54c52cc674c..65526f9bca4fc 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -12,8 +12,9 @@ import { MONITOR_ROUTE, OVERVIEW_ROUTE, SETTINGS_ROUTE, + STEP_DETAIL_ROUTE, } from '../common/constants'; -import { MonitorPage, NotFoundPage, SettingsPage } from './pages'; +import { MonitorPage, StepDetailPage, NotFoundPage, SettingsPage } from './pages'; import { CertificatesPage } from './pages/certificates'; import { UptimePage, useUptimeTelemetry } from './hooks'; import { PageHeader } from './components/common/header/page_header'; @@ -50,6 +51,13 @@ const Routes: RouteProps[] = [ dataTestSubj: 'uptimeCertificatesPage', telemetryId: UptimePage.Certificates, }, + { + title: baseTitle, + path: STEP_DETAIL_ROUTE, + component: StepDetailPage, + dataTestSubj: 'uptimeStepDetailPage', + telemetryId: UptimePage.StepDetail, + }, { title: baseTitle, path: OVERVIEW_ROUTE, diff --git a/x-pack/plugins/uptime/public/state/actions/network_events.ts b/x-pack/plugins/uptime/public/state/actions/network_events.ts new file mode 100644 index 0000000000000..e3564689fcd48 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/actions/network_events.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createAction } from 'redux-actions'; +import { SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; + +export interface FetchNetworkEventsParams { + checkGroup: string; + stepIndex: number; +} + +export interface FetchNetworkEventsFailPayload { + checkGroup: string; + stepIndex: number; + error: Error; +} + +export const getNetworkEvents = createAction('GET_NETWORK_EVENTS'); +export const getNetworkEventsSuccess = createAction< + Pick & SyntheticsNetworkEventsApiResponse +>('GET_NETWORK_EVENTS_SUCCESS'); +export const getNetworkEventsFail = createAction( + 'GET_NETWORK_EVENTS_FAIL' +); diff --git a/x-pack/plugins/uptime/public/state/api/network_events.ts b/x-pack/plugins/uptime/public/state/api/network_events.ts new file mode 100644 index 0000000000000..a4eceb4812d28 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/api/network_events.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { apiService } from './utils'; +import { FetchNetworkEventsParams } from '../actions/network_events'; +import { + SyntheticsNetworkEventsApiResponse, + SyntheticsNetworkEventsApiResponseType, +} from '../../../common/runtime_types'; + +export async function fetchNetworkEvents( + params: FetchNetworkEventsParams +): Promise { + return (await apiService.get( + `/api/uptime/network_events`, + { + checkGroup: params.checkGroup, + stepIndex: params.stepIndex, + }, + SyntheticsNetworkEventsApiResponseType + )) as SyntheticsNetworkEventsApiResponse; +} diff --git a/x-pack/plugins/uptime/public/state/api/utils.ts b/x-pack/plugins/uptime/public/state/api/utils.ts index 965cbefd13114..54e129c0811c2 100644 --- a/x-pack/plugins/uptime/public/state/api/utils.ts +++ b/x-pack/plugins/uptime/public/state/api/utils.ts @@ -8,6 +8,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import { isRight } from 'fp-ts/lib/Either'; import { HttpFetchQuery, HttpSetup } from 'src/core/public'; import * as t from 'io-ts'; +import { startsWith } from 'lodash'; function isObject(value: unknown) { const type = typeof value; @@ -60,7 +61,14 @@ class ApiService { } public async get(apiUrl: string, params?: HttpFetchQuery, decodeType?: any, asResponse = false) { - const response = await this._http!.fetch({ path: apiUrl, query: params, asResponse }); + const debugEnabled = + sessionStorage.getItem('uptime_debug') === 'true' && startsWith(apiUrl, '/api/uptime'); + + const response = await this._http!.fetch({ + path: apiUrl, + query: { ...params, ...(debugEnabled ? { _debug: true } : {}) }, + asResponse, + }); if (decodeType) { const decoded = decodeType.decode(response); diff --git a/x-pack/plugins/uptime/public/state/effects/index.ts b/x-pack/plugins/uptime/public/state/effects/index.ts index 4951f2102c8a7..3c75e75871882 100644 --- a/x-pack/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/plugins/uptime/public/state/effects/index.ts @@ -19,6 +19,7 @@ import { fetchIndexStatusEffect } from './index_status'; import { fetchCertificatesEffect } from '../certificates/certificates'; import { fetchAlertsEffect } from '../alerts/alerts'; import { fetchJourneyStepsEffect } from './journey'; +import { fetchNetworkEventsEffect } from './network_events'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -37,4 +38,5 @@ export function* rootEffect() { yield fork(fetchCertificatesEffect); yield fork(fetchAlertsEffect); yield fork(fetchJourneyStepsEffect); + yield fork(fetchNetworkEventsEffect); } diff --git a/x-pack/plugins/uptime/public/state/effects/network_events.ts b/x-pack/plugins/uptime/public/state/effects/network_events.ts new file mode 100644 index 0000000000000..95d24fbe37ae2 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/effects/network_events.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Action } from 'redux-actions'; +import { call, put, takeLatest } from 'redux-saga/effects'; +import { + getNetworkEvents, + getNetworkEventsSuccess, + getNetworkEventsFail, + FetchNetworkEventsParams, +} from '../actions/network_events'; +import { fetchNetworkEvents } from '../api/network_events'; + +export function* fetchNetworkEventsEffect() { + yield takeLatest(getNetworkEvents, function* (action: Action) { + try { + const response = yield call(fetchNetworkEvents, action.payload); + + yield put( + getNetworkEventsSuccess({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + ...response, + }) + ); + } catch (e) { + yield put( + getNetworkEventsFail({ + checkGroup: action.payload.checkGroup, + stepIndex: action.payload.stepIndex, + error: e, + }) + ); + } + }); +} diff --git a/x-pack/plugins/uptime/public/state/reducers/index.ts b/x-pack/plugins/uptime/public/state/reducers/index.ts index c0bab124d5f9d..661b637802707 100644 --- a/x-pack/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/plugins/uptime/public/state/reducers/index.ts @@ -22,6 +22,7 @@ import { certificatesReducer } from '../certificates/certificates'; import { selectedFiltersReducer } from './selected_filters'; import { alertsReducer } from '../alerts/alerts'; import { journeyReducer } from './journey'; +import { networkEventsReducer } from './network_events'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -41,4 +42,5 @@ export const rootReducer = combineReducers({ selectedFilters: selectedFiltersReducer, alerts: alertsReducer, journeys: journeyReducer, + networkEvents: networkEventsReducer, }); diff --git a/x-pack/plugins/uptime/public/state/reducers/journey.ts b/x-pack/plugins/uptime/public/state/reducers/journey.ts index e1c3dc808f1bf..133a5d1edb2c2 100644 --- a/x-pack/plugins/uptime/public/state/reducers/journey.ts +++ b/x-pack/plugins/uptime/public/state/reducers/journey.ts @@ -18,6 +18,7 @@ import { export interface JourneyState { checkGroup: string; steps: Ping[]; + details?: SyntheticsJourneyApiResponse['details']; loading: boolean; error?: Error; } @@ -56,13 +57,14 @@ export const journeyReducer = handleActions( [String(getJourneyStepsSuccess)]: ( state: JourneyKVP, - { payload: { checkGroup, steps } }: Action + { payload: { checkGroup, steps, details } }: Action ) => ({ ...state, [checkGroup]: { loading: false, checkGroup, steps, + details, }, }), diff --git a/x-pack/plugins/uptime/public/state/reducers/network_events.ts b/x-pack/plugins/uptime/public/state/reducers/network_events.ts new file mode 100644 index 0000000000000..44a23b0fa53d7 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/reducers/network_events.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions, Action } from 'redux-actions'; +import { NetworkEvent, SyntheticsNetworkEventsApiResponse } from '../../../common/runtime_types'; +import { + FetchNetworkEventsParams, + FetchNetworkEventsFailPayload, + getNetworkEvents, + getNetworkEventsFail, + getNetworkEventsSuccess, +} from '../actions/network_events'; + +export interface NetworkEventsState { + [checkGroup: string]: { + [stepIndex: number]: { + events: NetworkEvent[]; + loading: boolean; + error?: Error; + }; + }; +} + +const initialState: NetworkEventsState = {}; + +type Payload = FetchNetworkEventsParams & + SyntheticsNetworkEventsApiResponse & + FetchNetworkEventsFailPayload & + string[]; + +export const networkEventsReducer = handleActions( + { + [String(getNetworkEvents)]: ( + state: NetworkEventsState, + { payload: { checkGroup, stepIndex } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: true, + events: [], + } + : { + loading: true, + events: [], + }, + } + : { + [stepIndex]: { + loading: true, + events: [], + }, + }, + }), + + [String(getNetworkEventsSuccess)]: ( + state: NetworkEventsState, + { + payload: { events, checkGroup, stepIndex }, + }: Action + ) => { + return { + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: false, + events, + } + : { + loading: false, + events, + }, + } + : { + [stepIndex]: { + loading: false, + events, + }, + }, + }; + }, + + [String(getNetworkEventsFail)]: ( + state: NetworkEventsState, + { payload: { checkGroup, stepIndex, error } }: Action + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + [stepIndex]: state[checkGroup][stepIndex] + ? { + ...state[checkGroup][stepIndex], + loading: false, + events: [], + error, + } + : { + loading: false, + events: [], + error, + }, + } + : { + [stepIndex]: { + loading: false, + events: [], + error, + }, + }, + }), + }, + initialState +); diff --git a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts index f1a68318be863..64410b860b197 100644 --- a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -116,6 +116,7 @@ describe('state selectors', () => { anomalyAlertDeletion: { data: null, loading: false }, }, journeys: {}, + networkEvents: {}, }; it('selects base path from state', () => { diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index 6bfe67468aae5..eef53e1100029 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -96,3 +96,5 @@ export const selectedFiltersSelector = ({ selectedFilters }: AppState) => select export const monitorIdSelector = ({ ui: { monitorId } }: AppState) => monitorId; export const journeySelector = ({ journeys }: AppState) => journeys; + +export const networkEventsSelector = ({ networkEvents }: AppState) => networkEvents; diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts index 022ec48bad1d9..f5e79ad43336b 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts @@ -82,6 +82,7 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory = (_server, _li context: [], state: [...durationAnomalyTranslations.actionVariables, ...commonStateTranslations], }, + minimumLicenseRequired: 'basic', async executor({ options, uptimeEsClient, savedObjectsClient, dynamicSettings }) { const { services: { alertInstanceFactory }, diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 3e45ce302bf87..56ca7a85784c5 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -255,6 +255,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) = ], state: [...commonMonitorStateI18, ...commonStateTranslations], }, + minimumLicenseRequired: 'basic', async executor({ options: { params: rawParams, diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts index 41a5101716122..b6501f7d92059 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/tls.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -100,6 +100,7 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => context: [], state: [...tlsTranslations.actionVariables, ...commonStateTranslations], }, + minimumLicenseRequired: 'basic', async executor({ options, dynamicSettings, uptimeEsClient }) { const { services: { alertInstanceFactory }, diff --git a/x-pack/plugins/uptime/server/lib/lib.ts b/x-pack/plugins/uptime/server/lib/lib.ts index 39dd868462525..ee84cf4463ceb 100644 --- a/x-pack/plugins/uptime/server/lib/lib.ts +++ b/x-pack/plugins/uptime/server/lib/lib.ts @@ -3,7 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract, KibanaRequest } from 'kibana/server'; +import chalk from 'chalk'; import { UMBackendFrameworkAdapter } from './adapters'; import { UMLicenseCheck } from './domains'; import { UptimeRequests } from './requests'; @@ -19,53 +20,83 @@ export interface UMServerLibs extends UMDomainLibs { framework: UMBackendFrameworkAdapter; } +interface CountResponse { + body: { + count: number; + _shards: { + total: number; + successful: number; + skipped: number; + failed: number; + }; + }; +} + export type UptimeESClient = ReturnType; export function createUptimeESClient({ esClient, + request, savedObjectsClient, }: { esClient: ElasticsearchClient; + request?: KibanaRequest; savedObjectsClient: SavedObjectsClientContract; }) { + const { _debug = false } = (request?.query as { _debug: boolean }) ?? {}; + return { baseESClient: esClient, async search(params: TParams): Promise<{ body: ESSearchResponse }> { + let res: any; + let esError: any; const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( savedObjectsClient! ); - let res: any; + const esParams = { index: dynamicSettings!.heartbeatIndices, ...params }; + const startTime = process.hrtime(); + try { - res = await esClient.search({ index: dynamicSettings!.heartbeatIndices, ...params }); + res = await esClient.search(esParams); } catch (e) { - throw e; + esError = e; + } + if (_debug && request) { + debugESCall({ startTime, request, esError, operationName: 'search', params: esParams }); } + + if (esError) { + throw esError; + } + return res; }, - async count( - params: TParams - ): Promise<{ - body: { - count: number; - _shards: { - total: number; - successful: number; - skipped: number; - failed: number; - }; - }; - }> { + async count(params: TParams): Promise { + let res: any; + let esError: any; + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( savedObjectsClient! ); - let res: any; + const esParams = { index: dynamicSettings!.heartbeatIndices, ...params }; + const startTime = process.hrtime(); + try { - res = await esClient.count({ index: dynamicSettings!.heartbeatIndices, ...params }); + res = await esClient.count(esParams); } catch (e) { - throw e; + esError = e; } + + if (_debug && request) { + debugESCall({ startTime, request, esError, operationName: 'count', params: esParams }); + } + + if (esError) { + throw esError; + } + return res; }, getSavedObjectsClient() { @@ -73,3 +104,41 @@ export function createUptimeESClient({ }, }; } + +/* eslint-disable no-console */ + +function formatObj(obj: Record) { + return JSON.stringify(obj); +} + +export function debugESCall({ + operationName, + params, + request, + esError, + startTime, +}: { + operationName: string; + params: Record; + request: KibanaRequest; + esError: any; + startTime: [number, number]; +}) { + const highlightColor = esError ? 'bgRed' : 'inverse'; + const diff = process.hrtime(startTime); + const duration = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; + const routeInfo = `${request.route.method.toUpperCase()} ${request.route.path}`; + + console.log(chalk.bold[highlightColor](`=== Debug: ${routeInfo} (${duration}) ===`)); + + if (operationName === 'search') { + console.log(`GET ${params.index}/_${operationName}`); + console.log(formatObj(params.body)); + } else { + console.log(chalk.bold('ES operation:'), operationName); + + console.log(chalk.bold('ES query:')); + console.log(formatObj(params)); + } + console.log(`\n`); +} diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index 3e3a6878b18b9..a59a21f4afae1 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -38,7 +38,7 @@ describe('getLatestMonitor', () => { }, }, size: 1, - _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*', 'http', 'error'], + _source: ['url', 'monitor', 'observer', '@timestamp', 'tls.*', 'http', 'error', 'tags'], sort: { '@timestamp': { order: 'desc' }, }, diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_network_events.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_network_events.test.ts new file mode 100644 index 0000000000000..bb88911eedfb0 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_network_events.test.ts @@ -0,0 +1,251 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getUptimeESMockClient } from './helper'; +import { getNetworkEvents } from '../get_network_events'; + +describe('getNetworkEvents', () => { + let mockHits: any; + + beforeEach(() => { + mockHits = [ + { + _index: 'heartbeat-2020.12.14', + _id: 'YMfcYHYBOm8nKLizQt1o', + _score: null, + _source: { + '@timestamp': '2020-12-14T10:46:39.183Z', + synthetics: { + step: { + name: 'Click next link', + index: 2, + }, + journey: { + name: 'inline', + id: 'inline', + }, + type: 'journey/network_info', + package_version: '0.0.1-alpha.8', + payload: { + load_end_time: 3287.298251, + response_received_time: 3287.299074, + method: 'GET', + step: { + index: 2, + name: 'Click next link', + }, + status: 200, + type: 'Image', + request_sent_time: 3287.154973, + url: 'www.test.com', + request: { + initial_priority: 'Low', + referrer_policy: 'no-referrer-when-downgrade', + url: 'www.test.com', + method: 'GET', + headers: { + referer: 'www.test.com', + user_agent: + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/88.0.4324.0 Safari/537.36', + }, + mixed_content_type: 'none', + }, + response: { + from_service_worker: false, + security_details: { + protocol: 'TLS 1.2', + key_exchange: 'ECDHE_RSA', + valid_to: 1638230399, + certificate_transparency_compliance: 'unknown', + cipher: 'AES_128_GCM', + issuer: 'DigiCert TLS RSA SHA256 2020 CA1', + subject_name: 'syndication.twitter.com', + valid_from: 1606694400, + signed_certificate_timestamp_list: [], + key_exchange_group: 'P-256', + san_list: [ + 'syndication.twitter.com', + 'syndication.twimg.com', + 'cdn.syndication.twitter.com', + 'cdn.syndication.twimg.com', + 'syndication-o.twitter.com', + 'syndication-o.twimg.com', + ], + certificate_id: 0, + }, + security_state: 'secure', + connection_reused: true, + remote_port: 443, + timing: { + ssl_start: -1, + send_start: 0.214, + ssl_end: -1, + connect_start: -1, + connect_end: -1, + send_end: 0.402, + dns_start: -1, + request_time: 3287.155502, + push_end: 0, + worker_fetch_start: -1, + worker_ready: -1, + worker_start: -1, + proxy_end: -1, + push_start: 0, + worker_respond_with_settled: -1, + proxy_start: -1, + dns_end: -1, + receive_headers_end: 142.215, + }, + connection_id: 852, + remote_i_p_address: '104.244.42.200', + encoded_data_length: 337, + response_time: 1.60794279932414e12, + from_prefetch_cache: false, + mime_type: 'image/gif', + from_disk_cache: false, + url: 'www.test.com', + protocol: 'h2', + headers: { + x_frame_options: 'SAMEORIGIN', + cache_control: 'no-cache, no-store, must-revalidate, pre-check=0, post-check=0', + strict_transport_security: 'max-age=631138519', + x_twitter_response_tags: 'BouncerCompliant', + content_type: 'image/gif;charset=utf-8', + expires: 'Tue, 31 Mar 1981 05:00:00 GMT', + date: 'Mon, 14 Dec 2020 10:46:39 GMT', + x_transaction: '008fff3d00a1e64c', + x_connection_hash: 'cb6fe99b8676f4e4b827cc3e6512c90d', + last_modified: 'Mon, 14 Dec 2020 10:46:39 GMT', + x_content_type_options: 'nosniff', + content_encoding: 'gzip', + x_xss_protection: '0', + server: 'tsa_f', + x_response_time: '108', + pragma: 'no-cache', + content_length: '65', + status: '200 OK', + }, + status_text: '', + status: 200, + }, + timings: { + proxy: -1, + connect: -1, + receive: 0.5340000002433953, + blocked: 0.21400000014182297, + ssl: -1, + send: 0.18799999998009298, + total: 143.27800000000934, + queueing: 0.5289999999149586, + wait: 141.81299999972907, + dns: -1, + }, + is_navigation_request: false, + timestamp: 1607942799183375, + }, + }, + }, + }, + ]; + }); + + it('Uses the correct query', async () => { + const { uptimeEsClient, esClient } = getUptimeESMockClient(); + + esClient.search.mockResolvedValueOnce({ + body: { + hits: { + hits: mockHits, + }, + }, + } as any); + + await getNetworkEvents({ + uptimeEsClient, + checkGroup: 'my-fake-group', + stepIndex: '1', + }); + + expect(esClient.search.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "body": Object { + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "synthetics.type": "journey/network_info", + }, + }, + Object { + "term": Object { + "monitor.check_group": "my-fake-group", + }, + }, + Object { + "term": Object { + "synthetics.step.index": 1, + }, + }, + ], + }, + }, + "size": 1000, + }, + "index": "heartbeat-8*", + }, + ], + ] + `); + }); + + it('Returns the correct result', async () => { + const { esClient, uptimeEsClient } = getUptimeESMockClient(); + + esClient.search.mockResolvedValueOnce({ + body: { + hits: { + hits: mockHits, + }, + }, + } as any); + + const result = await getNetworkEvents({ + uptimeEsClient, + checkGroup: 'my-fake-group', + stepIndex: '1', + }); + + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "loadEndTime": 3287298.251, + "method": "GET", + "mimeType": "image/gif", + "requestSentTime": 3287154.973, + "requestStartTime": 3287155.502, + "status": 200, + "timestamp": "2020-12-14T10:46:39.183Z", + "timings": Object { + "blocked": 0.21400000014182297, + "connect": -1, + "dns": -1, + "proxy": -1, + "queueing": 0.5289999999149586, + "receive": 0.5340000002433953, + "send": 0.18799999998009298, + "ssl": -1, + "total": 143.27800000000934, + "wait": 141.81299999972907, + }, + "url": "www.test.com", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.ts new file mode 100644 index 0000000000000..ef11b00604490 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_details.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 { UMElasticsearchQueryFn } from '../adapters/framework'; +import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; + +interface GetJourneyDetails { + checkGroup: string; +} + +export const getJourneyDetails: UMElasticsearchQueryFn< + GetJourneyDetails, + SyntheticsJourneyApiResponse['details'] +> = async ({ uptimeEsClient, checkGroup }) => { + const baseParams = { + query: { + bool: { + filter: [ + { + term: { + 'monitor.check_group': checkGroup, + }, + }, + { + term: { + 'synthetics.type': 'journey/end', + }, + }, + ], + }, + }, + _source: ['@timestamp', 'monitor.id'], + size: 1, + }; + + const { body: thisJourney } = await uptimeEsClient.search({ body: baseParams }); + + if (thisJourney?.hits?.hits.length > 0) { + const thisJourneySource: any = thisJourney.hits.hits[0]._source; + + const baseSiblingParams = { + query: { + bool: { + filter: [ + { + term: { + 'monitor.id': thisJourneySource.monitor.id, + }, + }, + { + term: { + 'synthetics.type': 'journey/end', + }, + }, + ], + }, + }, + _source: ['@timestamp', 'monitor.check_group'], + size: 1, + }; + + const previousParams = { + ...baseSiblingParams, + query: { + bool: { + filter: [ + ...baseSiblingParams.query.bool.filter, + { + range: { + '@timestamp': { + lt: thisJourneySource['@timestamp'], + }, + }, + }, + ], + }, + }, + sort: [{ '@timestamp': { order: 'desc' } }], + }; + + const nextParams = { + ...baseSiblingParams, + query: { + bool: { + filter: [ + ...baseSiblingParams.query.bool.filter, + { + range: { + '@timestamp': { + gt: thisJourneySource['@timestamp'], + }, + }, + }, + ], + }, + }, + sort: [{ '@timestamp': { order: 'asc' } }], + }; + + const { body: previousJourneyResult } = await uptimeEsClient.search({ body: previousParams }); + const { body: nextJourneyResult } = await uptimeEsClient.search({ body: nextParams }); + const previousJourney: any = + previousJourneyResult?.hits?.hits.length > 0 ? previousJourneyResult?.hits?.hits[0] : null; + const nextJourney: any = + nextJourneyResult?.hits?.hits.length > 0 ? nextJourneyResult?.hits?.hits[0] : null; + return { + timestamp: thisJourneySource['@timestamp'], + previous: previousJourney + ? { + checkGroup: previousJourney._source.monitor.check_group, + timestamp: previousJourney._source['@timestamp'], + } + : undefined, + next: nextJourney + ? { + checkGroup: nextJourney._source.monitor.check_group, + timestamp: nextJourney._source['@timestamp'], + } + : undefined, + } as SyntheticsJourneyApiResponse['details']; + } else { + return null; + } +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index 1e323b57b30dc..cf29d3897f1ca 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -47,7 +47,7 @@ export const getLatestMonitor: UMElasticsearchQueryFn = async ({ uptimeEsClient, checkGroup, stepIndex }) => { + const params = { + query: { + bool: { + filter: [ + { term: { 'synthetics.type': 'journey/network_info' } }, + { term: { 'monitor.check_group': checkGroup } }, + { term: { 'synthetics.step.index': Number(stepIndex) } }, + ], + }, + }, + // NOTE: This limit may need tweaking in the future. Users can technically perform multiple + // navigations within one step, and may push up against this limit, however this manner + // of usage isn't advised. + size: 1000, + }; + + const { body: result } = await uptimeEsClient.search({ body: params }); + + const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000); + + return result.hits.hits.map((event: any) => { + const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time); + const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time); + const requestStartTime = + event._source.synthetics.payload.response && event._source.synthetics.payload.response.timing + ? microToMillis(event._source.synthetics.payload.response.timing.request_time) + : undefined; + + return { + timestamp: event._source['@timestamp'], + method: event._source.synthetics.payload?.method, + url: event._source.synthetics.payload?.url, + status: event._source.synthetics.payload?.status, + mimeType: event._source.synthetics.payload?.response?.mime_type, + requestSentTime, + requestStartTime, + loadEndTime, + timings: event._source.synthetics.payload.timings, + }; + }); +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index fd7e5f6041719..34137fe400b00 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -20,6 +20,8 @@ import { getSnapshotCount } from './get_snapshot_counts'; import { getIndexStatus } from './get_index_status'; import { getJourneySteps } from './get_journey_steps'; import { getJourneyScreenshot } from './get_journey_screenshot'; +import { getJourneyDetails } from './get_journey_details'; +import { getNetworkEvents } from './get_network_events'; import { getJourneyFailedSteps } from './get_journey_failed_steps'; export const requests = { @@ -40,6 +42,8 @@ export const requests = { getJourneySteps, getJourneyFailedSteps, getJourneyScreenshot, + getJourneyDetails, + getNetworkEvents, }; export type UptimeRequests = typeof requests; diff --git a/x-pack/plugins/uptime/server/rest_api/certs/certs.ts b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts index d377095a2a370..7af5717d8416d 100644 --- a/x-pack/plugins/uptime/server/rest_api/certs/certs.ts +++ b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts @@ -30,7 +30,7 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = direction: schema.maybe(schema.string()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const index = request.query?.index ?? 0; const size = request.query?.size ?? DEFAULT_SIZE; const from = request.query?.from ?? DEFAULT_FROM; @@ -38,7 +38,8 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = const sortBy = request.query?.sortBy ?? DEFAULT_SORT; const direction = request.query?.direction ?? DEFAULT_DIRECTION; const { search } = request.query; - const result = await libs.requests.getCerts({ + + return await libs.requests.getCerts({ uptimeEsClient, index, search, @@ -48,11 +49,5 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = sortBy, direction, }); - return response.ok({ - body: { - certs: result.certs, - total: result.total, - }, - }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts index 966dc20e27a7e..93593cc315d62 100644 --- a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts +++ b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts @@ -13,10 +13,22 @@ export const createRouteWithAuth = ( ): UptimeRoute => { const restRoute = routeCreator(libs); const { handler, method, path, options, ...rest } = restRoute; - const licenseCheckHandler: UMRouteHandler = async (customParams, context, request, response) => { + const licenseCheckHandler: UMRouteHandler = async ({ + uptimeEsClient, + context, + request, + response, + savedObjectsClient, + }) => { const { statusCode, message } = libs.license(context.licensing.license); if (statusCode === 200) { - return handler(customParams, context, request, response); + return handler({ + uptimeEsClient, + context, + request, + response, + savedObjectsClient, + }); } switch (statusCode) { case 400: @@ -29,6 +41,7 @@ export const createRouteWithAuth = ( return response.internalError(); } }; + return { method, path, diff --git a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts index f7be9e10d1004..3dd77be6eaf8c 100644 --- a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts @@ -20,12 +20,8 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSer method: 'GET', path: '/api/uptime/dynamic_settings', validate: false, - handler: async ({ savedObjectsClient }, _context, _request, response): Promise => { - const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); - - return response.ok({ - body: dynamicSettings, - }); + handler: async ({ savedObjectsClient }): Promise => { + return savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); }, }); @@ -60,7 +56,7 @@ export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSe }), }, writeAccess: true, - handler: async ({ savedObjectsClient }, _context, request, response): Promise => { + handler: async ({ savedObjectsClient, request, response }): Promise => { const decoded = DynamicSettingsType.decode(request.body); const certThresholdErrors = validateCertsValues(request.body as DynamicSettings); diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index a2475792edfbe..4db2da541079c 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -24,6 +24,7 @@ import { } from './monitors'; import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; +import { createNetworkEventsRoute } from './network_events'; import { createJourneyFailedStepsRoute } from './pings/journeys'; export * from './types'; @@ -48,5 +49,6 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetMonitorDurationRoute, createJourneyRoute, createJourneyScreenshotRoute, + createNetworkEventsRoute, createJourneyFailedStepsRoute, ]; diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts index 5c1be4cdd8143..671c6f0a71aa8 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts @@ -12,17 +12,9 @@ export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServer method: 'GET', path: API_URLS.INDEX_PATTERN, validate: false, - handler: async ({ uptimeEsClient }, _context, _request, response): Promise => { - try { - return response.ok({ - body: { - ...(await libs.requests.getIndexPattern({ - uptimeEsClient, - })), - }, - }); - } catch (e) { - return response.internalError({ body: { message: e.message } }); - } + handler: async ({ uptimeEsClient }): Promise => { + return await libs.requests.getIndexPattern({ + uptimeEsClient, + }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts index e57643aed7e36..a347baf770d6a 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; import { API_URLS } from '../../../common/constants'; @@ -11,18 +12,12 @@ import { API_URLS } from '../../../common/constants'; export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: API_URLS.INDEX_STATUS, - validate: false, - handler: async ({ uptimeEsClient }, _context, _request, response): Promise => { - try { - return response.ok({ - body: { - ...(await libs.requests.getIndexStatus({ - uptimeEsClient, - })), - }, - }); - } catch (e) { - return response.internalError({ body: { message: e.message } }); - } + validate: { + query: schema.object({ + _debug: schema.maybe(schema.boolean()), + }), + }, + handler: async ({ uptimeEsClient }): Promise => { + return await libs.requests.getIndexStatus({ uptimeEsClient }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts index 50fe616ae9cb5..2c3c649bf68c6 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_list.ts @@ -19,52 +19,39 @@ export const createMonitorListRoute: UMRestApiRouteFactory = (libs) => ({ pagination: schema.maybe(schema.string()), statusFilter: schema.maybe(schema.string()), pageSize: schema.number(), + _debug: schema.maybe(schema.boolean()), }), }, options: { tags: ['access:uptime-read'], }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { - try { - const { - dateRangeStart, - dateRangeEnd, - filters, - pagination, - statusFilter, - pageSize, - } = request.query; + handler: async ({ uptimeEsClient, request }): Promise => { + const { + dateRangeStart, + dateRangeEnd, + filters, + pagination, + statusFilter, + pageSize, + } = request.query; - const decodedPagination = pagination - ? JSON.parse(decodeURIComponent(pagination)) - : CONTEXT_DEFAULTS.CURSOR_PAGINATION; + const decodedPagination = pagination + ? JSON.parse(decodeURIComponent(pagination)) + : CONTEXT_DEFAULTS.CURSOR_PAGINATION; - const { - summaries, - nextPagePagination, - prevPagePagination, - } = await libs.requests.getMonitorStates({ - uptimeEsClient, - dateRangeStart, - dateRangeEnd, - pagination: decodedPagination, - pageSize, - filters, - // this is added to make typescript happy, - // this sort of reassignment used to be further downstream but I've moved it here - // because this code is going to be decomissioned soon - statusFilter: statusFilter || undefined, - }); + const result = await libs.requests.getMonitorStates({ + uptimeEsClient, + dateRangeStart, + dateRangeEnd, + pagination: decodedPagination, + pageSize, + filters, + // this is added to make typescript happy, + // this sort of reassignment used to be further downstream but I've moved it here + // because this code is going to be decomissioned soon + statusFilter: statusFilter || undefined, + }); - return response.ok({ - body: { - summaries, - nextPagePagination, - prevPagePagination, - }, - }); - } catch (e) { - return response.internalError({ body: { message: e.message } }); - } + return result; }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts index e2dbf7114fc91..48a39d94ac69e 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts @@ -17,20 +17,17 @@ export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMSe monitorId: schema.string(), dateStart: schema.string(), dateEnd: schema.string(), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const { monitorId, dateStart, dateEnd } = request.query; - return response.ok({ - body: { - ...(await libs.requests.getMonitorLocations({ - uptimeEsClient, - monitorId, - dateStart, - dateEnd, - })), - }, + return await libs.requests.getMonitorLocations({ + uptimeEsClient, + monitorId, + dateStart, + dateEnd, }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts index 3d47f0eab8640..94edc6e4d7efe 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts @@ -12,26 +12,22 @@ import { API_URLS } from '../../../common/constants'; export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', path: API_URLS.MONITOR_STATUS, - validate: { query: schema.object({ monitorId: schema.string(), dateStart: schema.string(), dateEnd: schema.string(), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const { monitorId, dateStart, dateEnd } = request.query; - const result = await libs.requests.getLatestMonitor({ + + return await libs.requests.getLatestMonitor({ uptimeEsClient, monitorId, dateStart, dateEnd, }); - return response.ok({ - body: { - ...result, - }, - }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts index 0982fc1986604..efbdf69a883d6 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -17,23 +17,20 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServ monitorId: schema.string(), dateStart: schema.maybe(schema.string()), dateEnd: schema.maybe(schema.string()), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, context, request, response): Promise => { + handler: async ({ uptimeEsClient, context, request }): Promise => { const { monitorId, dateStart, dateEnd } = request.query; const alertsClient = context.alerting?.getAlertsClient(); - return response.ok({ - body: { - ...(await libs.requests.getMonitorDetails({ - uptimeEsClient, - monitorId, - dateStart, - dateEnd, - alertsClient, - })), - }, + return await libs.requests.getMonitorDetails({ + uptimeEsClient, + monitorId, + dateStart, + dateEnd, + alertsClient, }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts index eec3fdf9e7257..09d0ce4555309 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts @@ -18,20 +18,17 @@ export const createGetMonitorDurationRoute: UMRestApiRouteFactory = (libs: UMSer monitorId: schema.string(), dateStart: schema.string(), dateEnd: schema.string(), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const { monitorId, dateStart, dateEnd } = request.query; - return response.ok({ - body: { - ...(await libs.requests.getMonitorDurationChart({ - uptimeEsClient, - monitorId, - dateStart, - dateEnd, - })), - }, + return await libs.requests.getMonitorDurationChart({ + uptimeEsClient, + monitorId, + dateStart, + dateEnd, }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts new file mode 100644 index 0000000000000..f24b319baff00 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/network_events/get_network_events.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createNetworkEventsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/network_events', + validate: { + query: schema.object({ + checkGroup: schema.string(), + stepIndex: schema.number(), + }), + }, + handler: async ({ uptimeEsClient, request }): Promise => { + const { checkGroup, stepIndex } = request.query; + + const result = await libs.requests.getNetworkEvents({ + uptimeEsClient, + checkGroup, + stepIndex, + }); + + return { + events: result, + }; + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/network_events/index.ts b/x-pack/plugins/uptime/server/rest_api/network_events/index.ts new file mode 100644 index 0000000000000..3f3c1afe06f99 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/network_events/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 { createNetworkEventsRoute } from './get_network_events'; diff --git a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts index 163fbd4f8dd6e..ac58a8002899b 100644 --- a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts +++ b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts @@ -26,9 +26,10 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi schemes: arrayOrStringType, ports: arrayOrStringType, tags: arrayOrStringType, + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response) => { + handler: async ({ uptimeEsClient, request, response }): Promise => { const { dateRangeStart, dateRangeEnd, locations, schemes, search, ports, tags } = request.query; let parsedSearch: Record | undefined; @@ -40,7 +41,7 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi } } - const filtersResponse = await libs.requests.getFilterBar({ + return await libs.requests.getFilterBar({ uptimeEsClient, dateRangeStart, dateRangeEnd, @@ -52,7 +53,5 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi tags, }), }); - - return response.ok({ body: { ...filtersResponse } }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts index ba36b171793b7..4797e4aae94bf 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts @@ -19,12 +19,13 @@ export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServe monitorId: schema.maybe(schema.string()), filters: schema.maybe(schema.string()), bucketSize: schema.maybe(schema.string()), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const { dateStart, dateEnd, monitorId, filters, bucketSize } = request.query; - const result = await libs.requests.getPingHistogram({ + return await libs.requests.getPingHistogram({ uptimeEsClient, from: dateStart, to: dateEnd, @@ -32,11 +33,5 @@ export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServe filters, bucketSize, }); - - return response.ok({ - body: { - ...result, - }, - }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts index 2a1a401ec3153..fa1ef565d0146 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts @@ -22,12 +22,13 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = size: schema.maybe(schema.number()), sort: schema.maybe(schema.string()), status: schema.maybe(schema.string()), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request, response }): Promise => { const { from, to, index, monitorId, status, sort, size, locations } = request.query; - const result = await libs.requests.getPings({ + return await libs.requests.getPings({ uptimeEsClient, dateRange: { from, to }, index, @@ -37,11 +38,5 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = size, locations: locations ? JSON.parse(locations) : [], }); - - return response.ok({ - body: { - ...result, - }, - }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts index 080fc8ab8f8ee..8343b24e601a1 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts @@ -15,10 +15,12 @@ export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServ params: schema.object({ checkGroup: schema.string(), stepIndex: schema.number(), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response) => { + handler: async ({ uptimeEsClient, request, response }) => { const { checkGroup, stepIndex } = request.params; + const result = await libs.requests.getJourneyScreenshot({ uptimeEsClient, checkGroup, diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts index f46cf3b990951..b2559ee8d7054 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -14,21 +14,26 @@ export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => validate: { params: schema.object({ checkGroup: schema.string(), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response) => { + handler: async ({ uptimeEsClient, request }): Promise => { const { checkGroup } = request.params; const result = await libs.requests.getJourneySteps({ uptimeEsClient, checkGroup, }); - return response.ok({ - body: { - checkGroup, - steps: result, - }, + const details = await libs.requests.getJourneyDetails({ + uptimeEsClient, + checkGroup, }); + + return { + checkGroup, + steps: result, + details, + }; }, }); @@ -40,18 +45,16 @@ export const createJourneyFailedStepsRoute: UMRestApiRouteFactory = (libs: UMSer checkGroups: schema.arrayOf(schema.string()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response) => { + handler: async ({ uptimeEsClient, request }): Promise => { const { checkGroups } = request.query; const result = await libs.requests.getJourneyFailedSteps({ uptimeEsClient, checkGroups, }); - return response.ok({ - body: { - checkGroups, - steps: result, - }, - }); + return { + checkGroups, + steps: result, + }; }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts index 224ef87fd90af..2d22259fbf786 100644 --- a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts +++ b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts @@ -17,20 +17,17 @@ export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs dateRangeStart: schema.string(), dateRangeEnd: schema.string(), filters: schema.maybe(schema.string()), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ({ uptimeEsClient }, _context, request, response): Promise => { + handler: async ({ uptimeEsClient, request }): Promise => { const { dateRangeStart, dateRangeEnd, filters } = request.query; - const result = await libs.requests.getSnapshotCount({ + + return await libs.requests.getSnapshotCount({ uptimeEsClient, dateRangeStart, dateRangeEnd, filters, }); - return response.ok({ - body: { - ...result, - }, - }); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts index 85f274c96cf9a..e69556837af44 100644 --- a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts +++ b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts @@ -21,14 +21,10 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ autoRefreshEnabled: schema.boolean(), autorefreshInterval: schema.number(), refreshTelemetryHistory: schema.maybe(schema.boolean()), + _debug: schema.maybe(schema.boolean()), }), }, - handler: async ( - { savedObjectsClient, uptimeEsClient }, - _context, - request, - response - ): Promise => { + handler: async ({ savedObjectsClient, uptimeEsClient, request }): Promise => { const pageView = request.body as PageViewParams; if (pageView.refreshTelemetryHistory) { KibanaTelemetryAdapter.clearLocalTelemetry(); @@ -37,10 +33,6 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ uptimeEsClient, savedObjectsClient ); - const pageViewResult = KibanaTelemetryAdapter.countPageView(pageView as PageViewParams); - - return response.ok({ - body: pageViewResult, - }); + return KibanaTelemetryAdapter.countPageView(pageView as PageViewParams); }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index df1762a3b318d..4e627cebb3459 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -14,7 +14,6 @@ import { KibanaRequest, KibanaResponseFactory, IKibanaResponse, - IScopedClusterClient, } from 'kibana/server'; import { UMServerLibs, UptimeESClient } from '../lib/lib'; @@ -59,20 +58,18 @@ export type UMRestApiRouteFactory = (libs: UMServerLibs) => UptimeRoute; export type UMKibanaRouteWrapper = (uptimeRoute: UptimeRoute) => UMKibanaRoute; /** - * This type can store custom parameters used by the internal Uptime route handlers. + * This is the contract we specify internally for route handling. */ -export interface UMRouteParams { +export type UMRouteHandler = ({ + uptimeEsClient, + context, + request, + response, + savedObjectsClient, +}: { uptimeEsClient: UptimeESClient; - esClient: IScopedClusterClient; + context: RequestHandlerContext; + request: KibanaRequest, Record, Record>; + response: KibanaResponseFactory; savedObjectsClient: SavedObjectsClientContract; -} - -/** - * This is the contract we specify internally for route handling. - */ -export type UMRouteHandler = ( - params: UMRouteParams, - context: RequestHandlerContext, - request: KibanaRequest, Record, Record>, - response: KibanaResponseFactory -) => IKibanaResponse | Promise>; +}) => IKibanaResponse | Promise>; diff --git a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts index a1cf3c05e2de3..c8769dc4ea3df 100644 --- a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts +++ b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts @@ -7,6 +7,9 @@ import { UMKibanaRouteWrapper } from './types'; import { createUptimeESClient } from '../lib/lib'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { KibanaResponse } from '../../../../../src/core/server/http/router'; + export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ ...uptimeRoute, options: { @@ -17,15 +20,39 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute) => ({ const { client: savedObjectsClient } = context.core.savedObjects; const uptimeEsClient = createUptimeESClient({ + request, savedObjectsClient, esClient: esClient.asCurrentUser, }); - return uptimeRoute.handler( - { uptimeEsClient, esClient, savedObjectsClient }, - context, - request, - response - ); + try { + const res = await uptimeRoute.handler({ + uptimeEsClient, + savedObjectsClient, + context, + request, + response, + }); + + if (res instanceof KibanaResponse) { + return res; + } + + return response.ok({ + body: { + ...res, + }, + }); + } catch (e) { + // please don't remove this, this will be really helpful during debugging + /* eslint-disable-next-line no-console */ + console.error(e); + + return response.internalError({ + body: { + message: e.message, + }, + }); + } }, }); diff --git a/x-pack/scripts/jest.js b/x-pack/scripts/jest.js index 68cfcf082f818..aca7e558301df 100644 --- a/x-pack/scripts/jest.js +++ b/x-pack/scripts/jest.js @@ -4,15 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -if (process.argv.indexOf('--config') === -1) { - // append correct jest.config if none is provided - const configPath = require('path').resolve(__dirname, '../jest.config.js'); - process.argv.push('--config', configPath); - console.log('Running Jest with --config', configPath); -} - -if (process.env.NODE_ENV == null) { - process.env.NODE_ENV = 'test'; -} - -require('jest').run(); +require('@kbn/test').runJest(); diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts new file mode 100644 index 0000000000000..f6b0ef2a773f1 --- /dev/null +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getTestAlertData } from '../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function basicAlertTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('basic alert', () => { + it('should return 200 when creating a basic license alert', async () => { + await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts new file mode 100644 index 0000000000000..3ba9d43cdedf0 --- /dev/null +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.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 { getTestAlertData } from '../../../common/lib'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function emailTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('create gold noop alert', () => { + it('should return 403 when creating an gold alert', async () => { + await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ alertTypeId: 'test.gold.noop' })) + .expect(403, { + statusCode: 403, + error: 'Forbidden', + message: + 'Alert test.gold.noop is disabled because it requires a Gold license. Contact your administrator to upgrade your license.', + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/index.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/index.ts new file mode 100644 index 0000000000000..84fceb9a6c0f4 --- /dev/null +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/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 '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function alertingTests({ loadTestFile }: FtrProviderContext) { + describe('Alerts', () => { + loadTestFile(require.resolve('./gold_noop_alert_type')); + loadTestFile(require.resolve('./basic_noop_alert_type')); + }); +} diff --git a/x-pack/test/alerting_api_integration/basic/tests/index.ts b/x-pack/test/alerting_api_integration/basic/tests/index.ts index 7f3152cc38ca8..80152cca07c60 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/index.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/index.ts @@ -15,5 +15,6 @@ export default function alertingApiIntegrationTests({ this.tags('ciGroup3'); loadTestFile(require.resolve('./actions')); + loadTestFile(require.resolve('./alerts')); }); } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/slack_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/slack_simulation.ts index 8f5b1ea75d188..dcbfff81cd85d 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/slack_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/slack_simulation.ts @@ -7,7 +7,17 @@ import http from 'http'; export async function initPlugin() { + const messages: string[] = []; + return http.createServer((request, response) => { + // return the messages that were posted to be remembered + if (request.method === 'GET') { + response.statusCode = 200; + response.setHeader('Content-Type', 'application/json'); + response.end(JSON.stringify(messages, null, 4)); + return; + } + if (request.method === 'POST') { let data = ''; request.on('data', (chunk) => { @@ -15,7 +25,7 @@ export async function initPlugin() { }); request.on('end', () => { const body = JSON.parse(data); - const text = body && body.text; + const text: string = body && body.text; if (text == null) { response.statusCode = 400; @@ -23,6 +33,15 @@ export async function initPlugin() { return; } + // store a message that was posted to be remembered + const match = text.match(/^message (.*)$/); + if (match) { + messages.push(match[1]); + response.statusCode = 200; + response.end('ok'); + return; + } + switch (text) { case 'success': { response.statusCode = 200; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/webhook_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/webhook_simulation.ts index 44d8ea0c2da20..a34293090d7af 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/webhook_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/webhook_simulation.ts @@ -10,6 +10,8 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { constant } from 'fp-ts/lib/function'; export async function initPlugin() { + const payloads: string[] = []; + return http.createServer((request, response) => { const credentials = pipe( fromNullable(request.headers.authorization), @@ -24,6 +26,14 @@ export async function initPlugin() { getOrElse(constant({ username: '', password: '' })) ); + // return the payloads that were posted to be remembered + if (request.method === 'GET') { + response.statusCode = 200; + response.setHeader('Content-Type', 'application/json'); + response.end(JSON.stringify(payloads, null, 4)); + return; + } + if (request.method === 'POST' || request.method === 'PUT') { let data = ''; request.on('data', (chunk) => { @@ -46,10 +56,18 @@ export async function initPlugin() { response.end('Error'); return; } + + // store a payload that was posted to be remembered + const match = data.match(/^payload (.*)$/); + if (match) { + payloads.push(match[1]); + response.statusCode = 200; + response.end('ok'); + return; + } + response.statusCode = 400; - response.end( - `unknown request to webhook simulator [${data ? `content: ${data}` : `no content`}]` - ); + response.end(`unexpected body ${data}`); return; }); } else { diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index 93ee72082d387..11065edd4beeb 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -6,7 +6,7 @@ import { CoreSetup } from 'src/core/server'; import { schema, TypeOf } from '@kbn/config-schema'; -import { times } from 'lodash'; +import { curry, times } from 'lodash'; import { ES_TEST_INDEX_NAME } from '../../../../lib'; import { FixtureStartDeps, FixtureSetupDeps } from './plugin'; import { @@ -15,6 +15,15 @@ import { AlertInstanceContext, } from '../../../../../../../plugins/alerts/server'; +export const EscapableStrings = { + escapableBold: '*bold*', + escapableBacktic: 'back`tic', + escapableBackticBold: '`*bold*`', + escapableHtml: '<&>', + escapableDoubleQuote: '"double quote"', + escapableLineFeed: 'line\x0afeed', +}; + function getAlwaysFiringAlertType() { const paramsSchema = schema.object({ index: schema.string(), @@ -43,72 +52,73 @@ function getAlwaysFiringAlertType() { }, producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', actionVariables: { state: [{ name: 'instanceStateValue', description: 'the instance state value' }], params: [{ name: 'instanceParamsValue', description: 'the instance params value' }], context: [{ name: 'instanceContextValue', description: 'the instance context value' }], }, - async executor(alertExecutorOptions) { - const { - services, - params, - state, - alertId, - spaceId, - namespace, - name, - tags, - createdBy, - updatedBy, - } = alertExecutorOptions; - let group: string | null = 'default'; - let subgroup: string | null = null; - const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy }; + executor: curry(alwaysFiringExecutor)(), + }; + return result; +} - if (params.groupsToScheduleActionsInSeries) { - const index = state.groupInSeriesIndex || 0; - const [scheduledGroup, scheduledSubgroup] = ( - params.groupsToScheduleActionsInSeries[index] ?? '' - ).split(':'); +async function alwaysFiringExecutor(alertExecutorOptions: any) { + const { + services, + params, + state, + alertId, + spaceId, + namespace, + name, + tags, + createdBy, + updatedBy, + } = alertExecutorOptions; + let group: string | null = 'default'; + let subgroup: string | null = null; + const alertInfo = { alertId, spaceId, namespace, name, tags, createdBy, updatedBy }; - group = scheduledGroup; - subgroup = scheduledSubgroup; - } + if (params.groupsToScheduleActionsInSeries) { + const index = state.groupInSeriesIndex || 0; + const [scheduledGroup, scheduledSubgroup] = ( + params.groupsToScheduleActionsInSeries[index] ?? '' + ).split(':'); - if (group) { - const instance = services - .alertInstanceFactory('1') - .replaceState({ instanceStateValue: true }); + group = scheduledGroup; + subgroup = scheduledSubgroup; + } - if (subgroup) { - instance.scheduleActionsWithSubGroup(group, subgroup, { - instanceContextValue: true, - }); - } else { - instance.scheduleActions(group, { - instanceContextValue: true, - }); - } - } + if (group) { + const instance = services.alertInstanceFactory('1').replaceState({ instanceStateValue: true }); - await services.scopedClusterClient.index({ - index: params.index, - refresh: 'wait_for', - body: { - state, - params, - reference: params.reference, - source: 'alert:test.always-firing', - alertInfo, - }, + if (subgroup) { + instance.scheduleActionsWithSubGroup(group, subgroup, { + instanceContextValue: true, }); - return { - globalStateValue: true, - groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1, - }; + } else { + instance.scheduleActions(group, { + instanceContextValue: true, + }); + } + } + + await services.scopedClusterClient.index({ + index: params.index, + refresh: 'wait_for', + body: { + state, + params, + reference: params.reference, + source: 'alert:test.always-firing', + alertInfo, }, + }); + return { + globalStateValue: true, + groupInSeriesIndex: (state.groupInSeriesIndex || 0) + 1, }; - return result; } function getCumulativeFiringAlertType() { @@ -127,6 +137,7 @@ function getCumulativeFiringAlertType() { ], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor(alertExecutorOptions) { const { services, state } = alertExecutorOptions; const group = 'default'; @@ -145,7 +156,7 @@ function getCumulativeFiringAlertType() { }; }, }; - return result; + return result as AlertType; } function getNeverFiringAlertType() { @@ -171,6 +182,7 @@ function getNeverFiringAlertType() { }, producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor({ services, params, state }) { await services.callCluster('index', { index: params.index, @@ -210,6 +222,7 @@ function getFailingAlertType() { ], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor({ services, params, state }) { await services.callCluster('index', { index: params.index, @@ -248,6 +261,7 @@ function getAuthorizationAlertType(core: CoreSetup) { ], defaultActionGroupId: 'default', producer: 'alertsFixture', + minimumLicenseRequired: 'basic', validate: { params: paramsSchema, }, @@ -333,6 +347,7 @@ function getValidationAlertType() { }, ], producer: 'alertsFixture', + minimumLicenseRequired: 'basic', defaultActionGroupId: 'default', validate: { params: paramsSchema, @@ -360,6 +375,7 @@ function getPatternFiringAlertType() { actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor(alertExecutorOptions) { const { services, state, params } = alertExecutorOptions; const pattern = params.pattern; @@ -394,7 +410,7 @@ function getPatternFiringAlertType() { for (const [instanceId, instancePattern] of Object.entries(pattern)) { const scheduleByPattern = instancePattern[patternIndex]; if (scheduleByPattern === true) { - services.alertInstanceFactory(instanceId).scheduleActions('default'); + services.alertInstanceFactory(instanceId).scheduleActions('default', EscapableStrings); } else if (typeof scheduleByPattern === 'string') { services .alertInstanceFactory(instanceId) @@ -420,6 +436,16 @@ export function defineAlertTypes( actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + }; + const goldNoopAlertType: AlertType = { + id: 'test.gold.noop', + name: 'Test: Noop', + actionGroups: [{ id: 'default', name: 'Default' }], + producer: 'alertsFixture', + defaultActionGroupId: 'default', + minimumLicenseRequired: 'gold', async executor() {}, }; const onlyContextVariablesAlertType: AlertType = { @@ -428,6 +454,7 @@ export function defineAlertTypes( actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', actionVariables: { context: [{ name: 'aContextVariable', description: 'this is a context variable' }], }, @@ -442,6 +469,7 @@ export function defineAlertTypes( actionVariables: { state: [{ name: 'aStateVariable', description: 'this is a state variable' }], }, + minimumLicenseRequired: 'basic', async executor() {}, }; const throwAlertType: AlertType = { @@ -455,6 +483,7 @@ export function defineAlertTypes( ], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor() { throw new Error('this alert is intended to fail'); }, @@ -470,6 +499,7 @@ export function defineAlertTypes( ], producer: 'alertsFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor() { await new Promise((resolve) => setTimeout(resolve, 5000)); }, @@ -487,4 +517,5 @@ export function defineAlertTypes( alerts.registerType(getPatternFiringAlertType()); alerts.registerType(throwAlertType); alerts.registerType(longRunningAlertType); + alerts.registerType(goldNoopAlertType); } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts index 3e3c44f2c2784..3a81d41a2ca9c 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts @@ -18,6 +18,7 @@ export function defineAlertTypes( actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsRestrictedFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', recoveryActionGroup: { id: 'restrictedRecovered', name: 'Restricted Recovery' }, async executor({ services, params, state }: AlertExecutorOptions) {}, }; @@ -27,6 +28,7 @@ export function defineAlertTypes( actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsRestrictedFixture', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor({ services, params, state }: AlertExecutorOptions) {}, }; alerts.registerType(noopRestrictedAlertType); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts index 1ce04683f79bf..87cc355a58568 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts @@ -28,10 +28,12 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { params: [], }, producer: 'alertsFixture', + minimumLicenseRequired: 'basic', recoveryActionGroup: { id: 'recovered', name: 'Recovered', }, + enabledInLicense: true, }; const expectedRestrictedNoOpType = { @@ -52,6 +54,8 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { params: [], }, producer: 'alertsRestrictedFixture', + minimumLicenseRequired: 'basic', + enabledInLicense: true, }; describe('list_alert_types', () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts index 2b24a75fab844..e97734f89c2cd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts @@ -34,6 +34,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./alerts_space1')); loadTestFile(require.resolve('./alerts_default_space')); loadTestFile(require.resolve('./builtin_alert_types')); + loadTestFile(require.resolve('./mustache_templates.ts')); loadTestFile(require.resolve('./notify_when')); // note that this test will destroy existing spaces diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts index c76a43b05b172..74deaf4c7296f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts @@ -40,6 +40,8 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { name: 'Recovered', }, producer: 'alertsFixture', + minimumLicenseRequired: 'basic', + enabledInLicense: true, }); expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts new file mode 100644 index 0000000000000..438438505f464 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * These tests ensure that the per-action mustache template escaping works + * for actions we have simulators for. It arranges to have an alert that + * schedules an action that will contain "escapable" characters in it, and + * then validates that the simulator receives the escaped versions. + */ + +import http from 'http'; +import getPort from 'get-port'; +import { URL, format as formatUrl } from 'url'; +import axios from 'axios'; + +import expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { + getWebhookServer, + getSlackServer, +} from '../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function executionStatusAlertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const retry = getService('retry'); + + describe('mustacheTemplates', () => { + const objectRemover = new ObjectRemover(supertest); + let webhookSimulatorURL: string = ''; + let webhookServer: http.Server; + let slackSimulatorURL: string = ''; + let slackServer: http.Server; + + before(async () => { + let availablePort: number; + + webhookServer = await getWebhookServer(); + availablePort = await getPort({ port: 9000 }); + webhookServer.listen(availablePort); + webhookSimulatorURL = `http://localhost:${availablePort}`; + + slackServer = await getSlackServer(); + availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + if (!slackServer.listening) { + slackServer.listen(availablePort); + } + slackSimulatorURL = `http://localhost:${availablePort}`; + }); + + after(async () => { + await objectRemover.removeAll(); + webhookServer.close(); + slackServer.close(); + }); + + it('should handle escapes in webhook', async () => { + const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); + const actionResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'test') + .send({ + name: 'testing mustache escapes for webhook', + actionTypeId: '.webhook', + secrets: {}, + config: { + headers: { + 'Content-Type': 'text/plain', + }, + url, + }, + }); + expect(actionResponse.status).to.eql(200); + const createdAction = actionResponse.body; + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + // from x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts + const varsTemplate = '{{context.escapableDoubleQuote}} -- {{context.escapableLineFeed}}'; + + const alertResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + name: 'testing variable escapes for webhook', + alertTypeId: 'test.patternFiring', + params: { + pattern: { instance: [true] }, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: { + body: `payload {{alertId}} - ${varsTemplate}`, + }, + }, + ], + }) + ); + expect(alertResponse.status).to.eql(200); + const createdAlert = alertResponse.body; + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + + const body = await retry.try(async () => + waitForActionBody(webhookSimulatorURL, createdAlert.id) + ); + expect(body).to.be(`\\"double quote\\" -- line\\nfeed`); + }); + + it('should handle escapes in slack', async () => { + const actionResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'test') + .send({ + name: "testing backtic'd mustache escapes for slack", + actionTypeId: '.slack', + secrets: { + webhookUrl: slackSimulatorURL, + }, + }); + expect(actionResponse.status).to.eql(200); + const createdAction = actionResponse.body; + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + // from x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts + const varsTemplate = + '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}}'; + + const alertResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + name: 'testing variable escapes for slack', + alertTypeId: 'test.patternFiring', + params: { + pattern: { instance: [true] }, + }, + actions: [ + { + id: createdAction.id, + group: 'default', + params: { + message: `message {{alertId}} - ${varsTemplate}`, + }, + }, + ], + }) + ); + expect(alertResponse.status).to.eql(200); + const createdAlert = alertResponse.body; + objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + + const body = await retry.try(async () => + waitForActionBody(slackSimulatorURL, createdAlert.id) + ); + expect(body).to.be("back'tic -- `*bold*` -- `'*bold*'` -- <&>"); + }); + }); + + async function waitForActionBody(url: string, id: string): Promise { + const response = await axios.get(url); + expect(response.status).to.eql(200); + + for (const datum of response.data) { + const match = datum.match(/^(.*) - (.*)$/); + if (match == null) continue; + + if (match[1] === id) return match[2]; + } + + throw new Error(`no action body posted yet for id ${id}`); + } +} diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index 6cf1a40a4d5a1..a8999fd065e75 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -14,6 +14,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; // @ts-ignore import { API_BASE_PATH } from './constants'; +import { DataStream } from '../../../../../plugins/index_management/common'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -66,32 +67,41 @@ export default function ({ getService }: FtrProviderContext) { before(async () => await createDataStream(testDataStreamName)); after(async () => await deleteDataStream(testDataStreamName)); - it('returns an array of all data streams', async () => { + it('returns an array of data streams', async () => { const { body: dataStreams } = await supertest .get(`${API_BASE_PATH}/data_streams`) .set('kbn-xsrf', 'xxx') .expect(200); + expect(dataStreams).to.be.an('array'); + + // returned array can contain automatically created data streams + const testDataStream = dataStreams.find( + (dataStream: DataStream) => dataStream.name === testDataStreamName + ); + + expect(testDataStream).to.be.ok(); + // ES determines these values so we'll just echo them back. - const { name: indexName, uuid } = dataStreams[0].indices[0]; - expect(dataStreams).to.eql([ - { - name: testDataStreamName, - privileges: { - delete_index: true, - }, - timeStampField: { name: '@timestamp' }, - indices: [ - { - name: indexName, - uuid, - }, - ], - generation: 1, - health: 'yellow', - indexTemplateName: testDataStreamName, + const { name: indexName, uuid } = testDataStream!.indices[0]; + + expect(testDataStream).to.eql({ + name: testDataStreamName, + privileges: { + delete_index: true, }, - ]); + timeStampField: { name: '@timestamp' }, + indices: [ + { + name: indexName, + uuid, + }, + ], + generation: 1, + health: 'yellow', + indexTemplateName: testDataStreamName, + hidden: false, + }); }); it('includes stats when provided the includeStats query parameter', async () => { @@ -100,12 +110,21 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'xxx') .expect(200); + expect(dataStreams).to.be.an('array'); + + // returned array can contain automatically created data streams + const testDataStream = dataStreams.find( + (dataStream: DataStream) => dataStream.name === testDataStreamName + ); + + expect(testDataStream).to.be.ok(); + // ES determines these values so we'll just echo them back. - const { name: indexName, uuid } = dataStreams[0].indices[0]; - const { storageSize, ...dataStreamWithoutStorageSize } = dataStreams[0]; + + const { name: indexName, uuid } = testDataStream!.indices[0]; + const { storageSize, ...dataStreamWithoutStorageSize } = testDataStream!; assertDataStreamStorageSizeExists(storageSize); - expect(dataStreams.length).to.be(1); expect(dataStreamWithoutStorageSize).to.eql({ name: testDataStreamName, privileges: { @@ -122,6 +141,7 @@ export default function ({ getService }: FtrProviderContext) { health: 'yellow', indexTemplateName: testDataStreamName, maxTimeStamp: 0, + hidden: false, }); }); @@ -152,6 +172,7 @@ export default function ({ getService }: FtrProviderContext) { health: 'yellow', indexTemplateName: testDataStreamName, maxTimeStamp: 0, + hidden: false, }); }); }); diff --git a/x-pack/test/api_integration/apis/ml/index.ts b/x-pack/test/api_integration/apis/ml/index.ts index 1bee29daf7b7d..05d682625d8c8 100644 --- a/x-pack/test/api_integration/apis/ml/index.ts +++ b/x-pack/test/api_integration/apis/ml/index.ts @@ -34,6 +34,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await ml.testResources.deleteIndexPatternByTitle('ft_module_siem_packetbeat'); await ml.testResources.deleteIndexPatternByTitle('ft_module_siem_winlogbeat'); await ml.testResources.deleteIndexPatternByTitle('ft_farequote'); + await ml.testResources.deleteIndexPatternByTitle('ft_logs-endpoint.events.*'); await esArchiver.unload('ml/ecommerce'); await esArchiver.unload('ml/categorization'); @@ -45,6 +46,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.unload('ml/module_nginx'); await esArchiver.unload('ml/module_sample_ecommerce'); await esArchiver.unload('ml/module_sample_logs'); + await esArchiver.unload('ml/module_security_endpoint'); await esArchiver.unload('ml/module_siem_auditbeat'); await esArchiver.unload('ml/module_siem_packetbeat'); await esArchiver.unload('ml/module_siem_winlogbeat'); diff --git a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts index 64f53bbe76c5e..5b70b669aa876 100644 --- a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts @@ -135,6 +135,16 @@ export default ({ getService }: FtrProviderContext) => { moduleIds: ['auditbeat_process_hosts_ecs', 'security_linux', 'siem_auditbeat'], }, }, + { + testTitleSuffix: 'for security endpoint dataset', + sourceDataArchive: 'ml/module_security_endpoint', + indexPattern: 'ft_logs-endpoint.events.*', + user: USER.ML_POWERUSER, + expected: { + responseCode: 200, + moduleIds: ['security_linux', 'security_windows'], + }, + }, ]; async function executeRecognizeModuleRequest(indexPattern: string, user: USER, rspCode: number) { diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts index c86cd8400a71a..d1316a0ededc6 100644 --- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -520,6 +520,134 @@ export default ({ getService }: FtrProviderContext) => { ] as string[], }, }, + { + testTitleSuffix: + 'for security_linux with prefix, startDatafeed true and estimateModelMemory true', + sourceDataArchive: 'ml/module_security_endpoint', + indexPattern: { name: 'ft_logs-endpoint.events.*', timeField: '@timestamp' }, + module: 'security_linux', + user: USER.ML_POWERUSER, + requestBody: { + prefix: 'pf15_', + indexPatternName: 'ft_logs-endpoint.events.*', + startDatafeed: true, + end: Date.now(), + }, + expected: { + responseCode: 200, + jobs: [ + { + jobId: 'pf15_v2_rare_process_by_host_linux_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf15_v2_linux_rare_metadata_user', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf15_v2_linux_rare_metadata_process', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf15_v2_linux_anomalous_user_name_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf15_v2_linux_anomalous_process_all_hosts_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf15_v2_linux_anomalous_network_port_activity_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + ], + searches: [] as string[], + visualizations: [] as string[], + dashboards: [] as string[], + }, + }, + { + testTitleSuffix: + 'for security_windows with prefix, startDatafeed true and estimateModelMemory true', + sourceDataArchive: 'ml/module_security_endpoint', + indexPattern: { name: 'ft_logs-endpoint.events.*', timeField: '@timestamp' }, + module: 'security_windows', + user: USER.ML_POWERUSER, + requestBody: { + prefix: 'pf16_', + indexPatternName: 'ft_logs-endpoint.events.*', + startDatafeed: true, + end: Date.now(), + }, + expected: { + responseCode: 200, + jobs: [ + { + jobId: 'pf16_v2_rare_process_by_host_windows_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_anomalous_network_activity_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_anomalous_path_activity_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '10mb', + }, + { + jobId: 'pf16_v2_windows_anomalous_process_all_hosts_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_anomalous_process_creation', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_anomalous_user_name_ecs', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_rare_metadata_process', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf16_v2_windows_rare_metadata_user', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + ], + searches: [] as string[], + visualizations: [] as string[], + dashboards: [] as string[], + }, + }, ]; const testDataListNegative = [ diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index 2b602760be342..19eaefec946c2 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -32,10 +32,7 @@ export default function ({ getService }: FtrProviderContext) { expect(body.networkDetails!.source!.geo!.continent_name).to.be('North America'); expect(body.networkDetails!.source!.geo!.location!.lat!).to.be(37.751); - expect(body.networkDetails!.host.os!.platform!).to.be('raspbian'); - expect(body.networkDetails!.destination!.geo!.continent_name).to.be('North America'); - expect(body.networkDetails!.destination!.geo!.location!.lat!).to.be(37.751); - expect(body.networkDetails!.host.os!.platform!).to.be('raspbian'); + expect(body.networkDetails!.host.os!.platform!).to.eql(['raspbian']); }); }); @@ -56,9 +53,9 @@ export default function ({ getService }: FtrProviderContext) { }) .expect(200); - expect(body.networkDetails!.host.id!).to.be('2ce8b1e7d69e4a1d9c6bcddc473da9d9'); - expect(body.networkDetails!.host.name!).to.be('zeek-sensor-amsterdam'); - expect(body.networkDetails!.host.os!.platform!).to.be('ubuntu'); + expect(body.networkDetails!.host.id!).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); + expect(body.networkDetails!.host.name!).to.eql(['zeek-sensor-amsterdam']); + expect(body.networkDetails!.host.os!.platform!).to.eql(['ubuntu']); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts b/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts new file mode 100644 index 0000000000000..3119de47a8635 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { format } from 'url'; +import archives from '../../../common/archives_metadata'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const archiveName = 'apm_8.0.0'; + const { end } = archives[archiveName]; + const start = new Date(Date.parse(end) - 600000).toISOString(); + + describe('Alerting chart previews', () => { + describe('GET /api/apm/alerts/chart_preview/transaction_error_rate', () => { + const url = format({ + pathname: '/api/apm/alerts/chart_preview/transaction_error_rate', + query: { + start, + end, + transactionType: 'request', + serviceName: 'opbeans-java', + }, + }); + + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + it('returns the correct data', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect( + response.body.some((item: { x: number; y: number | null }) => item.x && item.y) + ).to.equal(true); + }); + }); + }); + + describe('GET /api/apm/alerts/chart_preview/transaction_error_count', () => { + const url = format({ + pathname: '/api/apm/alerts/chart_preview/transaction_error_count', + query: { + start, + end, + serviceName: 'opbeans-java', + }, + }); + + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + it('returns the correct data', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect( + response.body.some((item: { x: number; y: number | null }) => item.x && item.y) + ).to.equal(true); + }); + }); + }); + + describe('GET /api/apm/alerts/chart_preview/transaction_duration', () => { + const url = format({ + pathname: '/api/apm/alerts/chart_preview/transaction_duration', + query: { + start, + end, + serviceName: 'opbeans-java', + transactionType: 'request', + }, + }); + + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + it('returns the correct data', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect( + response.body.some((item: { x: number; y: number | null }) => item.x && item.y) + ).to.equal(true); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts index 29c081a6fe493..4e1fc3957a6ea 100644 --- a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts @@ -107,21 +107,14 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, + url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, - }, - expectForbidden: expect403, - expectResponse: expect200, - }, - { - req: { - url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, + url: `/api/apm/services/foo/transactions/charts/latency?start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, }, expectForbidden: expect403, expectResponse: expect200, 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 3e625688e2459..f50868ee76c1c 100644 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ b/x-pack/test/apm_api_integration/basic/tests/index.ts @@ -11,6 +11,10 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont loadTestFile(require.resolve('./feature_controls')); + describe('Alerts', function () { + loadTestFile(require.resolve('./alerts/chart_preview')); + }); + describe('Service Maps', function () { loadTestFile(require.resolve('./service_maps/service_maps')); }); @@ -23,10 +27,10 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont loadTestFile(require.resolve('./services/transaction_types')); }); - // TODO: we should not have a service overview. describe('Service overview', function () { loadTestFile(require.resolve('./service_overview/error_groups')); loadTestFile(require.resolve('./service_overview/dependencies')); + loadTestFile(require.resolve('./service_overview/instances')); }); describe('Settings', function () { diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts b/x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts new file mode 100644 index 0000000000000..084555387a690 --- /dev/null +++ b/x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 url from 'url'; +import { pick, sortBy } from 'lodash'; +import { isFiniteNumber } from '../../../../../plugins/apm/common/utils/is_finite_number'; +import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import archives 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 { start, end } = archives[archiveName]; + + interface Response { + status: number; + body: APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>; + } + + describe('Service overview instances', () => { + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response: Response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-java/service_overview_instances`, + query: { + start, + end, + numBuckets: 20, + transactionType: 'request', + uiFilters: '{}', + }, + }) + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + describe('fetching java data', () => { + let response: Response; + + beforeEach(async () => { + response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-java/service_overview_instances`, + query: { + start, + end, + numBuckets: 20, + transactionType: 'request', + uiFilters: '{}', + }, + }) + ); + }); + + it('returns a service node item', () => { + expect(response.body.length).to.be.greaterThan(0); + }); + + it('returns statistics for each service node', () => { + const item = response.body[0]; + + expect(isFiniteNumber(item.cpuUsage?.value)).to.be(true); + expect(isFiniteNumber(item.memoryUsage?.value)).to.be(true); + expect(isFiniteNumber(item.errorRate?.value)).to.be(true); + expect(isFiniteNumber(item.throughput?.value)).to.be(true); + expect(isFiniteNumber(item.latency?.value)).to.be(true); + + expect(item.cpuUsage?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.memoryUsage?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.errorRate?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.throughput?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.latency?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + }); + + it('returns the right data', () => { + const items = sortBy(response.body, 'serviceNodeName'); + + const serviceNodeNames = items.map((item) => item.serviceNodeName); + + expectSnapshot(items.length).toMatchInline(`1`); + + expectSnapshot(serviceNodeNames).toMatchInline(` + Array [ + "02950c4c5fbb0fda1cc98c47bf4024b473a8a17629db6530d95dcee68bd54c6c", + ] + `); + + const item = items[0]; + + const values = pick(item, [ + 'cpuUsage.value', + 'memoryUsage.value', + 'errorRate.value', + 'throughput.value', + 'latency.value', + ]); + + expectSnapshot(values).toMatchInline(` + Object { + "cpuUsage": Object { + "value": 0.0120166666666667, + }, + "errorRate": Object { + "value": 0.16, + }, + "latency": Object { + "value": 237339.813333333, + }, + "memoryUsage": Object { + "value": 0.941324615478516, + }, + "throughput": Object { + "value": 75, + }, + } + `); + }); + }); + + describe('fetching non-java data', () => { + let response: Response; + + beforeEach(async () => { + response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-ruby/service_overview_instances`, + query: { + start, + end, + numBuckets: 20, + transactionType: 'request', + uiFilters: '{}', + }, + }) + ); + }); + + it('returns statistics for each service node', () => { + const item = response.body[0]; + + expect(isFiniteNumber(item.cpuUsage?.value)).to.be(true); + expect(isFiniteNumber(item.memoryUsage?.value)).to.be(true); + expect(isFiniteNumber(item.errorRate?.value)).to.be(true); + expect(isFiniteNumber(item.throughput?.value)).to.be(true); + expect(isFiniteNumber(item.latency?.value)).to.be(true); + + expect(item.cpuUsage?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.memoryUsage?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.errorRate?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.throughput?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + expect(item.latency?.timeseries.some((point) => isFiniteNumber(point.y))).to.be(true); + }); + + it('returns the right data', () => { + const items = sortBy(response.body, 'serviceNodeName'); + + const serviceNodeNames = items.map((item) => item.serviceNodeName); + + expectSnapshot(items.length).toMatchInline(`1`); + + expectSnapshot(serviceNodeNames).toMatchInline(` + Array [ + "_service_node_name_missing_", + ] + `); + + const item = items[0]; + + const values = pick( + item, + 'cpuUsage.value', + 'errorRate.value', + 'throughput.value', + 'latency.value' + ); + + expectSnapshot(values).toMatchInline(` + Object { + "cpuUsage": Object { + "value": 0.00111666666666667, + }, + "errorRate": Object { + "value": 0.0373134328358209, + }, + "latency": Object { + "value": 70518.9328358209, + }, + "throughput": Object { + "value": 134, + }, + } + `); + + expectSnapshot(values); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts index d856483752ac3..c657d37072412 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts @@ -22,17 +22,31 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('Latency', () => { describe('when data is not loaded ', () => { + it('returns 400 when latencyAggregationType is not informed', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request` + ); + + expect(response.status).to.be(400); + }); + + it('returns 400 when transactionType is not informed', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + + expect(response.status).to.be(400); + }); + it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request` ); expect(response.status).to.be(200); expect(response.body.overallAvgDuration).to.be(null); - expect(response.body.latencyTimeseries.avg.length).to.be(0); - expect(response.body.latencyTimeseries.p95.length).to.be(0); - expect(response.body.latencyTimeseries.p99.length).to.be(0); + expect(response.body.latencyTimeseries.length).to.be(0); }); }); @@ -42,19 +56,46 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: PromiseReturnType; - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + describe('average latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg` + ); + }); + + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); }); - it('returns average duration and timeseries', async () => { - expect(response.status).to.be(200); + describe('95th percentile latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95` + ); + }); + + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); + }); + + describe('99th percentile latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99` + ); + }); - expect(response.body.overallAvgDuration).not.to.be(null); - expect(response.body.latencyTimeseries.avg.length).to.be.greaterThan(0); - expect(response.body.latencyTimeseries.p95.length).to.be.greaterThan(0); - expect(response.body.latencyTimeseries.p99.length).to.be.greaterThan(0); + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); }); }); }); diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts b/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts index 28a572ab53043..0a0ba5af5b790 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts @@ -32,6 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); @@ -62,6 +63,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); @@ -138,6 +140,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); @@ -162,6 +165,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); @@ -186,6 +190,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'latency', + latencyAggregationType: 'avg', }, }) ); @@ -212,6 +217,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex: 0, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); @@ -239,6 +245,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { pageIndex, sortDirection: 'desc', sortField: 'impact', + latencyAggregationType: 'avg', }, }) ); diff --git a/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts index f248904facae3..a841dc7635268 100644 --- a/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts +++ b/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts @@ -34,7 +34,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const uiFilters = encodeURIComponent(JSON.stringify({})); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` ); }); it('should return an error response', () => { @@ -45,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('without uiFilters', () => { before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` ); }); it('should return an error response', () => { @@ -57,7 +57,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' })); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` ); }); @@ -86,7 +86,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-python/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-python/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` ); }); @@ -112,7 +112,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' })); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` ); }); @@ -131,7 +131,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); before(async () => { response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` ); }); diff --git a/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts b/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts index 36f07ef92b5f1..df200b34dc429 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/migrations.ts @@ -38,5 +38,18 @@ export default function createGetTests({ getService }: FtrProviderContext) { fields: null, }); }); + + it('7.11.0 migrates cases settings', async () => { + const { body } = await supertest + .get(`${CASES_URL}/e1900ac0-017f-11eb-93f8-d161651bf509`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body).key('settings'); + expect(body.settings).to.eql({ + syncAlerts: true, + }); + }); }); } diff --git a/x-pack/test/case_api_integration/basic/tests/cases/user_actions/get_all_user_actions.ts b/x-pack/test/case_api_integration/basic/tests/cases/user_actions/get_all_user_actions.ts index 6949052df4703..ec79c8a1ca494 100644 --- a/x-pack/test/case_api_integration/basic/tests/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/test/case_api_integration/basic/tests/cases/user_actions/get_all_user_actions.ts @@ -36,7 +36,7 @@ export default ({ getService }: FtrProviderContext): void => { await actionsRemover.removeAll(); }); - it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector']`, async () => { + it(`on new case, user action: 'create' should be called with actionFields: ['description', 'status', 'tags', 'title', 'connector', 'settings]`, async () => { const { body: postedCase } = await supertest .post(CASES_URL) .set('kbn-xsrf', 'true') @@ -51,7 +51,14 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.length).to.eql(1); - expect(body[0].action_field).to.eql(['description', 'status', 'tags', 'title', 'connector']); + expect(body[0].action_field).to.eql([ + 'description', + 'status', + 'tags', + 'title', + 'connector', + 'settings', + ]); expect(body[0].action).to.eql('create'); expect(body[0].old_value).to.eql(null); expect(body[0].new_value).to.eql(JSON.stringify(postCaseReq)); diff --git a/x-pack/test/case_api_integration/basic/tests/connectors/case.ts b/x-pack/test/case_api_integration/basic/tests/connectors/case.ts index 9a45dd541bb56..e0812d01d0fb8 100644 --- a/x-pack/test/case_api_integration/basic/tests/connectors/case.ts +++ b/x-pack/test/case_api_integration/basic/tests/connectors/case.ts @@ -391,6 +391,9 @@ export default ({ getService }: FtrProviderContext): void => { parent: null, }, }, + settings: { + syncAlerts: true, + }, }, }; @@ -442,6 +445,9 @@ export default ({ getService }: FtrProviderContext): void => { type: '.servicenow', fields: {}, }, + settings: { + syncAlerts: true, + }, }, }; @@ -673,7 +679,53 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should respond with a 400 Bad Request when missing attributes of type alert', async () => { + // TODO: Remove it when the creation of comments of type alert is supported + // https://github.com/elastic/kibana/issues/85750 + it('should fail adding a comment of type alert', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A case connector', + actionTypeId: '.case', + config: {}, + }) + .expect(200); + + createdActionId = createdAction.id; + + const caseRes = await supertest + .post(CASES_URL) + .set('kbn-xsrf', 'true') + .send(postCaseReq) + .expect(200); + + const params = { + subAction: 'addComment', + subActionParams: { + caseId: caseRes.body.id, + comment: { alertId: 'test-id', index: 'test-index', type: CommentType.alert }, + }, + }; + + const caseConnector = await supertest + .post(`/api/actions/action/${createdActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ params }) + .expect(200); + + expect(caseConnector.body).to.eql({ + status: 'error', + actionId: createdActionId, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.type]: expected value to equal [user]', + retry: false, + }); + }); + + // TODO: Enable when the creation of comments of type alert is supported + // https://github.com/elastic/kibana/issues/85750 + it.skip('should respond with a 400 Bad Request when missing attributes of type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') @@ -754,13 +806,15 @@ export default ({ getService }: FtrProviderContext): void => { expect(caseConnector.body).to.eql({ status: 'error', actionId: createdActionId, - message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.${attribute}]: definition for this key is missing\n - [subActionParams.comment.1.type]: expected value to equal [alert]`, + message: `error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [create]\n- [1.subAction]: expected value to equal [update]\n- [2.subActionParams.comment]: types that failed validation:\n - [subActionParams.comment.0.${attribute}]: definition for this key is missing`, retry: false, }); } }); - it('should respond with a 400 Bad Request when adding excess attributes for type alert', async () => { + // TODO: Enable when the creation of comments of type alert is supported + // https://github.com/elastic/kibana/issues/85750 + it.skip('should respond with a 400 Bad Request when adding excess attributes for type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') @@ -892,7 +946,9 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - it('should add a comment of type alert', async () => { + // TODO: Enable when the creation of comments of type alert is supported + // https://github.com/elastic/kibana/issues/85750 + it.skip('should add a comment of type alert', async () => { const { body: createdAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'foo') diff --git a/x-pack/test/case_api_integration/common/lib/mock.ts b/x-pack/test/case_api_integration/common/lib/mock.ts index dac6b2005a9c3..012af6b37f842 100644 --- a/x-pack/test/case_api_integration/common/lib/mock.ts +++ b/x-pack/test/case_api_integration/common/lib/mock.ts @@ -26,6 +26,9 @@ export const postCaseReq: CasePostRequest = { type: '.none' as ConnectorTypes, fields: null, }, + settings: { + syncAlerts: true, + }, }; export const postCommentUserReq: CommentRequestUserType = { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_signals_migrations.ts new file mode 100644 index 0000000000000..3271a75fcd1d1 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_signals_migrations.ts @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + DEFAULT_SIGNALS_INDEX, + DETECTION_ENGINE_SIGNALS_MIGRATION_URL, +} from '../../../../plugins/security_solution/common/constants'; +import { ROLES } from '../../../../plugins/security_solution/common/test'; +import { SIGNALS_TEMPLATE_VERSION } from '../../../../plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createSignalsIndex, + deleteMigrations, + deleteSignalsIndex, + getIndexNameFromLoad, + waitForIndexToPopulate, +} from '../../utils'; +import { createUserAndRole } from '../roles_users_utils'; + +interface CreateResponse { + index: string; + migration_index: string; + migration_id: string; +} + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const es = getService('es'); + const esArchiver = getService('esArchiver'); + const kbnClient = getService('kibanaServer'); + const security = getService('security'); + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('Creating signals migrations', () => { + let createdMigrations: CreateResponse[]; + let legacySignalsIndexName: string; + let outdatedSignalsIndexName: string; + + beforeEach(async () => { + createdMigrations = []; + await createSignalsIndex(supertest); + + legacySignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/legacy_signals_index') + ); + outdatedSignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/outdated_signals_index') + ); + }); + + afterEach(async () => { + await esArchiver.unload('signals/outdated_signals_index'); + await esArchiver.unload('signals/legacy_signals_index'); + await deleteMigrations({ + kbnClient, + ids: createdMigrations.filter((m) => m?.migration_id).map((m) => m.migration_id), + }); + await deleteSignalsIndex(supertest); + }); + + it('returns the information necessary to finalize the migration', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName] }) + .expect(200); + createdMigrations = [...createdMigrations, ...body.indices]; + + expect(body.indices).length(1); + const [createdMigration] = body.indices; + + expect(createdMigration.index).to.eql(legacySignalsIndexName); + expect(createdMigration.migration_id).to.be.a('string'); + expect(createdMigration.migration_id.length).to.be.greaterThan(0); + expect(createdMigration.migration_index).not.to.eql(legacySignalsIndexName); + expect(createdMigration.migration_index).to.contain(legacySignalsIndexName); + }); + + it('creates a new index containing migrated signals', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName, outdatedSignalsIndexName] }) + .expect(200); + createdMigrations = [...createdMigrations, ...body.indices]; + const createResponses: CreateResponse[] = body.indices; + + expect(createResponses).length(2); + createResponses.forEach((response) => expect(response.migration_id).to.be.a('string')); + + const [{ migration_index: newIndex }] = createResponses; + await waitForIndexToPopulate(es, newIndex); + const { body: migrationResults } = await es.search({ index: newIndex }); + + expect(migrationResults.hits.hits).length(1); + const migratedSignal = migrationResults.hits.hits[0]._source.signal; + expect(migratedSignal._meta.version).to.equal(SIGNALS_TEMPLATE_VERSION); + }); + + it('specifying the signals alias itself is a bad request', async () => { + const signalsAlias = `${DEFAULT_SIGNALS_INDEX}-default`; + + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [signalsAlias, legacySignalsIndexName] }) + .expect(400); + + expect(body).to.eql({ + message: + 'The following indices are not signals indices and cannot be migrated: [.siem-signals-default].', + status_code: 400, + }); + }); + + it('rejects extant non-signals indexes', async () => { + const unrelatedIndex = '.tasks'; + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName, unrelatedIndex] }) + .expect(400); + + expect(body).to.eql({ + message: 'The following indices are not signals indices and cannot be migrated: [.tasks].', + status_code: 400, + }); + }); + + it('rejects if an unknown index is specified', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: ['random-index', outdatedSignalsIndexName] }) + .expect(400); + + expect(body).to.eql({ + message: + 'The following indices are not signals indices and cannot be migrated: [random-index].', + status_code: 400, + }); + }); + + it('returns an inline error on a duplicated request as the destination index already exists', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName] }) + .expect(200); + createdMigrations = [...createdMigrations, ...body.indices]; + + const { body: duplicatedBody } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName] }) + .expect(200); + + const [{ error, ...info }] = duplicatedBody.indices; + expect(info).to.eql({ + index: legacySignalsIndexName, + migration_index: null, + migration_id: null, + }); + expect(error.status_code).to.eql(400); + expect(error.message).to.contain('resource_already_exists_exception'); + }); + + it('rejects the request if the user does not have sufficient privileges', async () => { + await createUserAndRole(security, ROLES.t1_analyst); + + await supertestWithoutAuth + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .auth(ROLES.t1_analyst, 'changeme') + .send({ index: [legacySignalsIndexName] }) + .expect(400); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts new file mode 100644 index 0000000000000..040c83c458eb3 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_signals_migrations.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { + DEFAULT_SIGNALS_INDEX, + DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL, + DETECTION_ENGINE_SIGNALS_MIGRATION_URL, +} from '../../../../plugins/security_solution/common/constants'; +import { ROLES } from '../../../../plugins/security_solution/common/test'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { createSignalsIndex, deleteSignalsIndex, getIndexNameFromLoad, waitFor } from '../../utils'; +import { createUserAndRole } from '../roles_users_utils'; + +interface CreateResponse { + index: string; + migration_index: string; + migration_id: string; +} + +interface FinalizeResponse extends CreateResponse { + completed?: boolean; + error?: unknown; +} + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const es = getService('es'); + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('deleting signals migrations', () => { + let outdatedSignalsIndexName: string; + let createdMigration: CreateResponse; + let finalizedMigration: FinalizeResponse; + + beforeEach(async () => { + await createSignalsIndex(supertest); + outdatedSignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/outdated_signals_index') + ); + + ({ + body: { + indices: [createdMigration], + }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [outdatedSignalsIndexName] }) + .expect(200)); + + await waitFor(async () => { + ({ + body: { + migrations: [finalizedMigration], + }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200)); + + return finalizedMigration.completed ?? false; + }, `polling finalize_migration until all complete`); + }); + + afterEach(async () => { + await esArchiver.unload('signals/outdated_signals_index'); + await deleteSignalsIndex(supertest); + }); + + it('returns the deleted migration SavedObjects', async () => { + const { body } = await supertest + .delete(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + + const deletedMigration = body.migrations[0]; + expect(deletedMigration.id).to.eql(createdMigration.migration_id); + expect(deletedMigration.sourceIndex).to.eql(outdatedSignalsIndexName); + }); + + it('marks the original index for deletion by applying our cleanup policy', async () => { + await supertest + .delete(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + + const { body } = await es.indices.getSettings({ index: createdMigration.index }); + const indexSettings = body[createdMigration.index].settings.index; + expect(indexSettings.lifecycle.name).to.eql( + `${DEFAULT_SIGNALS_INDEX}-default-migration-cleanup` + ); + }); + + it('rejects the request if the user does not have sufficient privileges', async () => { + await createUserAndRole(security, ROLES.t1_analyst); + + const { body } = await supertestWithoutAuth + .delete(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .auth(ROLES.t1_analyst, 'changeme') + .expect(200); + + const deletedMigration = body.migrations[0]; + + expect(deletedMigration.id).to.eql(createdMigration.migration_id); + expect(deletedMigration.error).to.eql({ + message: + 'security_exception: action [indices:admin/settings/update] is unauthorized for user [t1_analyst] on indices [], this action is granted by the privileges [manage,all]', + status_code: 403, + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts new file mode 100644 index 0000000000000..a754966cf18a9 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/finalize_signals_migrations.ts @@ -0,0 +1,255 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL, + DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL, + DETECTION_ENGINE_SIGNALS_MIGRATION_URL, +} from '../../../../plugins/security_solution/common/constants'; +import { ROLES } from '../../../../plugins/security_solution/common/test'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createSignalsIndex, + deleteMigrations, + deleteSignalsIndex, + getIndexNameFromLoad, + waitFor, +} from '../../utils'; +import { createUserAndRole } from '../roles_users_utils'; + +interface StatusResponse { + index: string; + is_outdated: boolean; +} + +interface CreateResponse { + index: string; + migration_index: string; + migration_id: string; +} + +interface FinalizeResponse { + id: string; + completed?: boolean; + error?: unknown; +} + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const esArchiver = getService('esArchiver'); + const kbnClient = getService('kibanaServer'); + const security = getService('security'); + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('Finalizing signals migrations', () => { + let legacySignalsIndexName: string; + let outdatedSignalsIndexName: string; + let createdMigrations: CreateResponse[]; + let createdMigration: CreateResponse; + + beforeEach(async () => { + createdMigrations = []; + await createSignalsIndex(supertest); + legacySignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/legacy_signals_index') + ); + outdatedSignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/outdated_signals_index') + ); + + ({ + body: { indices: createdMigrations }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [legacySignalsIndexName] }) + .expect(200)); + + [createdMigration] = createdMigrations; + }); + + afterEach(async () => { + await esArchiver.unload('signals/outdated_signals_index'); + await esArchiver.unload('signals/legacy_signals_index'); + await deleteMigrations({ + kbnClient, + ids: createdMigrations.filter((m) => m?.migration_id).map((m) => m.migration_id), + }); + await deleteSignalsIndex(supertest); + }); + + it('replaces the original index alias with the migrated one', async () => { + const { body } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200); + const statusResponses: StatusResponse[] = body.indices; + const indicesBefore = statusResponses.map((index) => index.index); + + expect(indicesBefore).to.contain(createdMigration.index); + expect(indicesBefore).not.to.contain(createdMigration.migration_index); + + await waitFor(async () => { + const { + body: { + migrations: [{ completed }], + }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + + return completed === true; + }, `polling finalize_migration until complete`); + + let statusAfter: StatusResponse[] = []; + await waitFor(async () => { + ({ + body: { indices: statusAfter }, + } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200)); + return statusAfter.some((s) => !s.is_outdated); + }, `polling finalize_migration until complete`); + + const indicesAfter = statusAfter.map((s) => s.index); + + expect(indicesAfter).to.contain(createdMigration.migration_index); + expect(indicesAfter).not.to.contain(createdMigration.index); + }); + + it('finalizes an arbitrary number of indices', async () => { + // start our second migration + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ index: [outdatedSignalsIndexName] }) + .expect(200); + createdMigrations = [...createdMigrations, ...body.indices]; + + let finalizeResponse: FinalizeResponse[]; + await waitFor(async () => { + ({ + body: { migrations: finalizeResponse }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: createdMigrations.map((m) => m.migration_id) }) + .expect(200)); + + return finalizeResponse.every((index) => index.completed); + }, `polling finalize_migration until all complete`); + + const { body: bodyAfter } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200); + + const statusAfter: StatusResponse[] = bodyAfter.indices; + expect(statusAfter.map((s) => s.index)).to.eql( + createdMigrations.map((c) => c.migration_index) + ); + expect(statusAfter.map((s) => s.is_outdated)).to.eql([false, false]); + }); + + it.skip('deletes the underlying migration task', async () => { + await waitFor(async () => { + const { + body: { + migrations: [{ completed }], + }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + + return completed; + }, `polling finalize_migration until complete`); + + // const [{ taskId }] = await getMigration({ id: migration.migration_id }); + // expect(taskId.length).greaterThan(0); + // const { statusCode } = await es.tasks.get({ task_id: taskId }, { ignore: [404] }); + // expect(statusCode).to.eql(404); + }); + + it('subsequent attempts at finalization are idempotent', async () => { + await waitFor(async () => { + const { + body: { + migrations: [{ completed }], + }, + } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + + return completed; + }, `polling finalize_migration until complete`); + + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .expect(200); + const finalizeResponse: FinalizeResponse = body.migrations[0]; + expect(finalizeResponse.completed).to.eql(true); + expect(finalizeResponse.id).to.eql(createdMigration.migration_id); + + const { body: bodyAfter } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200); + + const statusAfter: StatusResponse[] = bodyAfter.indices; + const indicesAfter = statusAfter.map((index) => index.index); + + expect(indicesAfter).to.contain(createdMigration.migration_index); + expect(indicesAfter).not.to.contain(createdMigration.index); + }); + + it('returns an empty array indicating a no-op for DNE migrations', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: ['dne-migration'] }) + .expect(200); + + expect(body).to.eql({ migrations: [] }); + }); + + it('rejects the request if the user does not have sufficient privileges', async () => { + await createUserAndRole(security, ROLES.t1_analyst); + + const { body } = await supertestWithoutAuth + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: [createdMigration.migration_id] }) + .auth(ROLES.t1_analyst, 'changeme') + .expect(200); + + const finalizeResponse: FinalizeResponse = body.migrations[0]; + + expect(finalizeResponse.id).to.eql(createdMigration.migration_id); + expect(finalizeResponse.completed).not.to.eql(true); + expect(finalizeResponse.error).to.eql({ + message: + 'security_exception: action [cluster:monitor/task/get] is unauthorized for user [t1_analyst]', + status_code: 403, + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_signals_migration_status.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_signals_migration_status.ts new file mode 100644 index 0000000000000..0a1b5b90c2794 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/get_signals_migration_status.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL } from '../../../../plugins/security_solution/common/constants'; +import { ROLES } from '../../../../plugins/security_solution/common/test'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { createSignalsIndex, deleteSignalsIndex, getIndexNameFromLoad } from '../../utils'; +import { createUserAndRole } from '../roles_users_utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext): void => { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('Signals migration status', () => { + let legacySignalsIndexName: string; + beforeEach(async () => { + await createSignalsIndex(supertest); + legacySignalsIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/legacy_signals_index') + ); + }); + + afterEach(async () => { + await esArchiver.unload('signals/legacy_signals_index'); + await deleteSignalsIndex(supertest); + }); + + it('returns no indexes if no signals exist in the specified range', async () => { + const { body } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-20' }) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(body.indices).to.eql([]); + }); + + it('includes an index if its signals are within the specified range', async () => { + const { + body: { indices }, + } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(indices).length(1); + expect(indices[0].index).to.eql(legacySignalsIndexName); + }); + + it("returns the mappings version and a breakdown of signals' version", async () => { + const outdatedIndexName = getIndexNameFromLoad( + await esArchiver.load('signals/outdated_signals_index') + ); + + const { body } = await supertest + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .query({ from: '2020-10-10' }) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(body.indices).to.eql([ + { + index: legacySignalsIndexName, + is_outdated: true, + migrations: [], + signal_versions: [ + { + count: 1, + version: 0, + }, + ], + version: 1, + }, + { + is_outdated: true, + index: outdatedIndexName, + migrations: [], + signal_versions: [ + { + count: 1, + version: 3, + }, + ], + version: 3, + }, + ]); + + await esArchiver.unload('signals/outdated_signals_index'); + }); + + it('rejects the request if the user does not have sufficient privileges', async () => { + await createUserAndRole(security, ROLES.t1_analyst); + + await supertestWithoutAuth + .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) + .set('kbn-xsrf', 'true') + .auth(ROLES.t1_analyst, 'changeme') + .query({ from: '2020-10-10' }) + .expect(403); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index d49d6ed3eedb0..6eb74af910605 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -33,6 +33,9 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./patch_rules')); loadTestFile(require.resolve('./query_signals')); loadTestFile(require.resolve('./open_close_signals')); - loadTestFile(require.resolve('./migrating_signals')); + loadTestFile(require.resolve('./get_signals_migration_status')); + loadTestFile(require.resolve('./create_signals_migrations')); + loadTestFile(require.resolve('./finalize_signals_migrations')); + loadTestFile(require.resolve('./delete_signals_migrations')); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrating_signals.ts deleted file mode 100644 index a256b026e5174..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrating_signals.ts +++ /dev/null @@ -1,521 +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 expect from '@kbn/expect'; - -import { - DEFAULT_SIGNALS_INDEX, - DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL, - DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL, - DETECTION_ENGINE_SIGNALS_MIGRATION_URL, -} from '../../../../plugins/security_solution/common/constants'; -import { ROLES } from '../../../../plugins/security_solution/common/test'; -import { encodeMigrationToken } from '../../../../plugins/security_solution/server/lib/detection_engine/migrations/helpers'; -import { SIGNALS_TEMPLATE_VERSION } from '../../../../plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - createSignalsIndex, - deleteSignalsIndex, - getIndexNameFromLoad, - waitFor, - waitForIndexToPopulate, -} from '../../utils'; -import { createUserAndRole } from '../roles_users_utils'; - -// eslint-disable-next-line import/no-default-export -export default ({ getService }: FtrProviderContext): void => { - const es = getService('es'); - const esArchiver = getService('esArchiver'); - const security = getService('security'); - const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - - describe('Migrating signals', () => { - beforeEach(async () => { - await createSignalsIndex(supertest); - }); - - afterEach(async () => { - await deleteSignalsIndex(supertest); - }); - - describe('migration status of signals indexes', async () => { - let legacySignalsIndexName: string; - - beforeEach(async () => { - legacySignalsIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/legacy_signals_index') - ); - }); - - afterEach(async () => { - await esArchiver.unload('signals/legacy_signals_index'); - }); - - it('returns no indexes if no signals exist in the specified range', async () => { - const { body } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-20' }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.indices).to.eql([]); - }); - - it('includes an index if its signals are within the specified range', async () => { - const { - body: { indices }, - } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-10' }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(indices).length(1); - expect(indices[0].name).to.eql(legacySignalsIndexName); - }); - - it("returns the mappings version and a breakdown of signals' version", async () => { - const outdatedIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/outdated_signals_index') - ); - - const { body } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-10' }) - .set('kbn-xsrf', 'true') - .expect(200); - - expect(body.indices).to.eql([ - { - name: legacySignalsIndexName, - is_outdated: true, - signal_versions: [ - { - doc_count: 1, - key: 0, - }, - ], - version: 1, - }, - { - is_outdated: true, - name: outdatedIndexName, - signal_versions: [ - { - doc_count: 1, - key: 3, - }, - ], - version: 3, - }, - ]); - - await esArchiver.unload('signals/outdated_signals_index'); - }); - - it('rejects the request if the user does not have sufficient privileges', async () => { - await createUserAndRole(security, ROLES.t1_analyst); - - await supertestWithoutAuth - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .set('kbn-xsrf', 'true') - .auth(ROLES.t1_analyst, 'changeme') - .query({ from: '2020-10-10' }) - .expect(403); - }); - }); - - describe('Creating a signals migration', async () => { - let legacySignalsIndexName: string; - let outdatedSignalsIndexName: string; - - beforeEach(async () => { - legacySignalsIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/legacy_signals_index') - ); - outdatedSignalsIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/outdated_signals_index') - ); - }); - - afterEach(async () => { - await esArchiver.unload('signals/outdated_signals_index'); - await esArchiver.unload('signals/legacy_signals_index'); - }); - - it('returns the information necessary to finalize the migration', async () => { - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName] }) - .expect(200); - - expect(body.indices).length(1); - const [index] = body.indices; - - expect(index.index).to.eql(legacySignalsIndexName); - expect(index.migration_token).to.be.a('string'); - expect(index.migration_token.length).to.be.greaterThan(0); - expect(index.migration_index).not.to.eql(legacySignalsIndexName); - expect(index.migration_index).to.contain(legacySignalsIndexName); - expect(index.migration_task_id).to.be.a('string'); - expect(index.migration_task_id.length).to.be.greaterThan(0); - }); - - it('creates a new index containing migrated signals', async () => { - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName, outdatedSignalsIndexName] }) - .expect(200); - - const indices = body.indices as Array<{ migration_token: string; migration_index: string }>; - expect(indices).length(2); - indices.forEach((index) => expect(index.migration_token).to.be.a('string')); - - const [{ migration_index: newIndex }] = indices; - await waitForIndexToPopulate(es, newIndex); - const { body: migrationResults } = await es.search({ index: newIndex }); - - expect(migrationResults.hits.hits).length(1); - const migratedSignal = migrationResults.hits.hits[0]._source.signal; - expect(migratedSignal._meta.version).to.equal(SIGNALS_TEMPLATE_VERSION); - }); - - it('specifying the signals alias itself is a bad request', async () => { - const signalsAlias = `${DEFAULT_SIGNALS_INDEX}-default`; - - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [signalsAlias, legacySignalsIndexName] }) - .expect(400); - - expect(body).to.eql({ - message: - 'The following indices are not signals indices and cannot be migrated: [.siem-signals-default].', - status_code: 400, - }); - }); - - it('rejects extant non-signals indexes', async () => { - const unrelatedIndex = '.tasks'; - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName, unrelatedIndex] }) - .expect(400); - - expect(body).to.eql({ - message: - 'The following indices are not signals indices and cannot be migrated: [.tasks].', - status_code: 400, - }); - }); - - it('rejects if an unknown index is specified', async () => { - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: ['random-index', outdatedSignalsIndexName] }) - .expect(400); - - expect(body).to.eql({ - message: - 'The following indices are not signals indices and cannot be migrated: [random-index].', - status_code: 400, - }); - }); - - it('returns an inline error on a duplicated request as the destination index already exists', async () => { - await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName] }) - .expect(200); - - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName] }) - .expect(200); - - const [{ error, ...info }] = body.indices; - expect(info).to.eql({ - index: legacySignalsIndexName, - migration_index: null, - migration_task_id: null, - migration_token: null, - }); - expect(error.status_code).to.eql(400); - expect(error.message).to.contain('resource_already_exists_exception'); - }); - - it('rejects the request if the user does not have sufficient privileges', async () => { - await createUserAndRole(security, ROLES.t1_analyst); - - await supertestWithoutAuth - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .auth(ROLES.t1_analyst, 'changeme') - .send({ index: [legacySignalsIndexName] }) - .expect(403); - }); - }); - - describe('finalizing signals migrations', async () => { - let legacySignalsIndexName: string; - let outdatedSignalsIndexName: string; - let migratingIndices: any[]; - - beforeEach(async () => { - legacySignalsIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/legacy_signals_index') - ); - outdatedSignalsIndexName = getIndexNameFromLoad( - await esArchiver.load('signals/outdated_signals_index') - ); - - ({ - body: { indices: migratingIndices }, - } = await supertest - .post(DETECTION_ENGINE_SIGNALS_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ index: [legacySignalsIndexName, outdatedSignalsIndexName] }) - .expect(200)); - }); - - afterEach(async () => { - await esArchiver.unload('signals/outdated_signals_index'); - await esArchiver.unload('signals/legacy_signals_index'); - }); - - it('replaces the original index alias with the migrated one', async () => { - const [migratingIndex] = migratingIndices; - - const { body } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-10' }) - .set('kbn-xsrf', 'true') - .expect(200); - const indicesBefore = (body.indices as Array<{ name: string }>).map((index) => index.name); - - expect(indicesBefore).to.contain(migratingIndex.index); - expect(indicesBefore).not.to.contain(migratingIndex.migration_index); - - await waitFor(async () => { - const { - body: { completed }, - } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .expect(200); - - return completed; - }, `polling finalize_migration until complete`); - - const { body: bodyAfter } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-10' }) - .set('kbn-xsrf', 'true') - .expect(200); - - const indicesAfter = (bodyAfter.indices as Array<{ name: string }>).map( - (index) => index.name - ); - - expect(indicesAfter).to.contain(migratingIndex.migration_index); - expect(indicesAfter).not.to.contain(migratingIndex.index); - }); - - it('marks the original index for deletion by applying our cleanup policy', async () => { - const [migratingIndex] = migratingIndices; - - await waitFor(async () => { - const { - body: { completed }, - } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .expect(200); - - return completed; - }, `polling finalize_migration until complete`); - - const { body } = await es.indices.getSettings({ index: migratingIndex.index }); - const indexSettings = body[migratingIndex.index].settings.index; - expect(indexSettings.lifecycle.name).to.eql( - `${DEFAULT_SIGNALS_INDEX}-default-migration-cleanup` - ); - }); - - it('deletes the original index for deletion by applying our cleanup policy', async () => { - const [migratingIndex] = migratingIndices; - - await waitFor(async () => { - const { - body: { completed }, - } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .expect(200); - - return completed; - }, `polling finalize_migration until complete`); - - const { statusCode } = await es.tasks.get( - { task_id: migratingIndex.migration_task_id }, - { ignore: [404] } - ); - expect(statusCode).to.eql(404); - }); - - it('subsequent attempts at finalization are 404s', async () => { - const [migratingIndex] = migratingIndices; - - await waitFor(async () => { - const { - body: { completed }, - } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .expect(200); - - return completed; - }, `polling finalize_migration until complete`); - - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .expect(404); - - expect(body.status_code).to.eql(404); - expect(body.message).to.contain('resource_not_found_exception'); - - const { body: bodyAfter } = await supertest - .get(DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL) - .query({ from: '2020-10-10' }) - .set('kbn-xsrf', 'true') - .expect(200); - - const indicesAfter = (bodyAfter.indices as Array<{ name: string }>).map( - (index) => index.name - ); - - expect(indicesAfter).to.contain(migratingIndex.migration_index); - expect(indicesAfter).not.to.contain(migratingIndex.index); - }); - - it('rejects if the provided token is invalid', async () => { - const requestBody = { migration_token: 'invalid_token' }; - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send(requestBody) - .expect(400); - - expect(body).to.eql({ - message: 'An error occurred while decoding the migration token: [invalid_token]', - status_code: 400, - }); - }); - - it('rejects if the specified indexes do not match the task', async () => { - const [ - { migration_index: destinationIndex, index: sourceIndex, migration_task_id: taskId }, - ] = migratingIndices; - const migrationDetails = { destinationIndex, sourceIndex, taskId }; - const invalidToken = encodeMigrationToken({ - ...migrationDetails, - sourceIndex: 'bad-index', - }); - const requestBody = { migration_token: invalidToken }; - - let finalizeResponse: any; - - await waitFor(async () => { - const { body, status } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send(requestBody); - finalizeResponse = body; - - return status !== 200; - }, `polling finalize_migration until task is complete (with error)`); - - expect(finalizeResponse).to.eql({ - message: `The specified task does not match the source and destination indexes. Task [${taskId}] did not specify source index [bad-index] and destination index [${destinationIndex}]`, - status_code: 400, - }); - }); - - it('rejects if the task is malformed', async () => { - const [ - { migration_index: destinationIndex, index: sourceIndex, migration_task_id: taskId }, - ] = migratingIndices; - const migrationDetails = { destinationIndex, sourceIndex, taskId }; - const invalidToken = encodeMigrationToken({ - ...migrationDetails, - taskId: 'bad-task-id', - }); - const requestBody = { migration_token: invalidToken }; - - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send(requestBody) - .expect(400); - - expect(body).to.eql({ - message: 'illegal_argument_exception: malformed task id bad-task-id', - status_code: 400, - }); - }); - - it('rejects if the task does not exist', async () => { - const [ - { migration_index: destinationIndex, index: sourceIndex, migration_task_id: taskId }, - ] = migratingIndices; - const migrationDetails = { destinationIndex, sourceIndex, taskId }; - const invalidToken = encodeMigrationToken({ - ...migrationDetails, - taskId: 'oTUltX4IQMOUUVeiohTt8A:124', - }); - const requestBody = { migration_token: invalidToken }; - - const { body } = await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send(requestBody) - .expect(404); - - expect(body).to.eql({ - message: - "resource_not_found_exception: task [oTUltX4IQMOUUVeiohTt8A:124] belongs to the node [oTUltX4IQMOUUVeiohTt8A] which isn't part of the cluster and there is no record of the task", - status_code: 404, - }); - }); - - it('rejects the request if the user does not have sufficient privileges', async () => { - const [migratingIndex] = migratingIndices; - await createUserAndRole(security, ROLES.t1_analyst); - - await supertestWithoutAuth - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_token: migratingIndex.migration_token }) - .auth(ROLES.t1_analyst, 'changeme') - .expect(403); - }); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 8d8d62cc754a6..5a36b950b6a5b 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { KbnClient } from '@kbn/dev-utils'; import { ApiResponse, Client } from '@elastic/elasticsearch'; import { SuperTest } from 'supertest'; import supertestAsPromised from 'supertest-as-promised'; @@ -25,6 +26,7 @@ import { ExceptionListSchema, } from '../../plugins/lists/common'; import { Signal } from '../../plugins/security_solution/server/lib/detection_engine/signals/types'; +import { signalsMigrationType } from '../../plugins/security_solution/server/lib/detection_engine/migrations/saved_objects'; import { Status, SignalIds, @@ -1063,3 +1065,20 @@ export const waitForIndexToPopulate = async (es: Client, index: string): Promise return response.body.count > 0; }, `waitForIndexToPopulate: ${index}`); }; + +export const deleteMigrations = async ({ + ids, + kbnClient, +}: { + ids: string[]; + kbnClient: KbnClient; +}): Promise => { + await Promise.all( + ids.map((id) => + kbnClient.savedObjects.delete({ + id, + type: signalsMigrationType, + }) + ) + ); +}; diff --git a/x-pack/test/fleet_api_integration/apis/agents_setup.ts b/x-pack/test/fleet_api_integration/apis/agents_setup.ts index 4da1335da9a6c..85e533a569c87 100644 --- a/x-pack/test/fleet_api_integration/apis/agents_setup.ts +++ b/x-pack/test/fleet_api_integration/apis/agents_setup.ts @@ -62,10 +62,10 @@ export default function (providerContext: FtrProviderContext) { names: [ 'logs-*', 'metrics-*', - 'events-*', + 'traces-*', '.ds-logs-*', '.ds-metrics-*', - '.ds-events-*', + '.ds-traces-*', ], privileges: ['write', 'create_index', 'indices:admin/auto_create'], allow_restricted_indices: false, diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index a7d46b9c6677e..1d5f864c27eea 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -459,6 +459,14 @@ const expectAssetsInstalled = ({ }, ], installed_es: [ + { + id: 'logs-all_assets.test_logs-all_assets', + type: 'data_stream_ilm_policy', + }, + { + id: 'metrics-all_assets.test_metrics-all_assets', + type: 'data_stream_ilm_policy', + }, { id: 'logs-all_assets.test_logs', type: 'index_template', @@ -496,6 +504,7 @@ const expectAssetsInstalled = ({ { id: '96c6eb85-fe2e-56c6-84be-5fda976796db', type: 'epm-packages-assets' }, { id: '2d73a161-fa69-52d0-aa09-1bdc691b95bb', type: 'epm-packages-assets' }, { id: '0a00c2d2-ce63-5b9c-9aa0-0cf1938f7362', type: 'epm-packages-assets' }, + { id: '691f0505-18c5-57a6-9f40-06e8affbdf7a', type: 'epm-packages-assets' }, { id: 'b36e6dd0-58f7-5dd0-a286-8187e4019274', type: 'epm-packages-assets' }, { id: 'f839c76e-d194-555a-90a1-3265a45789e4', type: 'epm-packages-assets' }, { id: '9af7bbb3-7d8a-50fa-acc9-9dde6f5efca2', type: 'epm-packages-assets' }, diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index 37aa94beec8b0..7b264f949532e 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -293,6 +293,10 @@ export default function (providerContext: FtrProviderContext) { }, ], installed_es: [ + { + id: 'logs-all_assets.test_logs-all_assets', + type: 'data_stream_ilm_policy', + }, { id: 'logs-all_assets.test_logs-0.2.0', type: 'ingest_pipeline', diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/elasticsearch/ilm_policy/all_assets.json new file mode 100644 index 0000000000000..7cf62e890f865 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/elasticsearch/ilm_policy/all_assets.json @@ -0,0 +1,15 @@ +{ + "policy": { + "phases": { + "hot": { + "min_age": "0ms", + "actions": { + "rollover": { + "max_size": "50gb", + "max_age": "30d" + } + } + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/README.md b/x-pack/test/functional/README.md index 6da3ccfa2c6ee..35324397ac3fc 100644 --- a/x-pack/test/functional/README.md +++ b/x-pack/test/functional/README.md @@ -1,3 +1,3 @@ # Kibana Functional Testing -See our [Functional Testing Guide](https://www.elastic.co/guide/en/kibana/current/development-functional-tests.html) +See our [Functional Testing Guide](https://www.elastic.co/guide/en/kibana/current/development-tests.html#development-functional-tests) diff --git a/x-pack/test/functional/apps/discover/async_scripted_fields.js b/x-pack/test/functional/apps/discover/async_scripted_fields.js index 33a64e4f9cdd3..d71603cf3793f 100644 --- a/x-pack/test/functional/apps/discover/async_scripted_fields.js +++ b/x-pack/test/functional/apps/discover/async_scripted_fields.js @@ -18,7 +18,8 @@ export default function ({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const security = getService('security'); - describe('async search with scripted fields', function () { + // Failing: See https://github.com/elastic/kibana/issues/78553 + describe.skip('async search with scripted fields', function () { this.tags(['skipFirefox']); before(async function () { @@ -40,7 +41,7 @@ export default function ({ getService, getPageObjects }) { await security.testUser.restoreDefaults(); }); - it('query should show failed shards pop up', async function () { + it.skip('query should show failed shards pop up', async function () { if (false) { /* If you had to modify the scripted fields, you could un-comment all this, run it, use es_archiver to update 'kibana_scripted_fields_on_logstash' */ @@ -72,7 +73,7 @@ export default function ({ getService, getPageObjects }) { }); }); - it('query return results with valid scripted field', async function () { + it.skip('query return results with valid scripted field', async function () { if (false) { /* the commented-out steps below were used to create the scripted fields in the logstash-* index pattern which are now saved in the esArchive. diff --git a/x-pack/test/functional/apps/uptime/ping_redirects.ts b/x-pack/test/functional/apps/uptime/ping_redirects.ts index b87e8c1748c82..82b9c74c896ff 100644 --- a/x-pack/test/functional/apps/uptime/ping_redirects.ts +++ b/x-pack/test/functional/apps/uptime/ping_redirects.ts @@ -18,7 +18,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const monitor = () => uptime.monitor; - describe('Ping redirects', () => { + // FLAKY: https://github.com/elastic/kibana/issues/84992 + describe.skip('Ping redirects', () => { const start = '~ 15 minutes ago'; const end = 'now'; diff --git a/x-pack/test/functional/es_archives/ml/module_security_endpoint/data.json.gz b/x-pack/test/functional/es_archives/ml/module_security_endpoint/data.json.gz new file mode 100644 index 0000000000000..f9033c722776a Binary files /dev/null and b/x-pack/test/functional/es_archives/ml/module_security_endpoint/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/ml/module_security_endpoint/mappings.json b/x-pack/test/functional/es_archives/ml/module_security_endpoint/mappings.json new file mode 100644 index 0000000000000..e9e8b542849a0 --- /dev/null +++ b/x-pack/test/functional/es_archives/ml/module_security_endpoint/mappings.json @@ -0,0 +1,7755 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.file-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "Ext": { + "properties": { + "correlation": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "Ext": { + "properties": { + "original": { + "properties": { + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "windows": { + "properties": { + "zone_identifier": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "args_count": { + "type": "long" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.file-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "Ext": { + "properties": { + "correlation": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "Ext": { + "properties": { + "original": { + "properties": { + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "windows": { + "properties": { + "zone_identifier": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "args_count": { + "type": "long" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.library-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "dll": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + } + } + }, + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + } + } + }, + "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": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.library-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "dll": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + } + } + }, + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + } + } + }, + "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": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.network-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "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" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "dns": { + "properties": { + "Ext": { + "properties": { + "options": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "type": "long" + } + } + }, + "question": { + "properties": { + "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" + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + } + } + }, + "response": { + "properties": { + "Ext": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + } + } + }, + "message": { + "type": "text" + }, + "network": { + "properties": { + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "iana_number": { + "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" + } + } + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "address": { + "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" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.network-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "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" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "dns": { + "properties": { + "Ext": { + "properties": { + "options": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "type": "long" + } + } + }, + "question": { + "properties": { + "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" + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + } + } + }, + "response": { + "properties": { + "Ext": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + } + } + }, + "message": { + "type": "text" + }, + "network": { + "properties": { + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "iana_number": { + "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" + } + } + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "address": { + "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" + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.process-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "package": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + }, + "authentication_id": { + "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" + } + }, + "type": "nested" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "elevation": { + "type": "boolean" + }, + "elevation_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + }, + "real": { + "properties": { + "pid": { + "type": "long" + } + } + } + } + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.process-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "package": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + }, + "authentication_id": { + "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" + } + }, + "type": "nested" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "token": { + "properties": { + "elevation": { + "type": "boolean" + }, + "elevation_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "integrity_level_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "Ext": { + "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" + } + }, + "type": "nested" + }, + "real": { + "properties": { + "pid": { + "type": "long" + } + } + } + } + }, + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.registry-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "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" + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.registry-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "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" + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.security-default-000001", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "ft_logs-endpoint.events.security-default-000002", + "mappings": { + "_meta": { + "managed": true, + "managed_by": "ingest-manager", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic": "false", + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "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" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "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" + }, + "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" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "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": { + "Ext": { + "properties": { + "variant": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "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" + } + } + }, + "message": { + "type": "text" + }, + "process": { + "properties": { + "Ext": { + "properties": { + "ancestry": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "caseless": { + "ignore_above": 1024, + "type": "keyword" + }, + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "thread": { + "properties": { + "id": { + "type": "long" + } + } + } + } + }, + "source": { + "properties": { + "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" + } + } + } + } + }, + "user": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "Ext": { + "properties": { + "real": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "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": { + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "0", + "number_of_shards": "1", + "refresh_interval": "-1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index da03fee476f13..b792aa2d183b6 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -55,8 +55,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--plugin-path=${corsTestPlugin}`, `--test.cors.port=${pluginPort}`, '--server.cors.enabled=true', - '--server.cors.credentials=true', - `--server.cors.origin=["${originUrl}"]`, + '--server.cors.allowCredentials=true', + `--server.cors.allowOrigin=["${originUrl}"]`, ], }, }; diff --git a/x-pack/test/functional_cors/tests/cors.ts b/x-pack/test/functional_cors/tests/cors.ts index ff5da26b4e275..774ffe1719f07 100644 --- a/x-pack/test/functional_cors/tests/cors.ts +++ b/x-pack/test/functional_cors/tests/cors.ts @@ -15,9 +15,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('CORS', () => { it('Communicates to Kibana with configured CORS', async () => { const args: string[] = config.get('kbnTestServer.serverArgs'); - const originSetting = args.find((str) => str.includes('server.cors.origin')); + const originSetting = args.find((str) => str.includes('server.cors.allowOrigin')); if (!originSetting) { - throw new Error('Cannot find "server.cors.origin" argument'); + throw new Error('Cannot find "server.cors.allowOrigin" argument'); } const [, value] = originSetting.split('='); const url = JSON.parse(value); diff --git a/x-pack/test/functional_enterprise_search/README.md b/x-pack/test/functional_enterprise_search/README.md index 63d13cbac7020..c1be4e06c1eb9 100644 --- a/x-pack/test/functional_enterprise_search/README.md +++ b/x-pack/test/functional_enterprise_search/README.md @@ -2,7 +2,7 @@ ## Running these tests -Follow the [Functional Test Runner instructions](https://www.elastic.co/guide/en/kibana/current/development-functional-tests.html#_running_functional_tests). +Follow the [Functional Test Runner instructions](https://www.elastic.co/guide/en/kibana/current/development-tests.html#development-functional-tests#_running_functional_tests). There are two suites available to run, a suite that requires a Kibana instance without an `enterpriseSearch.host` configured, and one that does. The later also [requires a running Enterprise Search instance](#enterprise-search-requirement), and a Private API key diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index f7281a1d93a46..16b338c893736 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -349,7 +349,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await deleteAlerts([createdAlert.id]); }); - it('should delete all selection', async () => { + it.skip('should delete all selection', async () => { const namePrefix = generateUniqueKey(); let count = 0; const createdAlertsFirstPage = await Promise.all( 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 6584c5891a8b9..f6cbc52e7a421 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 @@ -22,11 +22,12 @@ export const noopAlertType: AlertType = { name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor() {}, producer: 'alerts', }; -export const alwaysFiringAlertType: any = { +export const alwaysFiringAlertType: AlertType = { id: 'test.always-firing', name: 'Always Firing', actionGroups: [ @@ -35,6 +36,7 @@ export const alwaysFiringAlertType: any = { ], defaultActionGroupId: 'default', producer: 'alerts', + minimumLicenseRequired: 'basic', async executor(alertExecutorOptions: any) { const { services, state, params } = alertExecutorOptions; @@ -52,7 +54,7 @@ export const alwaysFiringAlertType: any = { }, }; -export const failingAlertType: any = { +export const failingAlertType: AlertType = { id: 'test.failing', name: 'Test: Failing', actionGroups: [ @@ -63,6 +65,7 @@ export const failingAlertType: any = { ], producer: 'alerts', defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', async executor() { throw new Error('Failed to execute alert type'); }, diff --git a/x-pack/test/licensing_plugin/scenario.ts b/x-pack/test/licensing_plugin/scenario.ts index 9b73a7793c527..b351c7443cbb6 100644 --- a/x-pack/test/licensing_plugin/scenario.ts +++ b/x-pack/test/licensing_plugin/scenario.ts @@ -67,6 +67,31 @@ export function createScenario({ getService, getPageObjects }: FtrProviderContex expect(response.body.trial_was_started).to.be(true); }, + async startEnterprise() { + const response = await esSupertestWithoutAuth + .post('/_license/?acknowledge=true') + .send({ + license: { + uid: '00000000-d3ad-7357-c0d3-000000000000', + type: 'enterprise', + issue_date_in_millis: 1577836800000, + start_date_in_millis: 1577836800000, + // expires 2022-12-31 + expiry_date_in_millis: 1672531199999, + max_resource_units: 250, + max_nodes: null, + issued_to: 'Elastic Internal Use (development environments)', + issuer: 'Elastic', + signature: + 'AAAABQAAAA1gHUVis7hel8b8nNCAAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAKMR+w3KZsMJfG5jNWgZXJLwRmiNqN7k94vKFgRdj1yM+gA9ufhXIn9d01OvFhPjilIqm+fxVjCxXwGKbFRiwtTWnTYjXPuNml+qCFGgUWguWEcVoIW6VU7/lYOqMJ4EB4zOMLe93P267iaDm542aelQrW1OJ69lGGuPBik8v9r1bNZzKBQ99VUr/qoosGDAm0udh2HxWzYoCL5lDML5Niy87xlVCubSSBXdUXzUgdZKKk6pKaMdHswB1gjvEfnwqPxEWAyrV0BCr/T1WehXd7U4p6/zt6sJ6cPh+34AZe9g4+3WPKrZhX4iaSHMDDHn4HNjO72CZ2oi42ZDNnJ37tA=', + }, + }) + .auth('license_manager_user', 'license_manager_user-password') + .expect(200); + + expect(response.body.license_status).to.be('valid'); + }, + async deleteLicense() { const response = await esSupertestWithoutAuth .delete('/_license') diff --git a/x-pack/test/licensing_plugin/server/updates.ts b/x-pack/test/licensing_plugin/server/updates.ts index e24b71939213c..ecfaae5f46620 100644 --- a/x-pack/test/licensing_plugin/server/updates.ts +++ b/x-pack/test/licensing_plugin/server/updates.ts @@ -62,5 +62,13 @@ export default function (ftrContext: FtrProviderContext) { // banner shown only when license expired not just deleted await testSubjects.missingOrFail('licenseExpiredBanner'); }); + + it('properly recognize an enterprise license', async () => { + await scenario.startEnterprise(); + await scenario.waitForPluginToDetectLicenseUpdate(); + + const enterpriseLicense = await scenario.getLicense(); + expect(enterpriseLicense.license?.type).to.be('enterprise'); + }); }); } diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/fleet_integrations.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/fleet_integrations.ts new file mode 100644 index 0000000000000..1f50ba4d460df --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/fleet_integrations.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const { fleetIntegrations, trustedApps } = getPageObjects(['trustedApps', 'fleetIntegrations']); + const policyTestResources = getService('policyTestResources'); + const testSubjects = getService('testSubjects'); + + describe('When in the Fleet application', function () { + this.tags(['ciGroup7']); + + describe('and on the Endpoint Integration details page', () => { + beforeEach(async () => { + await fleetIntegrations.navigateToIntegrationDetails( + await policyTestResources.getEndpointPkgKey() + ); + }); + + it('should show the Custom tab', async () => { + await fleetIntegrations.integrationDetailCustomTabExistsOrFail(); + }); + + it('should display the endpoint custom content', async () => { + await (await fleetIntegrations.findIntegrationDetailCustomTab()).click(); + await testSubjects.existOrFail('fleetEndpointPackageCustomContent'); + }); + + it('should show the Trusted Apps page when link is clicked', async () => { + await (await fleetIntegrations.findIntegrationDetailCustomTab()).click(); + await (await testSubjects.find('linkToTrustedApps')).click(); + await trustedApps.ensureIsOnTrustedAppsListPage(); + }); + }); + }); +} diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 3103d461669f1..bb740ef8acb88 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -33,5 +33,6 @@ export default function (providerContext: FtrProviderContext) { loadTestFile(require.resolve('./resolver')); loadTestFile(require.resolve('./endpoint_telemetry')); loadTestFile(require.resolve('./trusted_apps_list')); + loadTestFile(require.resolve('./fleet_integrations')); }); } diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 355e494cb459e..1a5c99294c281 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -553,35 +553,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { } }); - it('should show callout', async () => { - await testSubjects.existOrFail('endpointPackagePolicy_edit'); - }); - - it('should show actions button with expected action items', async () => { - const actionsButton = await pageObjects.ingestManagerCreatePackagePolicy.findEndpointActionsButton(); - await actionsButton.click(); - const menuPanel = await testSubjects.find('endpointActionsMenuPanel'); - const actionItems = await menuPanel.findAllByTagName<'button'>('button'); - const expectedItems = ['Edit Trusted Applications']; - - for (const action of actionItems) { - const buttonText = await action.getVisibleText(); - expect(buttonText).to.be(expectedItems.find((item) => item === buttonText)); - } - }); - - it('should navigate to Trusted Apps', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps'); - await pageObjects.trustedApps.ensureIsOnTrustedAppsListPage(); - }); - - it('should show the back button on Trusted Apps Page and navigate back to fleet', async () => { - await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps'); - const backButton = await pageObjects.trustedApps.findTrustedAppsListPageBackButton(); - await backButton.click(); - await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); - }); - it('should show the endpoint policy form', async () => { await testSubjects.existOrFail('endpointIntegrationPolicyForm'); }); diff --git a/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts new file mode 100644 index 0000000000000..3c747afab48c8 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/page_objects/fleet_integrations_page.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; +import { PLUGIN_ID } from '../../../plugins/fleet/common'; + +// NOTE: import path below should be the deep path to the actual module - else we get CI errors +import { pagePathGetters } from '../../../plugins/fleet/public/applications/fleet/constants/page_paths'; + +export function FleetIntegrations({ getService, getPageObjects }: FtrProviderContext) { + const pageObjects = getPageObjects(['common']); + const testSubjects = getService('testSubjects'); + + return { + async navigateToIntegrationDetails(pkgkey: string) { + await pageObjects.common.navigateToApp(PLUGIN_ID, { + hash: pagePathGetters.integration_details({ pkgkey }), + }); + }, + + async integrationDetailCustomTabExistsOrFail() { + await testSubjects.existOrFail('tab-custom'); + }, + + async findIntegrationDetailCustomTab() { + return await testSubjects.find('tab-custom'); + }, + }; +} diff --git a/x-pack/test/security_solution_endpoint/page_objects/index.ts b/x-pack/test/security_solution_endpoint/page_objects/index.ts index 3664a2033d8b7..2fb441464e7ee 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/index.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/index.ts @@ -11,6 +11,7 @@ import { TrustedAppsPageProvider } from './trusted_apps_page'; import { EndpointPageUtils } from './page_utils'; import { IngestManagerCreatePackagePolicy } from './ingest_manager_create_package_policy_page'; import { SecurityHostsPageProvider } from './hosts_page'; +import { FleetIntegrations } from './fleet_integrations_page'; export const pageObjects = { ...xpackFunctionalPageObjects, @@ -20,4 +21,5 @@ export const pageObjects = { endpointPageUtils: EndpointPageUtils, ingestManagerCreatePackagePolicy: IngestManagerCreatePackagePolicy, hosts: SecurityHostsPageProvider, + fleetIntegrations: FleetIntegrations, }; diff --git a/x-pack/test/security_solution_endpoint/services/endpoint_policy.ts b/x-pack/test/security_solution_endpoint/services/endpoint_policy.ts index 1b1d0bf96a187..5f54ab2539c5d 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint_policy.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint_policy.ts @@ -19,6 +19,9 @@ import { import { factory as policyConfigFactory } from '../../../plugins/security_solution/common/endpoint/models/policy_config'; import { Immutable } from '../../../plugins/security_solution/common/endpoint/types'; +// NOTE: import path below should be the deep path to the actual module - else we get CI errors +import { pkgKeyFromPackageInfo } from '../../../plugins/fleet/public/applications/fleet/services/pkg_key_from_package_info'; + const INGEST_API_ROOT = '/api/fleet'; const INGEST_API_AGENT_POLICIES = `${INGEST_API_ROOT}/agent_policies`; const INGEST_API_AGENT_POLICIES_DELETE = `${INGEST_API_AGENT_POLICIES}/delete`; @@ -106,6 +109,14 @@ export function EndpointPolicyTestResourcesProvider({ getService }: FtrProviderC })(); return { + /** + * Returns the endpoint package key for the currently installed package. This `pkgkey` can then + * be used to build URLs for Fleet pages or APIs + */ + async getEndpointPkgKey() { + return pkgKeyFromPackageInfo((await retrieveEndpointPackageInfo())!); + }, + /** * Retrieves the full Agent policy, which mirrors what the Elastic Agent would get * once they checkin. diff --git a/yarn.lock b/yarn.lock index cc7edd2aaeccb..2cf4f36f7e593 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1439,10 +1439,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@30.5.1": - version "30.5.1" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-30.5.1.tgz#c9782c2f4763d563de6afcc2fc56d81c1e5b183c" - integrity sha512-W8rW49prYG0XHNdMWGTxNW50Kef3/fh+IL5mzMOKLao1W4h0F45efIDbnIHyjGl//akknIIEa6bwdTU4dmLBgA== +"@elastic/eui@30.6.0": + version "30.6.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-30.6.0.tgz#6653223223f52407ac05303825d9bd08382df1d5" + integrity sha512-40Jiy54MpJAx3lD3NSZZLkMkVySwKpX6RxIKnvT3somE95pwIjXrWB688m2nL2g05y7kNhjrhwfdctVzNXZENA== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -1476,6 +1476,7 @@ tabbable "^3.0.0" text-diff "^1.0.1" unified "^9.2.0" + url-parse "^1.4.7" uuid "^8.3.0" vfile "^4.2.0" @@ -27898,7 +27899,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3: +url-parse@^1.4.3, url-parse@^1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==