diff --git a/.ci/Jenkinsfile_coverage b/.ci/Jenkinsfile_coverage new file mode 100644 index 00000000000000..d9ec1861c9979e --- /dev/null +++ b/.ci/Jenkinsfile_coverage @@ -0,0 +1,112 @@ +#!/bin/groovy + +library 'kibana-pipeline-library' +kibanaLibrary.load() // load from the Jenkins instance + +stage("Kibana Pipeline") { // This stage is just here to help the BlueOcean UI a little bit + timeout(time: 180, unit: 'MINUTES') { + timestamps { + ansiColor('xterm') { + catchError { + withEnv([ + 'CODE_COVERAGE=1', // Needed for multiple ci scripts, such as remote.ts, test/scripts/*.sh, schema.js, etc. + ]) { + parallel([ + 'kibana-intake-agent': { + withEnv([ + 'NODE_ENV=test' // Needed for jest tests only + ]) { + kibanaPipeline.legacyJobRunner('kibana-intake')() + } + }, + 'x-pack-intake-agent': { + withEnv([ + 'NODE_ENV=test' // Needed for jest tests only + ]) { + kibanaPipeline.legacyJobRunner('x-pack-intake')() + } + }, + 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ + 'oss-ciGroup1': kibanaPipeline.getOssCiGroupWorker(1), + 'oss-ciGroup2': kibanaPipeline.getOssCiGroupWorker(2), + 'oss-ciGroup3': kibanaPipeline.getOssCiGroupWorker(3), + 'oss-ciGroup4': kibanaPipeline.getOssCiGroupWorker(4), + 'oss-ciGroup5': kibanaPipeline.getOssCiGroupWorker(5), + 'oss-ciGroup6': kibanaPipeline.getOssCiGroupWorker(6), + 'oss-ciGroup7': kibanaPipeline.getOssCiGroupWorker(7), + 'oss-ciGroup8': kibanaPipeline.getOssCiGroupWorker(8), + 'oss-ciGroup9': kibanaPipeline.getOssCiGroupWorker(9), + 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), + 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), + 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), + ]), + 'kibana-xpack-agent-1': kibanaPipeline.withWorkers('kibana-xpack-tests-1', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), + 'xpack-ciGroup2': kibanaPipeline.getXpackCiGroupWorker(2), + ]), + 'kibana-xpack-agent-2': kibanaPipeline.withWorkers('kibana-xpack-tests-2', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup3': kibanaPipeline.getXpackCiGroupWorker(3), + 'xpack-ciGroup4': kibanaPipeline.getXpackCiGroupWorker(4), + ]), + + 'kibana-xpack-agent-3': kibanaPipeline.withWorkers('kibana-xpack-tests-3', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup5': kibanaPipeline.getXpackCiGroupWorker(5), + 'xpack-ciGroup6': kibanaPipeline.getXpackCiGroupWorker(6), + 'xpack-ciGroup7': kibanaPipeline.getXpackCiGroupWorker(7), + 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), + 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), + 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), + ]), + ]) + kibanaPipeline.jobRunner('tests-l', false) { + kibanaPipeline.downloadCoverageArtifacts() + kibanaPipeline.bash( + ''' + # bootstrap from x-pack folder + source src/dev/ci_setup/setup_env.sh + cd x-pack + yarn kbn bootstrap --prefer-offline + cd .. + # extract archives + mkdir -p /tmp/extracted_coverage + echo extracting intakes + tar -xzf /tmp/downloaded_coverage/coverage/kibana-intake/kibana-coverage.tar.gz -C /tmp/extracted_coverage + tar -xzf /tmp/downloaded_coverage/coverage/x-pack-intake/kibana-coverage.tar.gz -C /tmp/extracted_coverage + echo extracting kibana-oss-tests + tar -xzf /tmp/downloaded_coverage/coverage/kibana-oss-tests/kibana-coverage.tar.gz -C /tmp/extracted_coverage + echo extracting kibana-xpack-tests + for i in {1..3}; do + tar -xzf /tmp/downloaded_coverage/coverage/kibana-xpack-tests-${i}/kibana-coverage.tar.gz -C /tmp/extracted_coverage + done + # replace path in json files to have valid html report + pwd=$(pwd) + du -sh /tmp/extracted_coverage/target/kibana-coverage/ + echo replacing path in json files + for i in {1..9}; do + sed -i "s|/dev/shm/workspace/kibana|$pwd|g" /tmp/extracted_coverage/target/kibana-coverage/functional/${i}*.json & + done + wait + # merge oss & x-pack reports + echo merging coverage reports + yarn nyc report --temp-dir /tmp/extracted_coverage/target/kibana-coverage/jest --report-dir target/kibana-coverage/jest-combined --reporter=html --reporter=json-summary + yarn nyc report --temp-dir /tmp/extracted_coverage/target/kibana-coverage/functional --report-dir target/kibana-coverage/functional-combined --reporter=html --reporter=json-summary + echo copy mocha reports + mkdir -p target/kibana-coverage/mocha-combined + cp -r /tmp/extracted_coverage/target/kibana-coverage/mocha target/kibana-coverage/mocha-combined + ''', + "run `yarn kbn bootstrap && merge coverage`" + ) + sh 'tar -czf kibana-jest-coverage.tar.gz target/kibana-coverage/jest-combined/*' + kibanaPipeline.uploadCoverageArtifacts("coverage/jest-combined", 'kibana-jest-coverage.tar.gz') + sh 'tar -czf kibana-functional-coverage.tar.gz target/kibana-coverage/functional-combined/*' + kibanaPipeline.uploadCoverageArtifacts("coverage/functional-combined", 'kibana-functional-coverage.tar.gz') + sh 'tar -czf kibana-mocha-coverage.tar.gz target/kibana-coverage/mocha-combined/*' + kibanaPipeline.uploadCoverageArtifacts("coverage/mocha-combined", 'kibana-mocha-coverage.tar.gz') + } + } + } + kibanaPipeline.sendMail() + } + } + } +} diff --git a/.ci/es-snapshots/Jenkinsfile_build_es b/.ci/es-snapshots/Jenkinsfile_build_es new file mode 100644 index 00000000000000..ad0ad54275e12d --- /dev/null +++ b/.ci/es-snapshots/Jenkinsfile_build_es @@ -0,0 +1,162 @@ +#!/bin/groovy + +// This job effectively has two SCM configurations: +// one for kibana, used to check out this Jenkinsfile (which means it's the job's main SCM configuration), as well as kick-off the downstream verification job +// one for elasticsearch, used to check out the elasticsearch source before building it + +// There are two parameters that drive which branch is checked out for each of these, but they will typically be the same +// 'branch_specifier' is for kibana / the job itself +// ES_BRANCH is for elasticsearch + +library 'kibana-pipeline-library' +kibanaLibrary.load() + +def ES_BRANCH = params.ES_BRANCH + +if (!ES_BRANCH) { + error "Parameter 'ES_BRANCH' must be specified." +} + +currentBuild.displayName += " - ${ES_BRANCH}" +currentBuild.description = "ES: ${ES_BRANCH}
Kibana: ${params.branch_specifier}" + +def PROMOTE_WITHOUT_VERIFY = !!params.PROMOTE_WITHOUT_VERIFICATION + +timeout(time: 120, unit: 'MINUTES') { + timestamps { + ansiColor('xterm') { + node('linux && immutable') { + catchError { + def VERSION + def SNAPSHOT_ID + def DESTINATION + + def scmVars = checkoutEs(ES_BRANCH) + def GIT_COMMIT = scmVars.GIT_COMMIT + def GIT_COMMIT_SHORT = sh(script: "git rev-parse --short ${GIT_COMMIT}", returnStdout: true).trim() + + buildArchives('to-archive') + + dir('to-archive') { + def now = new Date() + def date = now.format("yyyyMMdd-HHmmss") + + def filesRaw = sh(script: "ls -1", returnStdout: true).trim() + def files = filesRaw + .split("\n") + .collect { filename -> + // Filename examples + // elasticsearch-oss-8.0.0-SNAPSHOT-linux-x86_64.tar.gz + // elasticsearch-8.0.0-SNAPSHOT-linux-x86_64.tar.gz + def parts = filename.replace("elasticsearch-oss", "oss").split("-") + + VERSION = VERSION ?: parts[1] + SNAPSHOT_ID = SNAPSHOT_ID ?: "${date}_${GIT_COMMIT_SHORT}" + DESTINATION = DESTINATION ?: "${VERSION}/archives/${SNAPSHOT_ID}" + + return [ + filename: filename, + checksum: filename + '.sha512', + url: "https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${DESTINATION}/${filename}".toString(), + version: parts[1], + platform: parts[3], + architecture: parts[4].split('\\.')[0], + license: parts[0] == 'oss' ? 'oss' : 'default', + ] + } + + sh 'find * -exec bash -c "shasum -a 512 {} > {}.sha512" \\;' + + def manifest = [ + bucket: "kibana-ci-es-snapshots-daily/${DESTINATION}".toString(), + branch: ES_BRANCH, + sha: GIT_COMMIT, + sha_short: GIT_COMMIT_SHORT, + version: VERSION, + generated: now.format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC")), + archives: files, + ] + def manifestJson = toJSON(manifest).toString() + writeFile file: 'manifest.json', text: manifestJson + + upload(DESTINATION, '*.*') + + sh "cp manifest.json manifest-latest.json" + upload(VERSION, 'manifest-latest.json') + } + + if (PROMOTE_WITHOUT_VERIFY) { + esSnapshots.promote(VERSION, SNAPSHOT_ID) + + emailext( + to: 'build-kibana@elastic.co', + subject: "ES snapshot promoted without verification: ${params.ES_BRANCH}", + body: '${SCRIPT,template="groovy-html.template"}', + mimeType: 'text/html', + ) + } else { + build( + propagate: false, + wait: false, + job: 'elasticsearch+snapshots+verify', + parameters: [ + string(name: 'branch_specifier', value: branch_specifier), + string(name: 'SNAPSHOT_VERSION', value: VERSION), + string(name: 'SNAPSHOT_ID', value: SNAPSHOT_ID), + ] + ) + } + } + + kibanaPipeline.sendMail() + } + } + } +} + +def checkoutEs(branch) { + retryWithDelay(8, 15) { + return checkout([ + $class: 'GitSCM', + branches: [[name: branch]], + doGenerateSubmoduleConfigurations: false, + extensions: [], + submoduleCfg: [], + userRemoteConfigs: [[ + credentialsId: 'f6c7695a-671e-4f4f-a331-acdce44ff9ba', + url: 'git@github.com:elastic/elasticsearch', + ]], + ]) + } +} + +def upload(destination, pattern) { + return googleStorageUpload( + credentialsId: 'kibana-ci-gcs-plugin', + bucket: "gs://kibana-ci-es-snapshots-daily/${destination}", + pattern: pattern, + sharedPublicly: false, + showInline: false, + ) +} + +def buildArchives(destination) { + def props = readProperties file: '.ci/java-versions.properties' + withEnv([ + // Select the correct JDK for this branch + "PATH=/var/lib/jenkins/.java/${props.ES_BUILD_JAVA}/bin:${env.PATH}", + + // These Jenkins env vars trigger some automation in the elasticsearch repo that we don't want + "BUILD_NUMBER=", + "JENKINS_URL=", + "BUILD_URL=", + "JOB_NAME=", + "NODE_NAME=", + ]) { + sh """ + ./gradlew -p distribution/archives assemble --parallel + mkdir -p ${destination} + find distribution/archives -type f \\( -name 'elasticsearch-*-*-*-*.tar.gz' -o -name 'elasticsearch-*-*-*-*.zip' \\) -not -path *no-jdk* -exec cp {} ${destination} \\; + """ + } +} diff --git a/.ci/es-snapshots/Jenkinsfile_trigger_build_es b/.ci/es-snapshots/Jenkinsfile_trigger_build_es new file mode 100644 index 00000000000000..186917e9678245 --- /dev/null +++ b/.ci/es-snapshots/Jenkinsfile_trigger_build_es @@ -0,0 +1,19 @@ +#!/bin/groovy + +if (!params.branches_yaml) { + error "'branches_yaml' parameter must be specified" +} + +def branches = readYaml text: params.branches_yaml + +branches.each { branch -> + build( + propagate: false, + wait: false, + job: 'elasticsearch+snapshots+build', + parameters: [ + string(name: 'branch_specifier', value: branch), + string(name: 'ES_BRANCH', value: branch), + ] + ) +} diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es new file mode 100644 index 00000000000000..3d5ec75fa0e72a --- /dev/null +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -0,0 +1,72 @@ +#!/bin/groovy + +library 'kibana-pipeline-library' +kibanaLibrary.load() + +def SNAPSHOT_VERSION = params.SNAPSHOT_VERSION +def SNAPSHOT_ID = params.SNAPSHOT_ID + +if (!SNAPSHOT_VERSION) { + error "Parameter SNAPSHOT_VERSION must be specified" +} + +if (!SNAPSHOT_ID) { + error "Parameter SNAPSHOT_ID must be specified" +} + +currentBuild.displayName += " - ${SNAPSHOT_VERSION}" +currentBuild.description = "ES: ${SNAPSHOT_VERSION}
Kibana: ${params.branch_specifier}" + +def SNAPSHOT_MANIFEST = "https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${SNAPSHOT_VERSION}/archives/${SNAPSHOT_ID}/manifest.json" + +timeout(time: 120, unit: 'MINUTES') { + timestamps { + ansiColor('xterm') { + catchError { + withEnv(["ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}"]) { + parallel([ + // TODO we just need to run integration tests from intake? + 'kibana-intake-agent': kibanaPipeline.legacyJobRunner('kibana-intake'), + 'x-pack-intake-agent': kibanaPipeline.legacyJobRunner('x-pack-intake'), + 'kibana-oss-agent': kibanaPipeline.withWorkers('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ + 'oss-ciGroup1': kibanaPipeline.getOssCiGroupWorker(1), + 'oss-ciGroup2': kibanaPipeline.getOssCiGroupWorker(2), + 'oss-ciGroup3': kibanaPipeline.getOssCiGroupWorker(3), + 'oss-ciGroup4': kibanaPipeline.getOssCiGroupWorker(4), + 'oss-ciGroup5': kibanaPipeline.getOssCiGroupWorker(5), + 'oss-ciGroup6': kibanaPipeline.getOssCiGroupWorker(6), + 'oss-ciGroup7': kibanaPipeline.getOssCiGroupWorker(7), + 'oss-ciGroup8': kibanaPipeline.getOssCiGroupWorker(8), + 'oss-ciGroup9': kibanaPipeline.getOssCiGroupWorker(9), + 'oss-ciGroup10': kibanaPipeline.getOssCiGroupWorker(10), + 'oss-ciGroup11': kibanaPipeline.getOssCiGroupWorker(11), + 'oss-ciGroup12': kibanaPipeline.getOssCiGroupWorker(12), + ]), + 'kibana-xpack-agent': kibanaPipeline.withWorkers('kibana-xpack-tests', { kibanaPipeline.buildXpack() }, [ + 'xpack-ciGroup1': kibanaPipeline.getXpackCiGroupWorker(1), + 'xpack-ciGroup2': kibanaPipeline.getXpackCiGroupWorker(2), + 'xpack-ciGroup3': kibanaPipeline.getXpackCiGroupWorker(3), + 'xpack-ciGroup4': kibanaPipeline.getXpackCiGroupWorker(4), + 'xpack-ciGroup5': kibanaPipeline.getXpackCiGroupWorker(5), + 'xpack-ciGroup6': kibanaPipeline.getXpackCiGroupWorker(6), + 'xpack-ciGroup7': kibanaPipeline.getXpackCiGroupWorker(7), + 'xpack-ciGroup8': kibanaPipeline.getXpackCiGroupWorker(8), + 'xpack-ciGroup9': kibanaPipeline.getXpackCiGroupWorker(9), + 'xpack-ciGroup10': kibanaPipeline.getXpackCiGroupWorker(10), + ]), + ]) + } + + promoteSnapshot(SNAPSHOT_VERSION, SNAPSHOT_ID) + } + + kibanaPipeline.sendMail() + } + } +} + +def promoteSnapshot(snapshotVersion, snapshotId) { + node('linux && immutable') { + esSnapshots.promote(snapshotVersion, snapshotId) + } +} diff --git a/.eslintrc.js b/.eslintrc.js index 03a674993ab507..c43366abf0c3d8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -113,12 +113,6 @@ module.exports = { 'react-hooks/exhaustive-deps': 'off', }, }, - { - files: ['src/legacy/core_plugins/vis_type_vega/**/*.{js,ts,tsx}'], - rules: { - 'react-hooks/exhaustive-deps': 'off', - }, - }, { files: ['src/legacy/ui/public/vis/**/*.{js,ts,tsx}'], rules: { @@ -177,12 +171,6 @@ module.exports = { 'react-hooks/exhaustive-deps': 'off', }, }, - { - files: ['x-pack/legacy/plugins/monitoring/**/*.{js,ts,tsx}'], - rules: { - 'jsx-a11y/click-events-have-key-events': 'off', - }, - }, { files: ['x-pack/legacy/plugins/snapshot_restore/**/*.{js,ts,tsx}'], rules: { @@ -253,6 +241,7 @@ module.exports = { '!x-pack/test/**/*', '(src|x-pack)/plugins/**/(public|server)/**/*', 'src/core/(public|server)/**/*', + 'examples/**/*', ], from: [ 'src/core/public/**/*', @@ -289,11 +278,15 @@ module.exports = { 'x-pack/legacy/plugins/**/*', '!x-pack/legacy/plugins/*/server/**/*', '!x-pack/legacy/plugins/*/index.{js,ts,tsx}', + + 'examples/**/*', + '!examples/**/server/**/*', ], from: [ 'src/core/server', 'src/core/server/**/*', '(src|x-pack)/plugins/*/server/**/*', + 'examples/**/server/**/*', ], errorMessage: 'Server modules cannot be imported into client modules or shared modules.', @@ -729,15 +722,13 @@ module.exports = { 'no-unreachable': 'error', 'no-unsafe-finally': 'error', 'no-useless-call': 'error', - // This will be turned on after bug fixes are mostly complete - // 'no-useless-catch': 'warn', + 'no-useless-catch': 'error', 'no-useless-concat': 'error', 'no-useless-computed-key': 'error', // This will be turned on after bug fixes are mostly complete // 'no-useless-escape': 'warn', 'no-useless-rename': 'error', - // This will be turned on after bug fixes are mostly complete - // 'no-useless-return': 'warn', + 'no-useless-return': 'error', // This will be turned on after bug fixers are mostly complete // 'no-void': 'warn', 'one-var-declaration-per-line': 'error', @@ -745,14 +736,13 @@ module.exports = { 'prefer-promise-reject-errors': 'error', 'prefer-rest-params': 'error', 'prefer-spread': 'error', - // This style will be turned on after most bugs are fixed - // 'prefer-template': 'warn', + 'prefer-template': 'error', 'react/boolean-prop-naming': 'error', 'react/button-has-type': 'error', + 'react/display-name': 'error', 'react/forbid-dom-props': 'error', 'react/no-access-state-in-setstate': 'error', - // This style will be turned on after most bugs are fixed - // 'react/no-children-prop': 'warn', + 'react/no-children-prop': 'error', 'react/no-danger-with-children': 'error', 'react/no-deprecated': 'error', 'react/no-did-mount-set-state': 'error', @@ -814,21 +804,6 @@ module.exports = { }, }, - /** - * Monitoring overrides - */ - { - files: ['x-pack/legacy/plugins/monitoring/**/*.js'], - rules: { - 'no-unused-vars': ['error', { args: 'all', argsIgnorePattern: '^_' }], - 'no-else-return': 'error', - }, - }, - { - files: ['x-pack/legacy/plugins/monitoring/public/**/*.js'], - env: { browser: true }, - }, - /** * Canvas overrides */ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1137fb99f81a73..a0a22446ba31d1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,6 +14,7 @@ /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/home/ @elastic/kibana-app /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app +/src/legacy/core_plugins/metrics/ @elastic/kibana-app /src/plugins/home/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app /src/plugins/timelion/ @elastic/kibana-app @@ -30,6 +31,7 @@ /src/plugins/visualizations/ @elastic/kibana-app-arch /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch /src/legacy/core_plugins/data/ @elastic/kibana-app-arch +/src/legacy/core_plugins/elasticsearch/lib/create_proxy.js @elastic/kibana-app-arch /src/legacy/core_plugins/embeddable_api/ @elastic/kibana-app-arch /src/legacy/core_plugins/interpreter/ @elastic/kibana-app-arch /src/legacy/core_plugins/kibana_react/ @elastic/kibana-app-arch @@ -146,6 +148,3 @@ /x-pack/legacy/plugins/searchprofiler/ @elastic/es-ui /x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui /x-pack/legacy/plugins/watcher/ @elastic/es-ui - -# Kibana TSVB external contractors -/src/legacy/core_plugins/metrics/ @elastic/kibana-tsvb-external diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06e08c85dafec0..6ae3db559b61ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -190,6 +190,19 @@ These snapshots are built on a nightly basis which expire after a couple weeks. yarn es snapshot ``` +##### Keeping data between snapshots + +If you want to keep the data inside your Elasticsearch between usages of this command, +you should use the following command, to keep your data folder outside the downloaded snapshot +folder: + +```bash +yarn es snapshot -E path.data=../data +``` + +The same parameter can be used with the source and archive command shown in the following +paragraphs. + #### Source By default, it will reference an [elasticsearch](https://github.com/elastic/elasticsearch) checkout which is a sibling to the Kibana directory named `elasticsearch`. If you wish to use a checkout in another location you can provide that by supplying `--source-path` diff --git a/docs/developer/plugin/development-uiexports.asciidoc b/docs/developer/plugin/development-uiexports.asciidoc index 6368446f7fb437..18d326cbfb9c08 100644 --- a/docs/developer/plugin/development-uiexports.asciidoc +++ b/docs/developer/plugin/development-uiexports.asciidoc @@ -9,8 +9,8 @@ An aggregate list of available UiExport types: | hacks | Any module that should be included in every application | visTypes | Modules that register providers with the `ui/registry/vis_types` registry. | inspectorViews | Modules that register custom inspector views via the `viewRegistry` in `ui/inspector`. -| chromeNavControls | Modules that register providers with the `ui/registry/chrome_nav_controls` registry. -| navbarExtensions | Modules that register providers with the `ui/registry/navbar_extensions` registry. -| docViews | Modules that register providers with the `ui/registry/doc_views` registry. +| chromeNavControls | Modules that register providers with the `ui/registry/chrome_header_nav_controls` registry. +| navbarExtensions | Modules that register providers with the setup contract of the `navigation` plugin. +| docViews | Modules that register providers with the setup contract method `addDocView` of the `discover` plugin. | app | Adds an application to the system. This uiExport type is defined as an object of metadata rather than just a module id. |======================================================================= diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md index 1ce18834f53196..a4fa3f17d0d94f 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.find.md @@ -9,5 +9,5 @@ Search for objects Signature: ```typescript -find: (options: Pick) => Promise>; +find: (options: Pick) => Promise>; ``` diff --git a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md index 3b916db9726738..88485aa71f7c54 100644 --- a/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md +++ b/docs/development/core/public/kibana-plugin-public.savedobjectsclient.md @@ -24,7 +24,7 @@ The constructor for this class is marked as internal. Third-party code should no | [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | (objects?: {
id: string;
type: string;
}[]) => Promise<SavedObjectsBatchResponse<SavedObjectAttributes>> | Returns an array of objects by id | | [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>> | Persists an object | | [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | (type: string, id: string) => Promise<{}> | Deletes an object | -| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>> | Search for objects | +| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "perPage" | "sortField" | "fields" | "searchFields" | "hasReference" | "defaultSearchOperator">) => Promise<SavedObjectsFindResponsePublic<T>> | Search for objects | | [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>> | Fetches a single object | ## Methods diff --git a/docs/development/core/server/kibana-plugin-server.clusterclient.asscoped.md b/docs/development/core/server/kibana-plugin-server.clusterclient.asscoped.md index ed7d028a1ec8a2..bb1f481c9ef4fb 100644 --- a/docs/development/core/server/kibana-plugin-server.clusterclient.asscoped.md +++ b/docs/development/core/server/kibana-plugin-server.clusterclient.asscoped.md @@ -9,14 +9,14 @@ Creates an instance of [IScopedClusterClient](./kibana-plugin-server.iscopedclus Signature: ```typescript -asScoped(request?: KibanaRequest | LegacyRequest | FakeRequest): IScopedClusterClient; +asScoped(request?: ScopeableRequest): IScopedClusterClient; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| request | KibanaRequest | LegacyRequest | FakeRequest | Request the IScopedClusterClient instance will be scoped to. Supports request optionality, Legacy.Request & FakeRequest for BWC with LegacyPlatform | +| request | ScopeableRequest | Request the IScopedClusterClient instance will be scoped to. Supports request optionality, Legacy.Request & FakeRequest for BWC with LegacyPlatform | Returns: diff --git a/docs/development/core/server/kibana-plugin-server.clusterclient.md b/docs/development/core/server/kibana-plugin-server.clusterclient.md index 5fdda7ef3e499a..d547b846e65b79 100644 --- a/docs/development/core/server/kibana-plugin-server.clusterclient.md +++ b/docs/development/core/server/kibana-plugin-server.clusterclient.md @@ -4,7 +4,7 @@ ## ClusterClient class -Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`). +Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`). See [ClusterClient](./kibana-plugin-server.clusterclient.md). diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient.md new file mode 100644 index 00000000000000..415423f555266d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) > [adminClient](./kibana-plugin-server.elasticsearchservicesetup.adminclient.md) + +## ElasticsearchServiceSetup.adminClient property + +A client for the `admin` cluster. All Elasticsearch config value changes are processed under the hood. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). + +Signature: + +```typescript +readonly adminClient: IClusterClient; +``` + +## Example + + +```js +const client = core.elasticsearch.adminClient; + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient_.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient_.md deleted file mode 100644 index b5bfc68d3ca0c4..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.adminclient_.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) > [adminClient$](./kibana-plugin-server.elasticsearchservicesetup.adminclient_.md) - -## ElasticsearchServiceSetup.adminClient$ property - -Observable of clients for the `admin` cluster. Observable emits when Elasticsearch config changes on the Kibana server. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). - - -```js -const client = await elasticsearch.adminClient$.pipe(take(1)).toPromise(); - -``` - -Signature: - -```typescript -readonly adminClient$: Observable; -``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md index 3d26f2d4cec889..797f402cc25807 100644 --- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md @@ -9,7 +9,7 @@ Create application specific Elasticsearch cluster API client with customized con Signature: ```typescript -readonly createClient: (type: string, clientConfig?: Partial) => IClusterClient; +readonly createClient: (type: string, clientConfig?: Partial) => ICustomClusterClient; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient.md new file mode 100644 index 00000000000000..e9845dce6915d0 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) > [dataClient](./kibana-plugin-server.elasticsearchservicesetup.dataclient.md) + +## ElasticsearchServiceSetup.dataClient property + +A client for the `data` cluster. All Elasticsearch config value changes are processed under the hood. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). + +Signature: + +```typescript +readonly dataClient: IClusterClient; +``` + +## Example + + +```js +const client = core.elasticsearch.dataClient; + +``` + diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient_.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient_.md deleted file mode 100644 index 9411f2f6b86946..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.dataclient_.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) > [dataClient$](./kibana-plugin-server.elasticsearchservicesetup.dataclient_.md) - -## ElasticsearchServiceSetup.dataClient$ property - -Observable of clients for the `data` cluster. Observable emits when Elasticsearch config changes on the Kibana server. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). - - -```js -const client = await elasticsearch.dataClient$.pipe(take(1)).toPromise(); - -``` - -Signature: - -```typescript -readonly dataClient$: Observable; -``` diff --git a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md index e3d151cdc0d8bb..2de3f6e6d1bbca 100644 --- a/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.md @@ -15,17 +15,7 @@ export interface ElasticsearchServiceSetup | Property | Type | Description | | --- | --- | --- | -| [adminClient$](./kibana-plugin-server.elasticsearchservicesetup.adminclient_.md) | Observable<IClusterClient> | Observable of clients for the admin cluster. Observable emits when Elasticsearch config changes on the Kibana server. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). -```js -const client = await elasticsearch.adminClient$.pipe(take(1)).toPromise(); - -``` - | -| [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md) | (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => IClusterClient | Create application specific Elasticsearch cluster API client with customized config. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). | -| [dataClient$](./kibana-plugin-server.elasticsearchservicesetup.dataclient_.md) | Observable<IClusterClient> | Observable of clients for the data cluster. Observable emits when Elasticsearch config changes on the Kibana server. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). -```js -const client = await elasticsearch.dataClient$.pipe(take(1)).toPromise(); - -``` - | +| [adminClient](./kibana-plugin-server.elasticsearchservicesetup.adminclient.md) | IClusterClient | A client for the admin cluster. All Elasticsearch config value changes are processed under the hood. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). | +| [createClient](./kibana-plugin-server.elasticsearchservicesetup.createclient.md) | (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ICustomClusterClient | Create application specific Elasticsearch cluster API client with customized config. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). | +| [dataClient](./kibana-plugin-server.elasticsearchservicesetup.dataclient.md) | IClusterClient | A client for the data cluster. All Elasticsearch config value changes are processed under the hood. See [IClusterClient](./kibana-plugin-server.iclusterclient.md). | diff --git a/docs/development/core/server/kibana-plugin-server.iclusterclient.md b/docs/development/core/server/kibana-plugin-server.iclusterclient.md index 834afa6db51570..e7435a9d91a74d 100644 --- a/docs/development/core/server/kibana-plugin-server.iclusterclient.md +++ b/docs/development/core/server/kibana-plugin-server.iclusterclient.md @@ -4,12 +4,12 @@ ## IClusterClient type -Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`). +Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`). See [ClusterClient](./kibana-plugin-server.clusterclient.md). Signature: ```typescript -export declare type IClusterClient = Pick; +export declare type IClusterClient = Pick; ``` diff --git a/docs/development/core/server/kibana-plugin-server.icustomclusterclient.md b/docs/development/core/server/kibana-plugin-server.icustomclusterclient.md new file mode 100644 index 00000000000000..bc9ea71bc23893 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.icustomclusterclient.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ICustomClusterClient](./kibana-plugin-server.icustomclusterclient.md) + +## ICustomClusterClient type + +Represents an Elasticsearch cluster API client created by a plugin.. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via `asScoped(...)`). + +See [ClusterClient](./kibana-plugin-server.clusterclient.md). + +Signature: + +```typescript +export declare type ICustomClusterClient = Pick; +``` diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index 5e7f84c55244d4..3f01048846fe1a 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -17,7 +17,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | Class | Description | | --- | --- | | [BasePath](./kibana-plugin-server.basepath.md) | Access or manipulate the Kibana base path | -| [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | +| [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | | [CspConfig](./kibana-plugin-server.cspconfig.md) | CSP configuration for use in Kibana. | | [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as body.error.header[WWW-Authenticate] | | [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. | @@ -175,8 +175,9 @@ The plugin integrates with the core system via lifecycle events: `setup` | [Headers](./kibana-plugin-server.headers.md) | Http request headers to read. | | [HttpResponsePayload](./kibana-plugin-server.httpresponsepayload.md) | Data send to the client as a response payload. | | [IBasePath](./kibana-plugin-server.ibasepath.md) | Access or manipulate the Kibana base path[BasePath](./kibana-plugin-server.basepath.md) | -| [IClusterClient](./kibana-plugin-server.iclusterclient.md) | Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | +| [IClusterClient](./kibana-plugin-server.iclusterclient.md) | Represents an Elasticsearch cluster API client created by the platform. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | | [IContextProvider](./kibana-plugin-server.icontextprovider.md) | A function that returns a context value for a specific key of given context type. | +| [ICustomClusterClient](./kibana-plugin-server.icustomclusterclient.md) | Represents an Elasticsearch cluster API client created by a plugin.. It allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)).See [ClusterClient](./kibana-plugin-server.clusterclient.md). | | [IsAuthenticated](./kibana-plugin-server.isauthenticated.md) | Return authentication status for a request. | | [ISavedObjectsRepository](./kibana-plugin-server.isavedobjectsrepository.md) | See [SavedObjectsRepository](./kibana-plugin-server.savedobjectsrepository.md) | | [IScopedClusterClient](./kibana-plugin-server.iscopedclusterclient.md) | Serves the same purpose as "normal" ClusterClient but exposes additional callAsCurrentUser method that doesn't use credentials of the Kibana internal user (as callAsInternalUser does) to request Elasticsearch API, but rather passes HTTP headers extracted from the current user request to the API.See [ScopedClusterClient](./kibana-plugin-server.scopedclusterclient.md). | @@ -213,6 +214,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing plugin state.\#\# SavedObjectsClient errorsSince the SavedObjectsClient has its hands in everything we are a little paranoid about the way we present errors back to to application code. Ideally, all errors will be either:1. Caused by bad implementation (ie. undefined is not a function) and as such unpredictable 2. An error that has been classified and decorated appropriately by the decorators in [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md)Type 1 errors are inevitable, but since all expected/handle-able errors should be Type 2 the isXYZError() helpers exposed at SavedObjectsErrorHelpers should be used to understand and manage error responses from the SavedObjectsClient.Type 2 errors are decorated versions of the source error, so if the elasticsearch client threw an error it will be decorated based on its type. That means that rather than looking for error.body.error.type or doing substring checks on error.body.error.reason, just use the helpers to understand the meaning of the error:\`\`\`js if (SavedObjectsErrorHelpers.isNotFoundError(error)) { // handle 404 }if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) { // 401 handling should be automatic, but in case you wanted to know }// always rethrow the error unless you handle it throw error; \`\`\`\#\#\# 404s from missing indexFrom the perspective of application code and APIs the SavedObjectsClient is a black box that persists objects. One of the internal details that users have no control over is that we use an elasticsearch index for persistance and that index might be missing.At the time of writing we are in the process of transitioning away from the operating assumption that the SavedObjects index is always available. Part of this transition is handling errors resulting from an index missing. These used to trigger a 500 error in most cases, and in others cause 404s with different error messages.From my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The object the request/call was targeting could not be found. This is why \#14141 takes special care to ensure that 404 errors are generic and don't distinguish between index missing or document missing.\#\#\# 503s from missing indexUnlike all other methods, create requests are supposed to succeed even when the Kibana index does not exist because it will be automatically created by elasticsearch. When that is not the case it is because Elasticsearch's action.auto_create_index setting prevents it from being created automatically so we throw a special 503 with the intention of informing the user that their Elasticsearch settings need to be updated.See [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) See [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | | [SavedObjectsClientFactory](./kibana-plugin-server.savedobjectsclientfactory.md) | Describes the factory used to create instances of the Saved Objects Client. | | [SavedObjectsClientWrapperFactory](./kibana-plugin-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. | +| [ScopeableRequest](./kibana-plugin-server.scopeablerequest.md) | A user credentials container. It accommodates the necessary auth credentials to impersonate the current user.See [KibanaRequest](./kibana-plugin-server.kibanarequest.md). | | [SharedGlobalConfig](./kibana-plugin-server.sharedglobalconfig.md) | | | [UiSettingsType](./kibana-plugin-server.uisettingstype.md) | UI element type to represent the settings. | diff --git a/docs/development/core/server/kibana-plugin-server.scopeablerequest.md b/docs/development/core/server/kibana-plugin-server.scopeablerequest.md new file mode 100644 index 00000000000000..5a9443376996d1 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.scopeablerequest.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ScopeableRequest](./kibana-plugin-server.scopeablerequest.md) + +## ScopeableRequest type + +A user credentials container. It accommodates the necessary auth credentials to impersonate the current user. + +See [KibanaRequest](./kibana-plugin-server.kibanarequest.md). + +Signature: + +```typescript +export declare type ScopeableRequest = KibanaRequest | LegacyRequest | FakeRequest; +``` diff --git a/docs/limitations.asciidoc b/docs/limitations.asciidoc index 9bcba3b65d6605..818cc766bf6a93 100644 --- a/docs/limitations.asciidoc +++ b/docs/limitations.asciidoc @@ -19,4 +19,4 @@ These {stack} features also have limitations that affect {kib}: include::limitations/nested-objects.asciidoc[] -include::limitations/export-data.asciidoc[] +include::limitations/export-data.asciidoc[] \ No newline at end of file diff --git a/docs/management/index-patterns.asciidoc b/docs/management/index-patterns.asciidoc index 8d9ef515108eda..8e687f641c92be 100644 --- a/docs/management/index-patterns.asciidoc +++ b/docs/management/index-patterns.asciidoc @@ -1,17 +1,22 @@ [[index-patterns]] -== Index patterns +== Creating an index pattern -To visualize and explore data in {kib}, you must create an index pattern. -An index pattern tells {kib} which {es} indices contain the data that you want to work with. -An index pattern can match a single index, multiple indices, and a rollup index. +To explore and visualize data in {kib}, you must create an index pattern. +An index pattern tells {kib} which {es} indices contain the data that +you want to work with. +Once you create an index pattern, you're ready to: + +* Interactively explore your data in <>. +* Analyze your data in charts, tables, gauges, tag clouds, and more in <>. +* Show off your data in a <> workpad. +* If your data includes geo data, visualize it with <>. [float] [[index-patterns-read-only-access]] === [xpack]#Read-only access# -If you have insufficient privileges to create or save index patterns, a read-only +If you have insufficient privileges to create or save index patterns, a read-only indicator appears in Kibana. The buttons to create new index patterns or save -existing index patterns are not visible. For more information on granting access to -Kibana see <>. +existing index patterns are not visible. For more information, see <>. [role="screenshot"] image::images/management-index-read-only-badge.png[Example of Index Pattern Management's read only access indicator in Kibana's header] @@ -20,12 +25,9 @@ image::images/management-index-read-only-badge.png[Example of Index Pattern Mana [[settings-create-pattern]] === Create an index pattern -To get started, go to *Management > Kibana > Index Patterns*. You begin with -an overview of your index patterns, including any that were added when you -downloaded sample data sets. - -You can create a standard index pattern, and if a rollup index is detected in the -cluster, a rollup index pattern. +If you are in an app that requires an index pattern, and you don't have one yet, +{kib} prompts you to create one. Or, you can go directly to +*Management > Kibana > Index Patterns*. [role="screenshot"] image:management/index-patterns/images/rollup-index-pattern.png["Menu with rollup index pattern"] @@ -33,83 +35,93 @@ image:management/index-patterns/images/rollup-index-pattern.png["Menu with rollu [float] ==== Standard index pattern -{kib} makes it easy for you to create an index pattern by walking you through -the process. Just start typing in the *Index pattern* field, and {kib} looks for -the names of {es} indices that match your input. Make sure that the name of the +Just start typing in the *Index pattern* field, and {kib} looks for +the names of {es} indices that match your input. Make sure that the name of the index pattern is unique. - -If you want to include system indices in your search, toggle the switch in the -upper right. +To include system indices in your search, toggle the switch in the upper right. [role="screenshot"] image:management/index-patterns/images/create-index-pattern.png["Create index pattern"] -Your index pattern can match multiple {es} indices. -Use a comma to separate the names, with no space after the comma. The notation for -wildcards (`*`) and the ability to "exclude" (`-`) also apply +Your index pattern can match multiple {es} indices. +Use a comma to separate the names, with no space after the comma. The notation for +wildcards (`*`) and the ability to "exclude" (`-`) also apply (for example, `test*,-test3`). -When {kib} detects an index with a timestamp, you’re asked to choose a field to -filter your data by time. If you don’t specify a field, you won’t be able +If {kib} detects an index with a timestamp, you’re asked to choose a field to +filter your data by time. If you don’t specify a field, you won’t be able to use the time filter. -Once you’ve created your index pattern, you can start working with -your {es} data in {kib}. Here are some things to try: -* Interactively explore your data in <>. -* Present your data in charts, tables, gauges, tag clouds, and more in <>. -* Show off your data in a <> presentation. -* If your data includes geo data, visualize it using <>. - -For a walkthrough of creating an index pattern and visualizing the data, -see <>. [float] ==== Rollup index pattern -If a rollup index is detected in the cluster, clicking *Create index pattern* -includes an item for creating a rollup index pattern. You create an -index pattern for rolled up data the same way you do for any data. +If a rollup index is detected in the cluster, clicking *Create index pattern* +includes an item for creating a rollup index pattern. +You can match an index pattern to only rolled up data, or mix both rolled +up and raw data to explore and visualize all data together. +An index pattern can match +only one rollup index. + +[float] +[[management-cross-cluster-search]] +==== {ccs-cap} index pattern + +If your {es} clusters are configured for {ref}/modules-cross-cluster-search.html[{ccs}], you can create +index patterns to search across the clusters of your choosing. Using the +same syntax that you'd use in a raw {ccs} request in {es}, create your +index pattern with the convention `:`. + +For example, to query {ls} indices across two {es} clusters +that you set up for {ccs}, which are named `cluster_one` and `cluster_two`, +you would use `cluster_one:logstash-*,cluster_two:logstash-*` as your index pattern. + +You can use wildcards in your cluster names +to match any number of clusters, so if you want to search {ls} indices across +clusters named `cluster_foo`, `cluster_bar`, and so on, you would use `cluster_*:logstash-*` +as your index pattern. -You can match an index pattern to only rolled up data, or mix both rolled -up and raw data to visualize all data together. An index pattern can match -only one rollup index, not multiple. There is no restriction on the -number of standard indices that an index pattern can match. +To query across all {es} clusters that have been configured for {ccs}, +use a standalone wildcard for your cluster name in your index +pattern: `*:logstash-*`. -See <> -for more detailed information. +Once an index pattern is configured using the {ccs} syntax, all searches and +aggregations using that index pattern in {kib} take advantage of {ccs}. [float] === Manage your index pattern -Once you’ve created an index pattern, you’re presented a table of all fields -and associated data types in the index. +Once you create an index pattern, manually or with a sample data set, +you can look at its fields and associated data types. +You can also perform housekeeping tasks, such as making the +index pattern the default or deleting it when you longer need it. +To drill down into the details of an index pattern, click its name in +the *Index patterns* overview. [role="screenshot"] image:management/index-patterns/images/new-index-pattern.png["Index files and data types"] -You can perform the following actions: +From the detailed view, you can perform the following actions: -* *Manage the index fields.* Click a column header to sort the table by that column. -Use the field dropdown menu to limit to display to a specific field. -See <> for more detailed information. +* *Manage the index fields.* You can add formatters to format values and create +scripted fields. +See <> for more information. -* [[set-default-pattern]]*Set the default index pattern.* {kib} uses a badge to make users -aware of which index pattern is the default. The first pattern -you create is automatically designated as the default pattern. The default -index pattern is loaded when you view the Discover tab. +* [[set-default-pattern]]*Set the default index pattern.* {kib} uses a badge to make users +aware of which index pattern is the default. The first pattern +you create is automatically designated as the default pattern. The default +index pattern is loaded when you open *Discover*. -* [[reload-fields]]*Reload the index fields list.* You can reload the index fields list to -pick up any newly-added fields. Doing so also resets Kibana’s popularity counters -for the fields. The popularity counters keep track of the fields -you’ve used most often in {kib} and are used to sort fields in lists. +* [[reload-fields]]*Refresh the index fields list.* You can refresh the index fields list to +pick up any newly-added fields. Doing so also resets Kibana’s popularity counters +for the fields. The popularity counters are used in *Discover* to sort fields in lists. -* [[delete-pattern]]*Delete the index pattern.* This action removes the pattern from the list of -Saved Objects in {kib}. You will not be able to recover field formatters, +* [[delete-pattern]]*Delete the index pattern.* This action removes the pattern from the list of +Saved Objects in {kib}. You will not be able to recover field formatters, scripted fields, source filters, and field popularity data associated with the index pattern. -+ -Deleting an index pattern breaks all visualizations, saved searches, and -other saved objects that reference the pattern. Deleting an index pattern does +Deleting an index pattern does not remove any indices or data documents from {es}. - -include::index-patterns/management-cross-cluster-search.asciidoc[] ++ +WARNING: Deleting an index pattern breaks all visualizations, saved searches, and +other saved objects that reference the pattern. diff --git a/docs/management/index-patterns/management-cross-cluster-search.asciidoc b/docs/management/index-patterns/management-cross-cluster-search.asciidoc deleted file mode 100644 index 9fd8deb7f34be7..00000000000000 --- a/docs/management/index-patterns/management-cross-cluster-search.asciidoc +++ /dev/null @@ -1,30 +0,0 @@ -[[management-cross-cluster-search]] -=== {ccs-cap} - -{es} supports the ability to run search and aggregation requests across multiple -clusters using a module called _{ccs}_. - -In order to take advantage of {ccs}, you must configure your {es} -clusters accordingly. Review the corresponding {es} -{ref}/modules-cross-cluster-search.html[documentation] before attempting to use {ccs} in {kib}. - -Once your {es} clusters are configured for {ccs}, you can create -specific index patterns in {kib} to search across the clusters of your choosing. Using the -same syntax that you'd use in a raw {ccs} request in {es}, create your -index pattern in {kib} with the convention `:`. - -For example, if you want to query {ls} indices across two of the {es} clusters -that you set up for {ccs}, which were named `cluster_one` and `cluster_two`, -you would use `cluster_one:logstash-*,cluster_two:logstash-*` as your index pattern in {kib}. - -Just like in raw search requests in {es}, you can use wildcards in your cluster names -to match any number of clusters, so if you wanted to search {ls} indices across any -clusters named `cluster_foo`, `cluster_bar`, and so on, you would use `cluster_*:logstash-*` -as your index pattern in {kib}. - -If you want to query across all {es} clusters that have been configured for {ccs}, -then use a standalone wildcard for your cluster name in your {kib} index -pattern: `*:logstash-*`. - -Once an index pattern is configured using the {ccs} syntax, all searches and -aggregations using that index pattern in {kib} take advantage of {ccs}. diff --git a/docs/settings/monitoring-settings.asciidoc b/docs/settings/monitoring-settings.asciidoc index 2fc74d2ffee32b..38a46a3cde5a0b 100644 --- a/docs/settings/monitoring-settings.asciidoc +++ b/docs/settings/monitoring-settings.asciidoc @@ -66,13 +66,6 @@ both the {es} monitoring cluster and the {es} production cluster. If not set, {kib} uses the value of the `elasticsearch.password` setting. -`telemetry.enabled`:: -Set to `true` (default) to send cluster statistics to Elastic. Reporting your -cluster statistics helps us improve your user experience. Your data is never -shared with anyone. Set to `false` to disable statistics reporting from any -browser connected to the {kib} instance. You can also opt out through the -*Advanced Settings* in {kib}. - `xpack.monitoring.elasticsearch.pingTimeout`:: Specifies the time in milliseconds to wait for {es} to respond to internal health checks. By default, it matches the `elasticsearch.pingTimeout` setting, diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 01e6bd51ea50b7..c98df6ca78356b 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -380,6 +380,11 @@ cannot be `false` at the same time. To enable telemetry and prevent users from disabling it, set `telemetry.allowChangingOptInStatus` to `false` and `telemetry.optIn` to `true`. +`telemetry.enabled`:: *Default: true* Reporting your cluster statistics helps +us improve your user experience. Your data is never shared with anyone. Set to +`false` to disable telemetry capabilities entirely. You can alternatively opt +out through the *Advanced Settings* in {kib}. + `vega.enableExternalUrls:`:: *Default: false* Set this value to true to allow Vega to use any URL to access external data sources and images. If false, Vega can only get data from Elasticsearch. `xpack.license_management.enabled`:: *Default: true* Set this value to false to diff --git a/docs/spaces/images/spaces-configure-landing-page.png b/docs/spaces/images/spaces-configure-landing-page.png new file mode 100644 index 00000000000000..15006594b6d7bc Binary files /dev/null and b/docs/spaces/images/spaces-configure-landing-page.png differ diff --git a/docs/spaces/index.asciidoc b/docs/spaces/index.asciidoc index 69655aac521e7a..fb5ef670692dc8 100644 --- a/docs/spaces/index.asciidoc +++ b/docs/spaces/index.asciidoc @@ -2,13 +2,13 @@ [[xpack-spaces]] == Spaces -Spaces enable you to organize your dashboards and other saved -objects into meaningful categories. Once inside a space, you see only -the dashboards and saved objects that belong to that space. +Spaces enable you to organize your dashboards and other saved +objects into meaningful categories. Once inside a space, you see only +the dashboards and saved objects that belong to that space. -{kib} creates a default space for you. -After you create your own -spaces, you're asked to choose a space when you log in to Kibana. You can change your +{kib} creates a default space for you. +After you create your own +spaces, you're asked to choose a space when you log in to Kibana. You can change your current space at any time by using the menu in the upper left. [role="screenshot"] @@ -29,24 +29,24 @@ Kibana supports spaces in several ways. You can: [[spaces-managing]] === View, create, and delete spaces -Go to **Management > Spaces** for an overview of your spaces. This view provides actions +Go to **Management > Spaces** for an overview of your spaces. This view provides actions for you to create, edit, and delete spaces. [role="screenshot"] image::spaces/images/space-management.png["Space management"] [float] -==== Create or edit a space +==== Create or edit a space -You can create as many spaces as you like. Click *Create a space* and provide a name, -URL identifier, optional description. +You can create as many spaces as you like. Click *Create a space* and provide a name, +URL identifier, optional description. -The URL identifier is a short text string that becomes part of the -{kib} URL when you are inside that space. {kib} suggests a URL identifier based +The URL identifier is a short text string that becomes part of the +{kib} URL when you are inside that space. {kib} suggests a URL identifier based on the name of your space, but you can customize the identifier to your liking. You cannot change the space identifier once you create the space. -{kib} also has an <> +{kib} also has an <> if you prefer to create spaces programatically. [role="screenshot"] @@ -55,7 +55,7 @@ image::spaces/images/edit-space.png["Space management"] [float] ==== Delete a space -Deleting a space permanently removes the space and all of its contents. +Deleting a space permanently removes the space and all of its contents. Find the space on the *Spaces* overview page and click the trash icon in the Actions column. You can't delete the default space, but you can customize it to your liking. @@ -63,14 +63,14 @@ You can't delete the default space, but you can customize it to your liking. [[spaces-control-feature-visibility]] === Control feature access based on user needs -You have control over which features are visible in each space. -For example, you might hide Dev Tools +You have control over which features are visible in each space. +For example, you might hide Dev Tools in your "Executive" space or show Stack Monitoring only in your "Admin" space. You can define which features to show or hide when you add or edit a space. -Controlling feature -visibility is not a security feature. To secure access -to specific features on a per-user basis, you must configure +Controlling feature +visibility is not a security feature. To secure access +to specific features on a per-user basis, you must configure <>. [role="screenshot"] @@ -80,10 +80,10 @@ image::spaces/images/edit-space-feature-visibility.png["Controlling features vis [[spaces-control-user-access]] === Control feature access based on user privileges -When using Kibana with security, you can configure applications and features -based on your users’ privileges. This means different roles can have access -to different features in the same space. -Power users might have privileges to create and edit visualizations and dashboards, +When using Kibana with security, you can configure applications and features +based on your users’ privileges. This means different roles can have access +to different features in the same space. +Power users might have privileges to create and edit visualizations and dashboards, while analysts or executives might have Dashboard and Canvas with read-only privileges. See <> for details. @@ -106,7 +106,7 @@ interface. . Import your saved objects. . (Optional) Delete objects in the export space that you no longer need. -{kib} also has beta <> and +{kib} also has beta <> and <> APIs if you want to automate this process. [float] @@ -115,17 +115,22 @@ interface. You can create a custom experience for users by configuring the {kib} landing page on a per-space basis. The landing page can route users to a specific dashboard, application, or saved object as they enter each space. -To configure the landing page, use the `defaultRoute` setting in < Advanced settings>>. + +To configure the landing page, use the default route setting in < Advanced settings>>. +For example, you might set the default route to `/app/kibana#/dashboards`. + +[role="screenshot"] +image::spaces/images/spaces-configure-landing-page.png["Configure space-level landing page"] + [float] [[spaces-delete-started]] === Disable and version updates -Spaces are automatically enabled in {kib}. If you don't want use this feature, +Spaces are automatically enabled in {kib}. If you don't want use this feature, you can disable it -by setting `xpack.spaces.enabled` to `false` in your +by setting `xpack.spaces.enabled` to `false` in your `kibana.yml` configuration file. -If you are upgrading your -version of {kib}, the default space will contain all of your existing saved objects. - +If you are upgrading your +version of {kib}, the default space will contain all of your existing saved objects. diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc index 36d6b0a6e473a9..7de7d73bf16642 100644 --- a/docs/user/discover.asciidoc +++ b/docs/user/discover.asciidoc @@ -3,6 +3,7 @@ [partintro] -- + When you know what your data includes, you can create visualizations that best display that data and build better dashboards. *Discover* enables you to explore your data, find @@ -99,6 +100,8 @@ or create a direct link to share. The *Save* and *Share* actions are in the men -- +include::{kib-repo-dir}/management/index-patterns.asciidoc[] + include::{kib-repo-dir}/discover/set-time-filter.asciidoc[] include::{kib-repo-dir}/discover/search.asciidoc[] diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc index d1acb915f1973f..2c41d0072fe5b9 100644 --- a/docs/user/management.asciidoc +++ b/docs/user/management.asciidoc @@ -13,8 +13,6 @@ visualizations, and dashboards. include::{kib-repo-dir}/management/managing-licenses.asciidoc[] -include::{kib-repo-dir}/management/index-patterns.asciidoc[] - include::{kib-repo-dir}/management/rollups/create_and_manage_rollups.asciidoc[] include::{kib-repo-dir}/management/index-lifecycle-policies/intro-to-lifecycle-policies.asciidoc[] @@ -40,5 +38,3 @@ include::{kib-repo-dir}/management/managing-beats.asciidoc[] include::{kib-repo-dir}/management/managing-remote-clusters.asciidoc[] include::{kib-repo-dir}/management/snapshot-restore/index.asciidoc[] - - diff --git a/docs/user/reporting/images/shareable-container.png b/docs/user/reporting/images/shareable-container.png new file mode 100644 index 00000000000000..db5a41dcff471c Binary files /dev/null and b/docs/user/reporting/images/shareable-container.png differ diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 06af9e60384455..fde88130a26b44 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -6,11 +6,11 @@ -- -You can generate a report that contains a {kib} dashboard, visualization, -saved search, or Canvas workpad. Depending on the object type, you can export the data as +You can generate a report that contains a {kib} dashboard, visualization, +saved search, or Canvas workpad. Depending on the object type, you can export the data as a PDF, PNG, or CSV document, which you can keep for yourself, or share with others. -Reporting is available from the *Share* menu +Reporting is available from the *Share* menu in *Discover*, *Visualize*, *Dashboard*, and *Canvas*. [role="screenshot"] @@ -40,9 +40,9 @@ for an example. [[manually-generate-reports]] == Generate a report manually -. Open the dashboard, visualization, Canvas workpad, or saved search that you want to include in the report. +. Open the dashboard, visualization, Canvas workpad, or saved search that you want to include in the report. -. In the {kib} toolbar, click *Share*. If you are working in Canvas, +. In the {kib} toolbar, click *Share*. If you are working in Canvas, click the share icon image:user/reporting/images/canvas-share-button.png["Canvas Share button"]. . Select the option appropriate for your object. You can export: @@ -55,14 +55,36 @@ click the share icon image:user/reporting/images/canvas-share-button.png["Canvas + A notification appears when the report is complete. +[float] +[[reporting-layout-sizing]] +== Layout and sizing +The layout and size of the PDF or PNG image depends on the {kib} app +with which the Reporting plugin is integrated. For Canvas, the +worksheet dimensions determine the size for Reporting. In other apps, +the dimensions are taken on the fly by looking at +the size of the visualization elements or panels on the page. + +The size dimensions are part of the reporting job parameters. Therefore, to +make the report output larger or smaller, you can change the size of the browser. +This resizes the shareable container before generating the +report, so the desired dimensions are passed in the job parameters. + +In the following {kib} dashboard, the shareable container is highlighted. +The shareable container is captured when you click the +*Generate* or *Copy POST URL* button. It might take some trial and error +before you're satisfied with the layout and dimensions in the resulting +PNG or PDF image. + +[role="screenshot"] +image::user/reporting/images/shareable-container.png["Shareable Container"] + + + [float] [[optimize-pdf]] == Optimize PDF for print—dashboard only -By default, {kib} creates a PDF -using the existing layout and size of the dashboard. To create a -printer-friendly PDF with multiple A4 portrait pages and two visualizations -per page, turn on *Optimize for printing*. +To create a printer-friendly PDF with multiple A4 portrait pages and two visualizations per page, turn on *Optimize for printing*. [role="screenshot"] image::user/reporting/images/preserve-layout-switch.png["Share"] @@ -72,8 +94,8 @@ image::user/reporting/images/preserve-layout-switch.png["Share"] [[manage-report-history]] == View and manage report history -For a list of your reports, go to *Management > Reporting*. -From this view, you can monitor the generation of a report and +For a list of your reports, go to *Management > Reporting*. +From this view, you can monitor the generation of a report and download reports that you previously generated. [float] diff --git a/docs/user/reporting/reporting-troubleshooting.asciidoc b/docs/user/reporting/reporting-troubleshooting.asciidoc index ca7fa6abcc9d9c..dc4ffdfebdae9b 100644 --- a/docs/user/reporting/reporting-troubleshooting.asciidoc +++ b/docs/user/reporting/reporting-troubleshooting.asciidoc @@ -7,12 +7,20 @@ Having trouble? Here are solutions to common problems you might encounter while using Reporting. +* <> +* <> +* <> +* <> +* <> +* <> +* <> + [float] [[reporting-troubleshooting-system-dependencies]] === System dependencies Reporting launches a "headless" web browser called Chromium on the Kibana server. It is a custom build made by Elastic of an open source project, and it is intended to have minimal dependencies on OS libraries. However, the Kibana server OS might still require additional -dependencies for Chromium. +dependencies to run the Chromium executable. Make sure Kibana server OS has the appropriate packages installed for the distribution. @@ -33,19 +41,30 @@ If you are using Ubuntu/Debian systems, install the following packages: * `fonts-liberation` * `libfontconfig1` +If the system is missing dependencies, then Reporting will fail in a non-deterministic way. {kib} runs a self-test at server startup, and +if it encounters errors, logs them in the Console. Unfortunately, the error message does not include +information about why Chromium failed to run. The most common error message is `Error: connect ECONNREFUSED`, which indicates +that {kib} could not connect to the Chromium process. + +To troubleshoot the problem, start the {kib} server with environment variables that tell Chromium to print verbose logs. See the +<> for more information. + [float] -=== Text is rendered incorrectly in generated reports +[[reporting-troubleshooting-text-incorrect]] +=== Text rendered incorrectly in generated reports If a report label is rendered as an empty rectangle, no system fonts are available. Install at least one font package on the system. If the report is missing certain Chinese, Japanese or Korean characters, ensure that a system font with those characters is installed. [float] +[[reporting-troubleshooting-missing-data]] === Missing data in PDF report of data table visualization There is currently a known limitation with the Data Table visualization that only the first page of data rows, which are the only data visible on the screen, are shown in PDF reports. [float] +[[reporting-troubleshooting-file-permissions]] === File permissions Ensure that the `headless_shell` binary located in your Kibana data directory is owned by the user who is running Kibana, that the user has the execute permission, and if applicable, that the filesystem is mounted with the `exec` option. @@ -63,25 +82,25 @@ Whenever possible, a Reporting error message tries to be as self-explanatory as along with the solution. [float] -==== "Max attempts reached" +==== Max attempts reached There are two primary causes of this error: -. You're creating a PDF of a visualization or dashboard that spans a large amount of data and Kibana is hitting the `xpack.reporting.queue.timeout` +* You're creating a PDF of a visualization or dashboard that spans a large amount of data and Kibana is hitting the `xpack.reporting.queue.timeout` -. Kibana is hosted behind a reverse-proxy, and the <> are not configured correctly +* Kibana is hosted behind a reverse-proxy, and the <> are not configured correctly Create a Markdown visualization and then create a PDF report. If this succeeds, increase the `xpack.reporting.queue.timeout` setting. If the PDF report fails with "Max attempts reached," check your <>. [float] [[reporting-troubleshooting-nss-dependency]] -==== "You must install nss for Reporting to work" +==== You must install nss for Reporting to work Reporting using the Chromium browser relies on the Network Security Service libraries (NSS). Install the appropriate nss package for your distribution. [float] [[reporting-troubleshooting-sandbox-dependency]] -==== "Unable to use Chromium sandbox" +==== Unable to use Chromium sandbox Chromium uses sandboxing techniques that are built on top of operating system primitives. The Linux sandbox depends on user namespaces, which were introduced with the 3.8 Linux kernel. However, many distributions don't have user namespaces enabled by default, or they require the CAP_SYS_ADMIN capability. @@ -90,6 +109,7 @@ Elastic recommends that you research the feasibility of enabling unprivileged us is if you are running Kibana in Docker because the container runs in a user namespace with the built-in seccomp/bpf filters. [float] +[[reporting-troubleshooting-verbose-logs]] === Verbose logs {kib} server logs have a lot of useful information for troubleshooting and understanding how things work. If you're having any issues at all, the full logs from Reporting will be the first place to look. In `kibana.yml`: @@ -101,10 +121,12 @@ logging.verbose: true For more information about logging, see <>. +[float] +[[reporting-troubleshooting-puppeteer-debug-logs]] === Puppeteer debug logs The Chromium browser that {kib} launches on the server is driven by a NodeJS library for Chromium called Puppeteer. The Puppeteer library has its own command-line method to generate its own debug logs, which can sometimes be helpful, particularly to figure out if a problem is -caused by Kibana or Chromium. See more at https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/README.md#debugging-tips +caused by Kibana or Chromium. See more at https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/README.md#debugging-tips[debugging tips]. Using Puppeteer's debug method when launching Kibana would look like: ``` @@ -114,3 +136,14 @@ The internal DevTools protocol traffic will be logged via the `debug` module und The Puppeteer logs are very verbose and could possibly contain sensitive information. Handle the generated output with care. + +[float] +[[reporting-troubleshooting-system-requirements]] +=== System requirements +In Elastic Cloud, the {kib} instances that most configurations provide by default is for 1GB of RAM for the instance. That is enough for +{kib} Reporting when the visualization or dashboard is relatively simple, such as a single pie chart or a dashboard with +a few visualizations. However, certain visualization types incur more load than others. For example, a TSVB panel has a lot of network +requests to render. + +If the {kib} instance doesn't have enough memory to run the report, the report fails with an error such as `Error: Page crashed!` +In this case, try increasing the memory for the {kib} instance to 2GB. diff --git a/docs/user/security/reporting.asciidoc b/docs/user/security/reporting.asciidoc index 86599be9af3754..c2ed295e83ce95 100644 --- a/docs/user/security/reporting.asciidoc +++ b/docs/user/security/reporting.asciidoc @@ -57,6 +57,30 @@ that provides read and write privileges in Go to *Management > Users*, add a new user, and assign the user the built-in `reporting_user` role and your new custom role, `custom_reporting_user`. +[float] +==== With a custom index + +If you are using Reporting with a custom index, +the `xpack.reporting.index` setting should begin +with `.reporting-*`. The default {kib} system user has +`all` privileges against the `.reporting-*` pattern of indices. + +[source,js] +xpack.reporting.index: '.reporting-custom-index' + +If you use a different pattern for the `xpack.reporting.index` setting, +you must create a custom role with appropriate access to the index, similar +to the following: + +. Go to *Management > Roles*, and click *Create role*. +. Name the role `custom-reporting-user`. +. Specify the custom index and assign it the `all` index privilege. +. Go to *Management > Users* and create a new user with +the `kibana_system` role and the `custom-reporting-user` role. +. Configure {kib} to use the new account: +[source,js] +elasticsearch.username: 'custom_kibana_system' + [float] [[reporting-roles-user-api]] ==== With the user API diff --git a/examples/demo_search/server/plugin.ts b/examples/demo_search/server/plugin.ts index 23c82225563c89..653aa217717fa2 100644 --- a/examples/demo_search/server/plugin.ts +++ b/examples/demo_search/server/plugin.ts @@ -18,7 +18,7 @@ */ import { Plugin, CoreSetup, PluginInitializerContext } from 'kibana/server'; -import { DataPluginSetup } from 'src/plugins/data/server/plugin'; +import { PluginSetup as DataPluginSetup } from 'src/plugins/data/server'; import { demoSearchStrategyProvider } from './demo_search_strategy'; import { DEMO_SEARCH_STRATEGY, IDemoRequest, IDemoResponse } from '../common'; diff --git a/package.json b/package.json index 99151f33962c44..db5764e6e91bac 100644 --- a/package.json +++ b/package.json @@ -240,7 +240,7 @@ "react-use": "^13.13.0", "reactcss": "1.2.3", "redux": "4.0.0", - "redux-actions": "2.2.1", + "redux-actions": "2.6.5", "redux-thunk": "2.3.0", "regenerator-runtime": "^0.13.3", "regression": "2.0.1", @@ -314,7 +314,7 @@ "@types/delete-empty": "^2.0.0", "@types/elasticsearch": "^5.0.33", "@types/enzyme": "^3.9.0", - "@types/eslint": "^6.1.2", + "@types/eslint": "^6.1.3", "@types/fetch-mock": "^7.3.1", "@types/getopts": "^2.0.1", "@types/glob": "^7.1.1", @@ -353,7 +353,7 @@ "@types/react-router-dom": "^5.1.3", "@types/react-virtualized": "^9.18.7", "@types/redux": "^3.6.31", - "@types/redux-actions": "^2.2.1", + "@types/redux-actions": "^2.6.1", "@types/request": "^2.48.2", "@types/selenium-webdriver": "^4.0.5", "@types/semver": "^5.5.0", @@ -368,8 +368,8 @@ "@types/uuid": "^3.4.4", "@types/vinyl-fs": "^2.4.11", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", + "@typescript-eslint/eslint-plugin": "^2.15.0", + "@typescript-eslint/parser": "^2.15.0", "angular-mocks": "^1.7.8", "archiver": "^3.1.1", "axe-core": "^3.3.2", @@ -389,21 +389,21 @@ "enzyme-adapter-react-16": "^1.15.1", "enzyme-adapter-utils": "^1.12.1", "enzyme-to-json": "^3.4.3", - "eslint": "^6.5.1", - "eslint-config-prettier": "^6.4.0", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.9.0", "eslint-plugin-babel": "^5.3.0", - "eslint-plugin-ban": "^1.3.0", - "eslint-plugin-cypress": "^2.7.0", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jest": "^22.19.0", + "eslint-plugin-ban": "^1.4.0", + "eslint-plugin-cypress": "^2.8.1", + "eslint-plugin-import": "^2.19.1", + "eslint-plugin-jest": "^23.3.0", "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-mocha": "^6.2.0", + "eslint-plugin-mocha": "^6.2.2", "eslint-plugin-no-unsanitized": "^3.0.2", - "eslint-plugin-node": "^10.0.0", + "eslint-plugin-node": "^11.0.0", "eslint-plugin-prefer-object-spread": "^1.2.1", - "eslint-plugin-prettier": "^3.1.1", - "eslint-plugin-react": "^7.16.0", - "eslint-plugin-react-hooks": "^2.1.2", + "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-react": "^7.17.0", + "eslint-plugin-react-hooks": "^2.3.0", "exit-hook": "^2.2.0", "faker": "1.1.0", "fetch-mock": "^7.3.9", diff --git a/packages/eslint-config-kibana/package.json b/packages/eslint-config-kibana/package.json index 04602d196a7f36..34b1b0fec376f6 100644 --- a/packages/eslint-config-kibana/package.json +++ b/packages/eslint-config-kibana/package.json @@ -15,19 +15,19 @@ }, "homepage": "https://github.com/elastic/eslint-config-kibana#readme", "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^2.12.0", - "@typescript-eslint/parser": "^2.12.0", + "@typescript-eslint/eslint-plugin": "^2.15.0", + "@typescript-eslint/parser": "^2.15.0", "babel-eslint": "^10.0.3", - "eslint": "^6.5.1", + "eslint": "^6.8.0", "eslint-plugin-babel": "^5.3.0", - "eslint-plugin-ban": "^1.3.0", + "eslint-plugin-ban": "^1.4.0", "eslint-plugin-jsx-a11y": "^6.2.3", - "eslint-plugin-import": "^2.18.2", - "eslint-plugin-jest": "^22.19.0", - "eslint-plugin-mocha": "^6.2.0", + "eslint-plugin-import": "^2.19.1", + "eslint-plugin-jest": "^23.3.0", + "eslint-plugin-mocha": "^6.2.2", "eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-prefer-object-spread": "^1.2.1", - "eslint-plugin-react": "^7.16.0", - "eslint-plugin-react-hooks": "^2.1.2" + "eslint-plugin-react": "^7.17.0", + "eslint-plugin-react-hooks": "^2.3.0" } } diff --git a/packages/kbn-analytics/src/report.ts b/packages/kbn-analytics/src/report.ts index 1c0b37966355fb..16c0a3069e5fdb 100644 --- a/packages/kbn-analytics/src/report.ts +++ b/packages/kbn-analytics/src/report.ts @@ -78,6 +78,7 @@ export class ReportManager { } assignReports(newMetrics: Metric | Metric[]) { wrapArray(newMetrics).forEach(newMetric => this.assignReport(this.report, newMetric)); + return { report: this.report }; } static createMetricKey(metric: Metric): string { switch (metric.type) { @@ -101,7 +102,7 @@ export class ReportManager { case METRIC_TYPE.USER_AGENT: { const { appName, type, userAgent } = metric; if (userAgent) { - this.report.userAgent = { + report.userAgent = { [key]: { key, appName, @@ -110,23 +111,22 @@ export class ReportManager { }, }; } + return; } case METRIC_TYPE.CLICK: case METRIC_TYPE.LOADED: case METRIC_TYPE.COUNT: { const { appName, type, eventName, count } = metric; - if (report.uiStatsMetrics) { - const existingStats = (report.uiStatsMetrics[key] || {}).stats; - this.report.uiStatsMetrics = this.report.uiStatsMetrics || {}; - this.report.uiStatsMetrics[key] = { - key, - appName, - eventName, - type, - stats: this.incrementStats(count, existingStats), - }; - } + report.uiStatsMetrics = report.uiStatsMetrics || {}; + const existingStats = (report.uiStatsMetrics[key] || {}).stats; + report.uiStatsMetrics[key] = { + key, + appName, + eventName, + type, + stats: this.incrementStats(count, existingStats), + }; return; } default: diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index fd62f1b3c03b22..e6f3e60128983d 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -156,6 +156,9 @@ __Usage:__ const valueSchema = schema.boolean({ defaultValue: false }); ``` +__Notes:__ +* The `schema.boolean()` also supports a string as input if it equals `'true'` or `'false'` (case-insensitive). + #### `schema.literal()` Validates input data as a [string](https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types), [numeric](https://www.typescriptlang.org/docs/handbook/advanced-types.html#numeric-literal-types) or boolean literal. @@ -397,7 +400,7 @@ const valueSchema = schema.byteSize({ min: '3kb' }); ``` __Notes:__ -* The string value for `schema.byteSize()` and its options supports the following prefixes: `b`, `kb`, `mb`, `gb` and `tb`. +* The string value for `schema.byteSize()` and its options supports the following optional suffixes: `b`, `kb`, `mb`, `gb` and `tb`. The default suffix is `b`. * The number value is treated as a number of bytes and hence should be a positive integer, e.g. `100` is equal to `'100b'`. * Currently you cannot specify zero bytes with a string format and should use number `0` instead. @@ -417,7 +420,7 @@ const valueSchema = schema.duration({ defaultValue: '70ms' }); ``` __Notes:__ -* The string value for `schema.duration()` supports the following prefixes: `ms`, `s`, `m`, `h`, `d`, `w`, `M` and `Y`. +* The string value for `schema.duration()` supports the following optional suffixes: `ms`, `s`, `m`, `h`, `d`, `w`, `M` and `Y`. The default suffix is `ms`. * The number value is treated as a number of milliseconds and hence should be a positive integer, e.g. `100` is equal to `'100ms'`. #### `schema.conditional()` diff --git a/packages/kbn-config-schema/src/byte_size_value/__snapshots__/index.test.ts.snap b/packages/kbn-config-schema/src/byte_size_value/__snapshots__/index.test.ts.snap index 1db6930062a9a1..97e9082401b3da 100644 --- a/packages/kbn-config-schema/src/byte_size_value/__snapshots__/index.test.ts.snap +++ b/packages/kbn-config-schema/src/byte_size_value/__snapshots__/index.test.ts.snap @@ -1,9 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`#constructor throws if number of bytes is negative 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [-1024]"`; +exports[`#constructor throws if number of bytes is negative 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [-1024]."`; -exports[`#constructor throws if number of bytes is not safe 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [NaN]"`; +exports[`#constructor throws if number of bytes is not safe 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [NaN]."`; -exports[`#constructor throws if number of bytes is not safe 2`] = `"Value in bytes is expected to be a safe positive integer, but provided [Infinity]"`; +exports[`#constructor throws if number of bytes is not safe 2`] = `"Value in bytes is expected to be a safe positive integer, but provided [Infinity]."`; -exports[`#constructor throws if number of bytes is not safe 3`] = `"Value in bytes is expected to be a safe positive integer, but provided [9007199254740992]"`; +exports[`#constructor throws if number of bytes is not safe 3`] = `"Value in bytes is expected to be a safe positive integer, but provided [9007199254740992]."`; + +exports[`parsing units throws an error when unsupported unit specified 1`] = `"Failed to parse [1tb] as byte value. Value must be either number of bytes, or follow the format [b|kb|mb|gb] (e.g., '1024kb', '200mb', '1gb'), where the number is a safe positive integer."`; diff --git a/packages/kbn-config-schema/src/byte_size_value/index.test.ts b/packages/kbn-config-schema/src/byte_size_value/index.test.ts index 46ed96c83dd1f7..198d95aa0ab4cc 100644 --- a/packages/kbn-config-schema/src/byte_size_value/index.test.ts +++ b/packages/kbn-config-schema/src/byte_size_value/index.test.ts @@ -20,6 +20,10 @@ import { ByteSizeValue } from '.'; describe('parsing units', () => { + test('number string (bytes)', () => { + expect(ByteSizeValue.parse('123').getValueInBytes()).toBe(123); + }); + test('bytes', () => { expect(ByteSizeValue.parse('123b').getValueInBytes()).toBe(123); }); @@ -37,12 +41,8 @@ describe('parsing units', () => { expect(ByteSizeValue.parse('1gb').getValueInBytes()).toBe(1073741824); }); - test('throws an error when no unit specified', () => { - expect(() => ByteSizeValue.parse('123')).toThrowError('could not parse byte size value'); - }); - test('throws an error when unsupported unit specified', () => { - expect(() => ByteSizeValue.parse('1tb')).toThrowError('could not parse byte size value'); + expect(() => ByteSizeValue.parse('1tb')).toThrowErrorMatchingSnapshot(); }); }); diff --git a/packages/kbn-config-schema/src/byte_size_value/index.ts b/packages/kbn-config-schema/src/byte_size_value/index.ts index fb0105503a1494..48862821bb78d9 100644 --- a/packages/kbn-config-schema/src/byte_size_value/index.ts +++ b/packages/kbn-config-schema/src/byte_size_value/index.ts @@ -35,9 +35,14 @@ export class ByteSizeValue { public static parse(text: string): ByteSizeValue { const match = /([1-9][0-9]*)(b|kb|mb|gb)/.exec(text); if (!match) { - throw new Error( - `could not parse byte size value [${text}]. Value must be a safe positive integer.` - ); + const number = Number(text); + if (typeof number !== 'number' || isNaN(number)) { + throw new Error( + `Failed to parse [${text}] as byte value. Value must be either number of bytes, or follow the format [b|kb|mb|gb] ` + + `(e.g., '1024kb', '200mb', '1gb'), where the number is a safe positive integer.` + ); + } + return new ByteSizeValue(number); } const value = parseInt(match[1], 0); @@ -49,8 +54,7 @@ export class ByteSizeValue { constructor(private readonly valueInBytes: number) { if (!Number.isSafeInteger(valueInBytes) || valueInBytes < 0) { throw new Error( - `Value in bytes is expected to be a safe positive integer, ` + - `but provided [${valueInBytes}]` + `Value in bytes is expected to be a safe positive integer, but provided [${valueInBytes}].` ); } } diff --git a/packages/kbn-config-schema/src/duration/index.ts b/packages/kbn-config-schema/src/duration/index.ts index ff8f96614a193b..b96b5a3687bbb4 100644 --- a/packages/kbn-config-schema/src/duration/index.ts +++ b/packages/kbn-config-schema/src/duration/index.ts @@ -25,10 +25,14 @@ const timeFormatRegex = /^(0|[1-9][0-9]*)(ms|s|m|h|d|w|M|Y)$/; function stringToDuration(text: string) { const result = timeFormatRegex.exec(text); if (!result) { - throw new Error( - `Failed to parse [${text}] as time value. ` + - `Format must be [ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y')` - ); + const number = Number(text); + if (typeof number !== 'number' || isNaN(number)) { + throw new Error( + `Failed to parse [${text}] as time value. Value must be a duration in milliseconds, or follow the format ` + + `[ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer.` + ); + } + return numberToDuration(number); } const count = parseInt(result[1], 0); @@ -40,8 +44,7 @@ function stringToDuration(text: string) { function numberToDuration(numberMs: number) { if (!Number.isSafeInteger(numberMs) || numberMs < 0) { throw new Error( - `Failed to parse [${numberMs}] as time value. ` + - `Value should be a safe positive integer number.` + `Value in milliseconds is expected to be a safe positive integer, but provided [${numberMs}].` ); } diff --git a/packages/kbn-config-schema/src/internals/index.ts b/packages/kbn-config-schema/src/internals/index.ts index 4d5091eaa09b13..044c3050f9fa87 100644 --- a/packages/kbn-config-schema/src/internals/index.ts +++ b/packages/kbn-config-schema/src/internals/index.ts @@ -82,7 +82,23 @@ export const internals = Joi.extend([ base: Joi.boolean(), coerce(value: any, state: State, options: ValidationOptions) { // If value isn't defined, let Joi handle default value if it's defined. - if (value !== undefined && typeof value !== 'boolean') { + if (value === undefined) { + return value; + } + + // Allow strings 'true' and 'false' to be coerced to booleans (case-insensitive). + + // From Joi docs on `Joi.boolean`: + // > Generates a schema object that matches a boolean data type. Can also + // > be called via bool(). If the validation convert option is on + // > (enabled by default), a string (either "true" or "false") will be + // converted to a boolean if specified. + if (typeof value === 'string') { + const normalized = value.toLowerCase(); + value = normalized === 'true' ? true : normalized === 'false' ? false : value; + } + + if (typeof value !== 'boolean') { return this.createError('boolean.base', { value }, state, options); } diff --git a/packages/kbn-config-schema/src/types/__snapshots__/boolean_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/boolean_type.test.ts.snap index c3f33dc29bf50e..0e5f6de2deea8f 100644 --- a/packages/kbn-config-schema/src/types/__snapshots__/boolean_type.test.ts.snap +++ b/packages/kbn-config-schema/src/types/__snapshots__/boolean_type.test.ts.snap @@ -9,3 +9,7 @@ exports[`returns error when not boolean 1`] = `"expected value of type [boolean] exports[`returns error when not boolean 2`] = `"expected value of type [boolean] but got [Array]"`; exports[`returns error when not boolean 3`] = `"expected value of type [boolean] but got [string]"`; + +exports[`returns error when not boolean 4`] = `"expected value of type [boolean] but got [number]"`; + +exports[`returns error when not boolean 5`] = `"expected value of type [boolean] but got [string]"`; diff --git a/packages/kbn-config-schema/src/types/__snapshots__/byte_size_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/byte_size_type.test.ts.snap index f6f45a96ca1612..ea2102b1776fbf 100644 --- a/packages/kbn-config-schema/src/types/__snapshots__/byte_size_type.test.ts.snap +++ b/packages/kbn-config-schema/src/types/__snapshots__/byte_size_type.test.ts.snap @@ -18,6 +18,12 @@ ByteSizeValue { } `; +exports[`#defaultValue can be a string-formatted number 1`] = ` +ByteSizeValue { + "valueInBytes": 1024, +} +`; + exports[`#max returns error when larger 1`] = `"Value is [1mb] ([1048576b]) but it must be equal to or less than [1kb]"`; exports[`#max returns value when smaller 1`] = ` @@ -38,20 +44,18 @@ exports[`includes namespace in failure 1`] = `"[foo-namespace]: expected value o exports[`is required by default 1`] = `"expected value of type [ByteSize] but got [undefined]"`; -exports[`returns error when not string or positive safe integer 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [-123]"`; +exports[`returns error when not valid string or positive safe integer 1`] = `"Value in bytes is expected to be a safe positive integer, but provided [-123]."`; -exports[`returns error when not string or positive safe integer 2`] = `"Value in bytes is expected to be a safe positive integer, but provided [NaN]"`; +exports[`returns error when not valid string or positive safe integer 2`] = `"Value in bytes is expected to be a safe positive integer, but provided [NaN]."`; -exports[`returns error when not string or positive safe integer 3`] = `"Value in bytes is expected to be a safe positive integer, but provided [Infinity]"`; +exports[`returns error when not valid string or positive safe integer 3`] = `"Value in bytes is expected to be a safe positive integer, but provided [Infinity]."`; -exports[`returns error when not string or positive safe integer 4`] = `"Value in bytes is expected to be a safe positive integer, but provided [9007199254740992]"`; +exports[`returns error when not valid string or positive safe integer 4`] = `"Value in bytes is expected to be a safe positive integer, but provided [9007199254740992]."`; -exports[`returns error when not string or positive safe integer 5`] = `"expected value of type [ByteSize] but got [Array]"`; +exports[`returns error when not valid string or positive safe integer 5`] = `"expected value of type [ByteSize] but got [Array]"`; -exports[`returns error when not string or positive safe integer 6`] = `"expected value of type [ByteSize] but got [RegExp]"`; +exports[`returns error when not valid string or positive safe integer 6`] = `"expected value of type [ByteSize] but got [RegExp]"`; -exports[`returns value by default 1`] = ` -ByteSizeValue { - "valueInBytes": 123, -} -`; +exports[`returns error when not valid string or positive safe integer 7`] = `"Failed to parse [123foo] as byte value. Value must be either number of bytes, or follow the format [b|kb|mb|gb] (e.g., '1024kb', '200mb', '1gb'), where the number is a safe positive integer."`; + +exports[`returns error when not valid string or positive safe integer 8`] = `"Failed to parse [123 456] as byte value. Value must be either number of bytes, or follow the format [b|kb|mb|gb] (e.g., '1024kb', '200mb', '1gb'), where the number is a safe positive integer."`; diff --git a/packages/kbn-config-schema/src/types/__snapshots__/duration_type.test.ts.snap b/packages/kbn-config-schema/src/types/__snapshots__/duration_type.test.ts.snap index a21c28e7cc6142..c4e4ff652a2d7e 100644 --- a/packages/kbn-config-schema/src/types/__snapshots__/duration_type.test.ts.snap +++ b/packages/kbn-config-schema/src/types/__snapshots__/duration_type.test.ts.snap @@ -6,20 +6,24 @@ exports[`#defaultValue can be a number 1`] = `"PT0.6S"`; exports[`#defaultValue can be a string 1`] = `"PT1H"`; +exports[`#defaultValue can be a string-formatted number 1`] = `"PT0.6S"`; + exports[`includes namespace in failure 1`] = `"[foo-namespace]: expected value of type [moment.Duration] but got [undefined]"`; exports[`is required by default 1`] = `"expected value of type [moment.Duration] but got [undefined]"`; -exports[`returns error when not string or non-safe positive integer 1`] = `"Failed to parse [-123] as time value. Value should be a safe positive integer number."`; +exports[`returns error when not valid string or non-safe positive integer 1`] = `"Value in milliseconds is expected to be a safe positive integer, but provided [-123]."`; + +exports[`returns error when not valid string or non-safe positive integer 2`] = `"Value in milliseconds is expected to be a safe positive integer, but provided [NaN]."`; -exports[`returns error when not string or non-safe positive integer 2`] = `"Failed to parse [NaN] as time value. Value should be a safe positive integer number."`; +exports[`returns error when not valid string or non-safe positive integer 3`] = `"Value in milliseconds is expected to be a safe positive integer, but provided [Infinity]."`; -exports[`returns error when not string or non-safe positive integer 3`] = `"Failed to parse [Infinity] as time value. Value should be a safe positive integer number."`; +exports[`returns error when not valid string or non-safe positive integer 4`] = `"Value in milliseconds is expected to be a safe positive integer, but provided [9007199254740992]."`; -exports[`returns error when not string or non-safe positive integer 4`] = `"Failed to parse [9007199254740992] as time value. Value should be a safe positive integer number."`; +exports[`returns error when not valid string or non-safe positive integer 5`] = `"expected value of type [moment.Duration] but got [Array]"`; -exports[`returns error when not string or non-safe positive integer 5`] = `"expected value of type [moment.Duration] but got [Array]"`; +exports[`returns error when not valid string or non-safe positive integer 6`] = `"expected value of type [moment.Duration] but got [RegExp]"`; -exports[`returns error when not string or non-safe positive integer 6`] = `"expected value of type [moment.Duration] but got [RegExp]"`; +exports[`returns error when not valid string or non-safe positive integer 7`] = `"Failed to parse [123foo] as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer."`; -exports[`returns value by default 1`] = `"PT2M3S"`; +exports[`returns error when not valid string or non-safe positive integer 8`] = `"Failed to parse [123 456] as time value. Value must be a duration in milliseconds, or follow the format [ms|s|m|h|d|w|M|Y] (e.g. '70ms', '5s', '3d', '1Y'), where the duration is a safe positive integer."`; diff --git a/packages/kbn-config-schema/src/types/boolean_type.test.ts b/packages/kbn-config-schema/src/types/boolean_type.test.ts index d6e274f05e3ffa..e94999b5054377 100644 --- a/packages/kbn-config-schema/src/types/boolean_type.test.ts +++ b/packages/kbn-config-schema/src/types/boolean_type.test.ts @@ -23,6 +23,17 @@ test('returns value by default', () => { expect(schema.boolean().validate(true)).toBe(true); }); +test('handles boolean strings', () => { + expect(schema.boolean().validate('true')).toBe(true); + expect(schema.boolean().validate('TRUE')).toBe(true); + expect(schema.boolean().validate('True')).toBe(true); + expect(schema.boolean().validate('TrUe')).toBe(true); + expect(schema.boolean().validate('false')).toBe(false); + expect(schema.boolean().validate('FALSE')).toBe(false); + expect(schema.boolean().validate('False')).toBe(false); + expect(schema.boolean().validate('FaLse')).toBe(false); +}); + test('is required by default', () => { expect(() => schema.boolean().validate(undefined)).toThrowErrorMatchingSnapshot(); }); @@ -49,4 +60,8 @@ test('returns error when not boolean', () => { expect(() => schema.boolean().validate([1, 2, 3])).toThrowErrorMatchingSnapshot(); expect(() => schema.boolean().validate('abc')).toThrowErrorMatchingSnapshot(); + + expect(() => schema.boolean().validate(0)).toThrowErrorMatchingSnapshot(); + + expect(() => schema.boolean().validate('no')).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/kbn-config-schema/src/types/byte_size_type.test.ts b/packages/kbn-config-schema/src/types/byte_size_type.test.ts index 67eae1e7c382a6..7c65ec2945b493 100644 --- a/packages/kbn-config-schema/src/types/byte_size_type.test.ts +++ b/packages/kbn-config-schema/src/types/byte_size_type.test.ts @@ -23,7 +23,15 @@ import { ByteSizeValue } from '../byte_size_value'; const { byteSize } = schema; test('returns value by default', () => { - expect(byteSize().validate('123b')).toMatchSnapshot(); + expect(byteSize().validate('123b')).toEqual(new ByteSizeValue(123)); +}); + +test('handles numeric strings', () => { + expect(byteSize().validate('123')).toEqual(new ByteSizeValue(123)); +}); + +test('handles numbers', () => { + expect(byteSize().validate(123)).toEqual(new ByteSizeValue(123)); }); test('is required by default', () => { @@ -51,6 +59,14 @@ describe('#defaultValue', () => { ).toMatchSnapshot(); }); + test('can be a string-formatted number', () => { + expect( + byteSize({ + defaultValue: '1024', + }).validate(undefined) + ).toMatchSnapshot(); + }); + test('can be a number', () => { expect( byteSize({ @@ -88,7 +104,7 @@ describe('#max', () => { }); }); -test('returns error when not string or positive safe integer', () => { +test('returns error when not valid string or positive safe integer', () => { expect(() => byteSize().validate(-123)).toThrowErrorMatchingSnapshot(); expect(() => byteSize().validate(NaN)).toThrowErrorMatchingSnapshot(); @@ -100,4 +116,8 @@ test('returns error when not string or positive safe integer', () => { expect(() => byteSize().validate([1, 2, 3])).toThrowErrorMatchingSnapshot(); expect(() => byteSize().validate(/abc/)).toThrowErrorMatchingSnapshot(); + + expect(() => byteSize().validate('123foo')).toThrowErrorMatchingSnapshot(); + + expect(() => byteSize().validate('123 456')).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/kbn-config-schema/src/types/duration_type.test.ts b/packages/kbn-config-schema/src/types/duration_type.test.ts index 39655d43d7b75b..09e92ce727f2a2 100644 --- a/packages/kbn-config-schema/src/types/duration_type.test.ts +++ b/packages/kbn-config-schema/src/types/duration_type.test.ts @@ -23,7 +23,15 @@ import { schema } from '..'; const { duration, object, contextRef, siblingRef } = schema; test('returns value by default', () => { - expect(duration().validate('123s')).toMatchSnapshot(); + expect(duration().validate('123s')).toEqual(momentDuration(123000)); +}); + +test('handles numeric string', () => { + expect(duration().validate('123000')).toEqual(momentDuration(123000)); +}); + +test('handles number', () => { + expect(duration().validate(123000)).toEqual(momentDuration(123000)); }); test('is required by default', () => { @@ -51,6 +59,14 @@ describe('#defaultValue', () => { ).toMatchSnapshot(); }); + test('can be a string-formatted number', () => { + expect( + duration({ + defaultValue: '600', + }).validate(undefined) + ).toMatchSnapshot(); + }); + test('can be a number', () => { expect( duration({ @@ -124,7 +140,7 @@ Object { }); }); -test('returns error when not string or non-safe positive integer', () => { +test('returns error when not valid string or non-safe positive integer', () => { expect(() => duration().validate(-123)).toThrowErrorMatchingSnapshot(); expect(() => duration().validate(NaN)).toThrowErrorMatchingSnapshot(); @@ -136,4 +152,8 @@ test('returns error when not string or non-safe positive integer', () => { expect(() => duration().validate([1, 2, 3])).toThrowErrorMatchingSnapshot(); expect(() => duration().validate(/abc/)).toThrowErrorMatchingSnapshot(); + + expect(() => duration().validate('123foo')).toThrowErrorMatchingSnapshot(); + + expect(() => duration().validate('123 456')).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/kbn-dev-utils/src/run/README.md b/packages/kbn-dev-utils/src/run/README.md index 913b601058f870..99893a62376689 100644 --- a/packages/kbn-dev-utils/src/run/README.md +++ b/packages/kbn-dev-utils/src/run/README.md @@ -117,7 +117,7 @@ $ node scripts/my_task - *`flags.allowUnexpected: boolean`* - By default, any flag that is passed but not mentioned in `flags.string`, `flags.boolean`, `flags.alias` or `flags.default` will trigger an error, preventing the run function from calling its first argument. If you have a reason to disable this behavior set this option to `true`. + By default, any flag that is passed but not mentioned in `flags.string`, `flags.boolean`, `flags.alias` or `flags.default` will trigger an error, preventing the run function from calling its first argument. If you have a reason to disable this behavior set this option to `true`. Unexpected flags will be collected from argv into `flags.unexpected`. To parse these flags and guess at their types, you can additionally pass `flags.guessTypesForUnexpectedFlags` but that's not recommended. - ***`createFailError(reason: string, options: { exitCode: number, showHelp: boolean }): FailError`*** diff --git a/packages/kbn-dev-utils/src/run/flags.test.ts b/packages/kbn-dev-utils/src/run/flags.test.ts new file mode 100644 index 00000000000000..c730067a84f46a --- /dev/null +++ b/packages/kbn-dev-utils/src/run/flags.test.ts @@ -0,0 +1,94 @@ +/* + * 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 { getFlags } from './flags'; + +it('gets flags correctly', () => { + expect( + getFlags(['-a', '--abc=bcd', '--foo=bar', '--no-bar', '--foo=baz', '--box', 'yes', '-zxy'], { + flags: { + boolean: ['x'], + string: ['abc'], + alias: { + x: 'extra', + }, + allowUnexpected: true, + }, + }) + ).toMatchInlineSnapshot(` + Object { + "_": Array [], + "abc": "bcd", + "debug": false, + "extra": true, + "help": false, + "quiet": false, + "silent": false, + "unexpected": Array [ + "-a", + "--foo=bar", + "--foo=baz", + "--no-bar", + "--box", + "yes", + "-z", + "-y", + ], + "v": false, + "verbose": false, + "x": true, + } + `); +}); + +it('guesses types for unexpected flags', () => { + expect( + getFlags(['-abc', '--abc=bcd', '--no-foo', '--bar'], { + flags: { + allowUnexpected: true, + guessTypesForUnexpectedFlags: true, + }, + }) + ).toMatchInlineSnapshot(` + Object { + "_": Array [], + "a": true, + "abc": "bcd", + "b": true, + "bar": true, + "c": true, + "debug": false, + "foo": false, + "help": false, + "quiet": false, + "silent": false, + "unexpected": Array [ + "-a", + "-b", + "-c", + "-abc", + "--abc=bcd", + "--no-foo", + "--bar", + ], + "v": false, + "verbose": false, + } + `); +}); diff --git a/packages/kbn-dev-utils/src/run/flags.ts b/packages/kbn-dev-utils/src/run/flags.ts index 6a2966359ece12..c809a40d8512ba 100644 --- a/packages/kbn-dev-utils/src/run/flags.ts +++ b/packages/kbn-dev-utils/src/run/flags.ts @@ -37,7 +37,7 @@ export interface Flags { } export function getFlags(argv: string[], options: Options): Flags { - const unexpected: string[] = []; + const unexpectedNames = new Set(); const flagOpts = options.flags || {}; const { verbose, quiet, silent, debug, help, _, ...others } = getopts(argv, { @@ -49,15 +49,64 @@ export function getFlags(argv: string[], options: Options): Flags { }, default: flagOpts.default, unknown: (name: string) => { - unexpected.push(name); + unexpectedNames.add(name); + return flagOpts.guessTypesForUnexpectedFlags; + }, + } as any); + + const unexpected: string[] = []; + for (const unexpectedName of unexpectedNames) { + const matchingArgv: string[] = []; + + iterArgv: for (const [i, v] of argv.entries()) { + for (const prefix of ['--', '-']) { + if (v.startsWith(prefix)) { + // -/--name=value + if (v.startsWith(`${prefix}${unexpectedName}=`)) { + matchingArgv.push(v); + continue iterArgv; + } + + // -/--name (value possibly follows) + if (v === `${prefix}${unexpectedName}`) { + matchingArgv.push(v); - if (options.flags && options.flags.allowUnexpected) { - return true; + // value follows -/--name + if (argv.length > i + 1 && !argv[i + 1].startsWith('-')) { + matchingArgv.push(argv[i + 1]); + } + + continue iterArgv; + } + } } - return false; - }, - } as any); + // special case for `--no-{flag}` disabling of boolean flags + if (v === `--no-${unexpectedName}`) { + matchingArgv.push(v); + continue iterArgv; + } + + // special case for shortcut flags formatted as `-abc` where `a`, `b`, + // and `c` will be three separate unexpected flags + if ( + unexpectedName.length === 1 && + v[0] === '-' && + v[1] !== '-' && + !v.includes('=') && + v.includes(unexpectedName) + ) { + matchingArgv.push(`-${unexpectedName}`); + continue iterArgv; + } + } + + if (matchingArgv.length) { + unexpected.push(...matchingArgv); + } else { + throw new Error(`unable to find unexpected flag named "${unexpectedName}"`); + } + } return { verbose, @@ -75,7 +124,7 @@ export function getHelp(options: Options) { const usage = options.usage || `node ${relative(process.cwd(), process.argv[1])}`; const optionHelp = ( - dedent((options.flags && options.flags.help) || '') + + dedent(options?.flags?.help || '') + '\n' + dedent` --verbose, -v Log verbosely diff --git a/packages/kbn-dev-utils/src/run/run.ts b/packages/kbn-dev-utils/src/run/run.ts index 06746c663b917c..1d28d435757297 100644 --- a/packages/kbn-dev-utils/src/run/run.ts +++ b/packages/kbn-dev-utils/src/run/run.ts @@ -36,6 +36,7 @@ export interface Options { description?: string; flags?: { allowUnexpected?: boolean; + guessTypesForUnexpectedFlags?: boolean; help?: string; alias?: { [key: string]: string | string[] }; boolean?: string[]; @@ -46,7 +47,6 @@ export interface Options { export async function run(fn: RunFn, options: Options = {}) { const flags = getFlags(process.argv.slice(2), options); - const allowUnexpected = options.flags ? options.flags.allowUnexpected : false; if (flags.help) { process.stderr.write(getHelp(options)); @@ -97,7 +97,7 @@ export async function run(fn: RunFn, options: Options = {}) { const cleanupTasks: CleanupTask[] = [unhookExit]; try { - if (!allowUnexpected && flags.unexpected.length) { + if (!options.flags?.allowUnexpected && flags.unexpected.length) { throw createFlagError(`Unknown flag(s) "${flags.unexpected.join('", "')}"`); } diff --git a/packages/kbn-dev-utils/tsconfig.json b/packages/kbn-dev-utils/tsconfig.json index 4c519a609d86fe..0ec058eeb8a280 100644 --- a/packages/kbn-dev-utils/tsconfig.json +++ b/packages/kbn-dev-utils/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "outDir": "target", "target": "ES2019", - "declaration": true + "declaration": true, + "declarationMap": true }, "include": [ "src/**/*" diff --git a/packages/kbn-es/src/artifact.js b/packages/kbn-es/src/artifact.js index 19d95e82fe4800..9ea78386269d93 100644 --- a/packages/kbn-es/src/artifact.js +++ b/packages/kbn-es/src/artifact.js @@ -27,16 +27,14 @@ const { createHash } = require('crypto'); const path = require('path'); const asyncPipeline = promisify(pipeline); -const V1_VERSIONS_API = 'https://artifacts-api.elastic.co/v1/versions'; +const DAILY_SNAPSHOTS_BASE_URL = 'https://storage.googleapis.com/kibana-ci-es-snapshots-daily'; +const PERMANENT_SNAPSHOTS_BASE_URL = + 'https://storage.googleapis.com/kibana-ci-es-snapshots-permanent'; const { cache } = require('./utils'); const { resolveCustomSnapshotUrl } = require('./custom_snapshots'); const { createCliError, isCliError } = require('./errors'); -const TEST_ES_SNAPSHOT_VERSION = process.env.TEST_ES_SNAPSHOT_VERSION - ? process.env.TEST_ES_SNAPSHOT_VERSION - : 'latest'; - function getChecksumType(checksumUrl) { if (checksumUrl.endsWith('.sha512')) { return 'sha512'; @@ -45,20 +43,6 @@ function getChecksumType(checksumUrl) { throw new Error(`unable to determine checksum type: ${checksumUrl}`); } -function getPlatform(key) { - if (key.includes('-linux-')) { - return 'linux'; - } - - if (key.includes('-windows-')) { - return 'win32'; - } - - if (key.includes('-darwin-')) { - return 'darwin'; - } -} - function headersToString(headers, indent = '') { return [...headers.entries()].reduce( (acc, [key, value]) => `${acc}\n${indent}${key}: ${value}`, @@ -85,6 +69,75 @@ async function retry(log, fn) { return await doAttempt(1); } +// Setting this flag provides an easy way to run the latest un-promoted snapshot without having to look it up +function shouldUseUnverifiedSnapshot() { + return !!process.env.KBN_ES_SNAPSHOT_USE_UNVERIFIED; +} + +async function fetchSnapshotManifest(url, log) { + log.info('Downloading snapshot manifest from %s', chalk.bold(url)); + + const abc = new AbortController(); + const resp = await retry(log, async () => await fetch(url, { signal: abc.signal })); + const json = await resp.text(); + + return { abc, resp, json }; +} + +async function getArtifactSpecForSnapshot(urlVersion, license, log) { + const desiredVersion = urlVersion.replace('-SNAPSHOT', ''); + const desiredLicense = license === 'oss' ? 'oss' : 'default'; + + const customManifestUrl = process.env.ES_SNAPSHOT_MANIFEST; + const primaryManifestUrl = `${DAILY_SNAPSHOTS_BASE_URL}/${desiredVersion}/manifest-latest${ + shouldUseUnverifiedSnapshot() ? '' : '-verified' + }.json`; + const secondaryManifestUrl = `${PERMANENT_SNAPSHOTS_BASE_URL}/${desiredVersion}/manifest.json`; + + let { abc, resp, json } = await fetchSnapshotManifest( + customManifestUrl || primaryManifestUrl, + log + ); + + if (!customManifestUrl && !shouldUseUnverifiedSnapshot() && resp.status === 404) { + log.info('Daily snapshot manifest not found, falling back to permanent manifest'); + ({ abc, resp, json } = await fetchSnapshotManifest(secondaryManifestUrl, log)); + } + + if (resp.status === 404) { + abc.abort(); + throw createCliError(`Snapshots for ${desiredVersion} are not available`); + } + + if (!resp.ok) { + abc.abort(); + throw new Error(`Unable to read snapshot manifest: ${resp.statusText}\n ${json}`); + } + + const manifest = JSON.parse(json); + + const platform = process.platform === 'win32' ? 'windows' : process.platform; + const archive = manifest.archives.find( + archive => + archive.version === desiredVersion && + archive.platform === platform && + archive.license === desiredLicense + ); + + if (!archive) { + throw createCliError( + `Snapshots for ${desiredVersion} are available, but couldn't find an artifact in the manifest for [${desiredVersion}, ${desiredLicense}, ${platform}]` + ); + } + + return { + url: archive.url, + checksumUrl: archive.url + '.sha512', + checksumType: 'sha512', + filename: archive.filename, + }; +} + exports.Artifact = class Artifact { /** * Fetch an Artifact from the Artifact API for a license level and version @@ -100,71 +153,7 @@ exports.Artifact = class Artifact { return new Artifact(customSnapshotArtifactSpec, log); } - const urlBuild = encodeURIComponent(TEST_ES_SNAPSHOT_VERSION); - const url = `${V1_VERSIONS_API}/${urlVersion}/builds/${urlBuild}/projects/elasticsearch`; - - const json = await retry(log, async () => { - log.info('downloading artifact info from %s', chalk.bold(url)); - - const abc = new AbortController(); - const resp = await fetch(url, { signal: abc.signal }); - const json = await resp.text(); - - if (resp.status === 404) { - abc.abort(); - throw createCliError( - `Snapshots for ${version}/${TEST_ES_SNAPSHOT_VERSION} are not available` - ); - } - - if (!resp.ok) { - abc.abort(); - throw new Error(`Unable to read artifact info from ${url}: ${resp.statusText}\n ${json}`); - } - - return json; - }); - - // parse the api response into an array of Artifact objects - const { - project: { packages: artifactInfoMap }, - } = JSON.parse(json); - const filenames = Object.keys(artifactInfoMap); - const hasNoJdkVersions = filenames.some(filename => filename.includes('-no-jdk-')); - const artifactSpecs = filenames.map(filename => ({ - filename, - url: artifactInfoMap[filename].url, - checksumUrl: artifactInfoMap[filename].sha_url, - checksumType: getChecksumType(artifactInfoMap[filename].sha_url), - type: artifactInfoMap[filename].type, - isOss: filename.includes('-oss-'), - platform: getPlatform(filename), - jdkRequired: hasNoJdkVersions ? filename.includes('-no-jdk-') : true, - })); - - // pick the artifact we are going to use for this license/version combo - const reqOss = license === 'oss'; - const reqPlatform = artifactSpecs.some(a => a.platform !== undefined) - ? process.platform - : undefined; - const reqJdkRequired = hasNoJdkVersions ? false : true; - const reqType = process.platform === 'win32' ? 'zip' : 'tar'; - - const artifactSpec = artifactSpecs.find( - spec => - spec.isOss === reqOss && - spec.type === reqType && - spec.platform === reqPlatform && - spec.jdkRequired === reqJdkRequired - ); - - if (!artifactSpec) { - throw new Error( - `Unable to determine artifact for license [${license}] and version [${version}]\n` + - ` options: ${filenames.join(',')}` - ); - } - + const artifactSpec = await getArtifactSpecForSnapshot(urlVersion, license, log); return new Artifact(artifactSpec, log); } diff --git a/packages/kbn-es/src/artifact.test.js b/packages/kbn-es/src/artifact.test.js new file mode 100644 index 00000000000000..985b65c7475634 --- /dev/null +++ b/packages/kbn-es/src/artifact.test.js @@ -0,0 +1,191 @@ +/* + * 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 { ToolingLog } from '@kbn/dev-utils'; +jest.mock('node-fetch'); +import fetch from 'node-fetch'; +const { Response } = jest.requireActual('node-fetch'); + +import { Artifact } from './artifact'; + +const log = new ToolingLog(); +let MOCKS; + +const PLATFORM = process.platform === 'win32' ? 'windows' : process.platform; +const MOCK_VERSION = 'test-version'; +const MOCK_URL = 'http://127.0.0.1:12345'; +const MOCK_FILENAME = 'test-filename'; + +const DAILY_SNAPSHOT_BASE_URL = 'https://storage.googleapis.com/kibana-ci-es-snapshots-daily'; +const PERMANENT_SNAPSHOT_BASE_URL = + 'https://storage.googleapis.com/kibana-ci-es-snapshots-permanent'; + +const createArchive = (params = {}) => { + const license = params.license || 'default'; + + return { + license: 'default', + version: MOCK_VERSION, + url: MOCK_URL + `/${license}`, + platform: PLATFORM, + filename: MOCK_FILENAME + `.${license}`, + ...params, + }; +}; + +const mockFetch = mock => + fetch.mockReturnValue(Promise.resolve(new Response(JSON.stringify(mock)))); + +let previousSnapshotManifestValue = null; + +beforeAll(() => { + if ('ES_SNAPSHOT_MANIFEST' in process.env) { + previousSnapshotManifestValue = process.env.ES_SNAPSHOT_MANIFEST; + delete process.env.ES_SNAPSHOT_MANIFEST; + } +}); + +afterAll(() => { + if (previousSnapshotManifestValue !== null) { + process.env.ES_SNAPSHOT_MANIFEST = previousSnapshotManifestValue; + } else { + delete process.env.ES_SNAPSHOT_MANIFEST; + } +}); + +beforeEach(() => { + jest.resetAllMocks(); + + MOCKS = { + valid: { + archives: [createArchive({ license: 'oss' }), createArchive({ license: 'default' })], + }, + }; +}); + +const artifactTest = (requestedLicense, expectedLicense, fetchTimesCalled = 1) => { + return async () => { + const artifact = await Artifact.getSnapshot(requestedLicense, MOCK_VERSION, log); + expect(fetch).toHaveBeenCalledTimes(fetchTimesCalled); + expect(fetch.mock.calls[0][0]).toEqual( + `${DAILY_SNAPSHOT_BASE_URL}/${MOCK_VERSION}/manifest-latest-verified.json` + ); + if (fetchTimesCalled === 2) { + expect(fetch.mock.calls[1][0]).toEqual( + `${PERMANENT_SNAPSHOT_BASE_URL}/${MOCK_VERSION}/manifest.json` + ); + } + expect(artifact.getUrl()).toEqual(MOCK_URL + `/${expectedLicense}`); + expect(artifact.getChecksumUrl()).toEqual(MOCK_URL + `/${expectedLicense}.sha512`); + expect(artifact.getChecksumType()).toEqual('sha512'); + expect(artifact.getFilename()).toEqual(MOCK_FILENAME + `.${expectedLicense}`); + }; +}; + +describe('Artifact', () => { + describe('getSnapshot()', () => { + describe('with default snapshot', () => { + beforeEach(() => { + mockFetch(MOCKS.valid); + }); + + it('should return artifact metadata for a daily oss artifact', artifactTest('oss', 'oss')); + + it( + 'should return artifact metadata for a daily default artifact', + artifactTest('default', 'default') + ); + + it( + 'should default to default license with anything other than "oss"', + artifactTest('INVALID_LICENSE', 'default') + ); + + it('should throw when an artifact cannot be found in the manifest for the specified parameters', async () => { + await expect(Artifact.getSnapshot('default', 'INVALID_VERSION', log)).rejects.toThrow( + "couldn't find an artifact" + ); + }); + }); + + describe('with missing default snapshot', () => { + beforeEach(() => { + fetch.mockReturnValueOnce(Promise.resolve(new Response('', { status: 404 }))); + mockFetch(MOCKS.valid); + }); + + it( + 'should return artifact metadata for a permanent oss artifact', + artifactTest('oss', 'oss', 2) + ); + + it( + 'should return artifact metadata for a permanent default artifact', + artifactTest('default', 'default', 2) + ); + + it( + 'should default to default license with anything other than "oss"', + artifactTest('INVALID_LICENSE', 'default', 2) + ); + + it('should throw when an artifact cannot be found in the manifest for the specified parameters', async () => { + await expect(Artifact.getSnapshot('default', 'INVALID_VERSION', log)).rejects.toThrow( + "couldn't find an artifact" + ); + }); + }); + + describe('with custom snapshot manifest URL', () => { + const CUSTOM_URL = 'http://www.creedthoughts.gov.www/creedthoughts'; + + beforeEach(() => { + process.env.ES_SNAPSHOT_MANIFEST = CUSTOM_URL; + mockFetch(MOCKS.valid); + }); + + it('should use the custom URL when looking for a snapshot', async () => { + await Artifact.getSnapshot('oss', MOCK_VERSION, log); + expect(fetch.mock.calls[0][0]).toEqual(CUSTOM_URL); + }); + + afterEach(() => { + delete process.env.ES_SNAPSHOT_MANIFEST; + }); + }); + + describe('with latest unverified snapshot', () => { + beforeEach(() => { + process.env.KBN_ES_SNAPSHOT_USE_UNVERIFIED = 1; + mockFetch(MOCKS.valid); + }); + + it('should use the daily unverified URL when looking for a snapshot', async () => { + await Artifact.getSnapshot('oss', MOCK_VERSION, log); + expect(fetch.mock.calls[0][0]).toEqual( + `${DAILY_SNAPSHOT_BASE_URL}/${MOCK_VERSION}/manifest-latest.json` + ); + }); + + afterEach(() => { + delete process.env.KBN_ES_SNAPSHOT_USE_UNVERIFIED; + }); + }); + }); +}); diff --git a/packages/kbn-es/src/custom_snapshots.js b/packages/kbn-es/src/custom_snapshots.js index 74de3c2c792fdc..c6b00f77f0a881 100644 --- a/packages/kbn-es/src/custom_snapshots.js +++ b/packages/kbn-es/src/custom_snapshots.js @@ -25,8 +25,13 @@ function isVersionFlag(a) { function getCustomSnapshotUrl() { // force use of manually created snapshots until ReindexPutMappings fix - if (!process.env.KBN_ES_SNAPSHOT_URL && !process.argv.some(isVersionFlag)) { - return 'https://storage.googleapis.com/kibana-ci-tmp-artifacts/{name}-{version}-{os}-x86_64.{ext}'; + if ( + !process.env.ES_SNAPSHOT_MANIFEST && + !process.env.KBN_ES_SNAPSHOT_URL && + !process.argv.some(isVersionFlag) + ) { + // return 'https://storage.googleapis.com/kibana-ci-tmp-artifacts/{name}-{version}-{os}-x86_64.{ext}'; + return; } if (process.env.KBN_ES_SNAPSHOT_URL && process.env.KBN_ES_SNAPSHOT_URL !== 'false') { diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index badcf13187cafb..026938213ac83a 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -4,12 +4,12 @@ "private": true, "license": "Apache-2.0", "peerDependencies": { - "eslint": "6.5.1", + "eslint": "6.8.0", "babel-eslint": "^10.0.3" }, "dependencies": { "micromatch": "3.1.10", "dedent": "^0.7.0", - "eslint-module-utils": "2.4.1" + "eslint-module-utils": "2.5.0" } } diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 7c5937af441a2e..c3f3f2f477fdd0 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -37054,8 +37054,8 @@ const tooling_log_1 = __webpack_require__(415); const fail_1 = __webpack_require__(425); const flags_1 = __webpack_require__(426); async function run(fn, options = {}) { + var _a; const flags = flags_1.getFlags(process.argv.slice(2), options); - const allowUnexpected = options.flags ? options.flags.allowUnexpected : false; if (flags.help) { process.stderr.write(flags_1.getHelp(options)); process.exit(1); @@ -37098,7 +37098,7 @@ async function run(fn, options = {}) { const unhookExit = exit_hook_1.default(doCleanup); const cleanupTasks = [unhookExit]; try { - if (!allowUnexpected && flags.unexpected.length) { + if (!((_a = options.flags) === null || _a === void 0 ? void 0 : _a.allowUnexpected) && flags.unexpected.length) { throw fail_1.createFlagError(`Unknown flag(s) "${flags.unexpected.join('", "')}"`); } try { @@ -37218,7 +37218,7 @@ const path_1 = __webpack_require__(16); const dedent_1 = tslib_1.__importDefault(__webpack_require__(14)); const getopts_1 = tslib_1.__importDefault(__webpack_require__(427)); function getFlags(argv, options) { - const unexpected = []; + const unexpectedNames = new Set(); const flagOpts = options.flags || {}; const { verbose, quiet, silent, debug, help, _, ...others } = getopts_1.default(argv, { string: flagOpts.string, @@ -37229,13 +37229,55 @@ function getFlags(argv, options) { }, default: flagOpts.default, unknown: (name) => { - unexpected.push(name); - if (options.flags && options.flags.allowUnexpected) { - return true; - } - return false; + unexpectedNames.add(name); + return flagOpts.guessTypesForUnexpectedFlags; }, }); + const unexpected = []; + for (const unexpectedName of unexpectedNames) { + const matchingArgv = []; + iterArgv: for (const [i, v] of argv.entries()) { + for (const prefix of ['--', '-']) { + if (v.startsWith(prefix)) { + // -/--name=value + if (v.startsWith(`${prefix}${unexpectedName}=`)) { + matchingArgv.push(v); + continue iterArgv; + } + // -/--name (value possibly follows) + if (v === `${prefix}${unexpectedName}`) { + matchingArgv.push(v); + // value follows -/--name + if (argv.length > i + 1 && !argv[i + 1].startsWith('-')) { + matchingArgv.push(argv[i + 1]); + } + continue iterArgv; + } + } + } + // special case for `--no-{flag}` disabling of boolean flags + if (v === `--no-${unexpectedName}`) { + matchingArgv.push(v); + continue iterArgv; + } + // special case for shortcut flags formatted as `-abc` where `a`, `b`, + // and `c` will be three separate unexpected flags + if (unexpectedName.length === 1 && + v[0] === '-' && + v[1] !== '-' && + !v.includes('=') && + v.includes(unexpectedName)) { + matchingArgv.push(`-${unexpectedName}`); + continue iterArgv; + } + } + if (matchingArgv.length) { + unexpected.push(...matchingArgv); + } + else { + throw new Error(`unable to find unexpected flag named "${unexpectedName}"`); + } + } return { verbose, quiet, @@ -37249,8 +37291,9 @@ function getFlags(argv, options) { } exports.getFlags = getFlags; function getHelp(options) { + var _a, _b; const usage = options.usage || `node ${path_1.relative(process.cwd(), process.argv[1])}`; - const optionHelp = (dedent_1.default((options.flags && options.flags.help) || '') + + const optionHelp = (dedent_1.default(((_b = (_a = options) === null || _a === void 0 ? void 0 : _a.flags) === null || _b === void 0 ? void 0 : _b.help) || '') + '\n' + dedent_1.default ` --verbose, -v Log verbosely @@ -79815,7 +79858,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(704); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(909); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(914); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -79997,8 +80040,8 @@ const EventEmitter = __webpack_require__(379); const path = __webpack_require__(16); const arrify = __webpack_require__(706); const globby = __webpack_require__(707); -const cpFile = __webpack_require__(900); -const CpyError = __webpack_require__(907); +const cpFile = __webpack_require__(905); +const CpyError = __webpack_require__(912); const preprocessSrcPath = (srcPath, options) => options.cwd ? path.resolve(options.cwd, srcPath) : srcPath; @@ -80127,8 +80170,8 @@ const fs = __webpack_require__(23); const arrayUnion = __webpack_require__(708); const glob = __webpack_require__(502); const fastGlob = __webpack_require__(710); -const dirGlob = __webpack_require__(893); -const gitignore = __webpack_require__(896); +const dirGlob = __webpack_require__(898); +const gitignore = __webpack_require__(901); const DEFAULT_FILTER = () => false; @@ -80379,11 +80422,11 @@ module.exports.generateTasks = pkg.generateTasks; Object.defineProperty(exports, "__esModule", { value: true }); var optionsManager = __webpack_require__(712); var taskManager = __webpack_require__(713); -var reader_async_1 = __webpack_require__(864); -var reader_stream_1 = __webpack_require__(888); -var reader_sync_1 = __webpack_require__(889); -var arrayUtils = __webpack_require__(891); -var streamUtils = __webpack_require__(892); +var reader_async_1 = __webpack_require__(869); +var reader_stream_1 = __webpack_require__(893); +var reader_sync_1 = __webpack_require__(894); +var arrayUtils = __webpack_require__(896); +var streamUtils = __webpack_require__(897); /** * Synchronous API. */ @@ -81023,9 +81066,9 @@ var extend = __webpack_require__(830); */ var compilers = __webpack_require__(833); -var parsers = __webpack_require__(860); -var cache = __webpack_require__(861); -var utils = __webpack_require__(862); +var parsers = __webpack_require__(865); +var cache = __webpack_require__(866); +var utils = __webpack_require__(867); var MAX_LENGTH = 1024 * 64; /** @@ -99558,9 +99601,9 @@ var toRegex = __webpack_require__(721); */ var compilers = __webpack_require__(850); -var parsers = __webpack_require__(856); -var Extglob = __webpack_require__(859); -var utils = __webpack_require__(858); +var parsers = __webpack_require__(861); +var Extglob = __webpack_require__(864); +var utils = __webpack_require__(863); var MAX_LENGTH = 1024 * 64; /** @@ -100070,7 +100113,7 @@ var parsers = __webpack_require__(854); * Module dependencies */ -var debug = __webpack_require__(793)('expand-brackets'); +var debug = __webpack_require__(856)('expand-brackets'); var extend = __webpack_require__(730); var Snapdragon = __webpack_require__(760); var toRegex = __webpack_require__(721); @@ -100664,12 +100707,839 @@ exports.createRegex = function(pattern, include) { /* 856 */ /***/ (function(module, exports, __webpack_require__) { +/** + * Detect Electron renderer process, which is node, but we should + * treat as a browser. + */ + +if (typeof process !== 'undefined' && process.type === 'renderer') { + module.exports = __webpack_require__(857); +} else { + module.exports = __webpack_require__(860); +} + + +/***/ }), +/* 857 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = __webpack_require__(858); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); + +/** + * Colors. + */ + +exports.colors = [ + 'lightseagreen', + 'forestgreen', + 'goldenrod', + 'dodgerblue', + 'darkorchid', + 'crimson' +]; + +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ + +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; + } + + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} + +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ + +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } +}; + + +/** + * Colorize log arguments if enabled. + * + * @api public + */ + +function formatArgs(args) { + var useColors = this.useColors; + + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); + + if (!useColors) return; + + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit') + + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); + + args.splice(lastC, 0, c); +} + +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ + +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; + } + } catch(e) {} +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} + + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + + return r; +} + +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ + +exports.enable(load()); + +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + return window.localStorage; + } catch (e) {} +} + + +/***/ }), +/* 858 */ +/***/ (function(module, exports, __webpack_require__) { + + +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = __webpack_require__(859); + +/** + * The currently active debug mode names, and names to skip. + */ + +exports.names = []; +exports.skips = []; + +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ + +exports.formatters = {}; + +/** + * Previous log timestamp. + */ + +var prevTime; + +/** + * Select a color. + * @param {String} namespace + * @return {Number} + * @api private + */ + +function selectColor(namespace) { + var hash = 0, i; + + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer + } + + return exports.colors[Math.abs(hash) % exports.colors.length]; +} + +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ + +function createDebug(namespace) { + + function debug() { + // disabled? + if (!debug.enabled) return; + + var self = debug; + + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; + + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } + + args[0] = exports.coerce(args[0]); + + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); + } + + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); + + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; + } + return match; + }); + + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); + + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } + + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } + + return debug; +} + +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ + +function enable(namespaces) { + exports.save(namespaces); + + exports.names = []; + exports.skips = []; + + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; + + for (var i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } +} + +/** + * Disable debug output. + * + * @api public + */ + +function disable() { + exports.enable(''); +} + +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ + +function enabled(name) { + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; + } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; + } + } + return false; +} + +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ + +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} + + +/***/ }), +/* 859 */ +/***/ (function(module, exports) { + +/** + * Helpers. + */ + +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; + +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ + +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; + +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ + +function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} + +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} + +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ + +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; +} + +/** + * Pluralization helper. + */ + +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} + + +/***/ }), +/* 860 */ +/***/ (function(module, exports, __webpack_require__) { + +/** + * Module dependencies. + */ + +var tty = __webpack_require__(480); +var util = __webpack_require__(29); + +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ + +exports = module.exports = __webpack_require__(858); +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; + +/** + * Colors. + */ + +exports.colors = [6, 2, 3, 4, 5, 1]; + +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ + +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // camel-case + var prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); + + // coerce string value into JS value + var val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) val = true; + else if (/^(no|off|false|disabled)$/i.test(val)) val = false; + else if (val === 'null') val = null; + else val = Number(val); + + obj[prop] = val; + return obj; +}, {}); + +/** + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: + * + * $ DEBUG_FD=3 node script.js 3>debug.log + */ + +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; + +if (1 !== fd && 2 !== fd) { + util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() +} + +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); + +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ + +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(fd); +} + +/** + * Map %o to `util.inspect()`, all on a single line. + */ + +exports.formatters.o = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .split('\n').map(function(str) { + return str.trim() + }).join(' '); +}; + +/** + * Map %o to `util.inspect()`, allowing multiple lines if needed. + */ + +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; + +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ + +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; + + if (useColors) { + var c = this.color; + var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; + + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } +} + +/** + * Invokes `util.format()` with the specified arguments and writes to `stream`. + */ + +function log() { + return stream.write(util.format.apply(util, arguments) + '\n'); +} + +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ + +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} + +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ + +function load() { + return process.env.DEBUG; +} + +/** + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. + */ + +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + + // Note stream._type is used for test-module-load-list.js + + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; + + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + case 'FILE': + var fs = __webpack_require__(23); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; + + case 'PIPE': + case 'TCP': + var net = __webpack_require__(798); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); + + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; + + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; + + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } + + // For supporting legacy API we put the FD here. + stream.fd = fd; + + stream._isStdio = true; + + return stream; +} + +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ + +function init (debug) { + debug.inspectOpts = {}; + + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} + +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ + +exports.enable(load()); + + +/***/ }), +/* 861 */ +/***/ (function(module, exports, __webpack_require__) { + "use strict"; var brackets = __webpack_require__(851); -var define = __webpack_require__(857); -var utils = __webpack_require__(858); +var define = __webpack_require__(862); +var utils = __webpack_require__(863); /** * Characters to use in text regex (we want to "not" match @@ -100824,7 +101694,7 @@ module.exports = parsers; /***/ }), -/* 857 */ +/* 862 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100862,7 +101732,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 858 */ +/* 863 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100938,7 +101808,7 @@ utils.createRegex = function(str) { /***/ }), -/* 859 */ +/* 864 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100949,7 +101819,7 @@ utils.createRegex = function(str) { */ var Snapdragon = __webpack_require__(760); -var define = __webpack_require__(857); +var define = __webpack_require__(862); var extend = __webpack_require__(730); /** @@ -100957,7 +101827,7 @@ var extend = __webpack_require__(730); */ var compilers = __webpack_require__(850); -var parsers = __webpack_require__(856); +var parsers = __webpack_require__(861); /** * Customize Snapdragon parser and renderer @@ -101023,7 +101893,7 @@ module.exports = Extglob; /***/ }), -/* 860 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101113,14 +101983,14 @@ function textRegex(pattern) { /***/ }), -/* 861 */ +/* 866 */ /***/ (function(module, exports, __webpack_require__) { module.exports = new (__webpack_require__(842))(); /***/ }), -/* 862 */ +/* 867 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101138,7 +102008,7 @@ utils.define = __webpack_require__(829); utils.diff = __webpack_require__(846); utils.extend = __webpack_require__(830); utils.pick = __webpack_require__(847); -utils.typeOf = __webpack_require__(863); +utils.typeOf = __webpack_require__(868); utils.unique = __webpack_require__(733); /** @@ -101436,7 +102306,7 @@ utils.unixify = function(options) { /***/ }), -/* 863 */ +/* 868 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -101571,7 +102441,7 @@ function isBuffer(val) { /***/ }), -/* 864 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101590,9 +102460,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(865); -var reader_1 = __webpack_require__(878); -var fs_stream_1 = __webpack_require__(882); +var readdir = __webpack_require__(870); +var reader_1 = __webpack_require__(883); +var fs_stream_1 = __webpack_require__(887); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -101653,15 +102523,15 @@ exports.default = ReaderAsync; /***/ }), -/* 865 */ +/* 870 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(866); -const readdirAsync = __webpack_require__(874); -const readdirStream = __webpack_require__(877); +const readdirSync = __webpack_require__(871); +const readdirAsync = __webpack_require__(879); +const readdirStream = __webpack_require__(882); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -101745,7 +102615,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 866 */ +/* 871 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101753,11 +102623,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(867); +const DirectoryReader = __webpack_require__(872); let syncFacade = { - fs: __webpack_require__(872), - forEach: __webpack_require__(873), + fs: __webpack_require__(877), + forEach: __webpack_require__(878), sync: true }; @@ -101786,7 +102656,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 867 */ +/* 872 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101795,9 +102665,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(868); -const stat = __webpack_require__(870); -const call = __webpack_require__(871); +const normalizeOptions = __webpack_require__(873); +const stat = __webpack_require__(875); +const call = __webpack_require__(876); /** * Asynchronously reads the contents of a directory and streams the results @@ -102173,14 +103043,14 @@ module.exports = DirectoryReader; /***/ }), -/* 868 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(869); +const globToRegExp = __webpack_require__(874); module.exports = normalizeOptions; @@ -102357,7 +103227,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 869 */ +/* 874 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -102494,13 +103364,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 870 */ +/* 875 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(871); +const call = __webpack_require__(876); module.exports = stat; @@ -102575,7 +103445,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 871 */ +/* 876 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102636,14 +103506,14 @@ function callOnce (fn) { /***/ }), -/* 872 */ +/* 877 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(871); +const call = __webpack_require__(876); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -102707,7 +103577,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 873 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102736,7 +103606,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 874 */ +/* 879 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102744,12 +103614,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(875); -const DirectoryReader = __webpack_require__(867); +const maybe = __webpack_require__(880); +const DirectoryReader = __webpack_require__(872); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(876), + forEach: __webpack_require__(881), async: true }; @@ -102791,7 +103661,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 875 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102818,7 +103688,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 876 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102854,7 +103724,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 877 */ +/* 882 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102862,11 +103732,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(867); +const DirectoryReader = __webpack_require__(872); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(876), + forEach: __webpack_require__(881), async: true }; @@ -102886,16 +103756,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 878 */ +/* 883 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(879); -var entry_1 = __webpack_require__(881); -var pathUtil = __webpack_require__(880); +var deep_1 = __webpack_require__(884); +var entry_1 = __webpack_require__(886); +var pathUtil = __webpack_require__(885); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -102961,13 +103831,13 @@ exports.default = Reader; /***/ }), -/* 879 */ +/* 884 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(880); +var pathUtils = __webpack_require__(885); var patternUtils = __webpack_require__(714); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { @@ -103051,7 +103921,7 @@ exports.default = DeepFilter; /***/ }), -/* 880 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103082,13 +103952,13 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 881 */ +/* 886 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(880); +var pathUtils = __webpack_require__(885); var patternUtils = __webpack_require__(714); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { @@ -103174,7 +104044,7 @@ exports.default = EntryFilter; /***/ }), -/* 882 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103194,8 +104064,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var fsStat = __webpack_require__(883); -var fs_1 = __webpack_require__(887); +var fsStat = __webpack_require__(888); +var fs_1 = __webpack_require__(892); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -103245,14 +104115,14 @@ exports.default = FileSystemStream; /***/ }), -/* 883 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(884); -const statProvider = __webpack_require__(886); +const optionsManager = __webpack_require__(889); +const statProvider = __webpack_require__(891); /** * Asynchronous API. */ @@ -103283,13 +104153,13 @@ exports.statSync = statSync; /***/ }), -/* 884 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(885); +const fsAdapter = __webpack_require__(890); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -103302,7 +104172,7 @@ exports.prepare = prepare; /***/ }), -/* 885 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103325,7 +104195,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 886 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103377,7 +104247,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 887 */ +/* 892 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103408,7 +104278,7 @@ exports.default = FileSystem; /***/ }), -/* 888 */ +/* 893 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103428,9 +104298,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var readdir = __webpack_require__(865); -var reader_1 = __webpack_require__(878); -var fs_stream_1 = __webpack_require__(882); +var readdir = __webpack_require__(870); +var reader_1 = __webpack_require__(883); +var fs_stream_1 = __webpack_require__(887); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -103498,7 +104368,7 @@ exports.default = ReaderStream; /***/ }), -/* 889 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103517,9 +104387,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(865); -var reader_1 = __webpack_require__(878); -var fs_sync_1 = __webpack_require__(890); +var readdir = __webpack_require__(870); +var reader_1 = __webpack_require__(883); +var fs_sync_1 = __webpack_require__(895); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -103579,7 +104449,7 @@ exports.default = ReaderSync; /***/ }), -/* 890 */ +/* 895 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103598,8 +104468,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(883); -var fs_1 = __webpack_require__(887); +var fsStat = __webpack_require__(888); +var fs_1 = __webpack_require__(892); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -103645,7 +104515,7 @@ exports.default = FileSystemSync; /***/ }), -/* 891 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103661,7 +104531,7 @@ exports.flatten = flatten; /***/ }), -/* 892 */ +/* 897 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103682,13 +104552,13 @@ exports.merge = merge; /***/ }), -/* 893 */ +/* 898 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(894); +const pathType = __webpack_require__(899); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -103754,13 +104624,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 894 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(895); +const pify = __webpack_require__(900); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -103803,7 +104673,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 895 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103894,7 +104764,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 896 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103902,9 +104772,9 @@ module.exports = (obj, opts) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const fastGlob = __webpack_require__(710); -const gitIgnore = __webpack_require__(897); -const pify = __webpack_require__(898); -const slash = __webpack_require__(899); +const gitIgnore = __webpack_require__(902); +const pify = __webpack_require__(903); +const slash = __webpack_require__(904); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -104002,7 +104872,7 @@ module.exports.sync = options => { /***/ }), -/* 897 */ +/* 902 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -104471,7 +105341,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 898 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104546,7 +105416,7 @@ module.exports = (input, options) => { /***/ }), -/* 899 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104564,17 +105434,17 @@ module.exports = input => { /***/ }), -/* 900 */ +/* 905 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const {Buffer} = __webpack_require__(901); -const CpFileError = __webpack_require__(902); -const fs = __webpack_require__(904); -const ProgressEmitter = __webpack_require__(906); +const {Buffer} = __webpack_require__(906); +const CpFileError = __webpack_require__(907); +const fs = __webpack_require__(909); +const ProgressEmitter = __webpack_require__(911); const cpFile = (source, destination, options) => { if (!source || !destination) { @@ -104728,7 +105598,7 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 901 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { /* eslint-disable node/no-deprecated-api */ @@ -104796,12 +105666,12 @@ SafeBuffer.allocUnsafeSlow = function (size) { /***/ }), -/* 902 */ +/* 907 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(903); +const NestedError = __webpack_require__(908); class CpFileError extends NestedError { constructor(message, nested) { @@ -104815,7 +105685,7 @@ module.exports = CpFileError; /***/ }), -/* 903 */ +/* 908 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(509); @@ -104869,15 +105739,15 @@ module.exports = NestedError; /***/ }), -/* 904 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(22); const makeDir = __webpack_require__(559); -const pify = __webpack_require__(905); -const CpFileError = __webpack_require__(902); +const pify = __webpack_require__(910); +const CpFileError = __webpack_require__(907); const fsP = pify(fs); @@ -105022,7 +105892,7 @@ if (fs.copyFileSync) { /***/ }), -/* 905 */ +/* 910 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105097,7 +105967,7 @@ module.exports = (input, options) => { /***/ }), -/* 906 */ +/* 911 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105138,12 +106008,12 @@ module.exports = ProgressEmitter; /***/ }), -/* 907 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(908); +const NestedError = __webpack_require__(913); class CpyError extends NestedError { constructor(message, nested) { @@ -105157,7 +106027,7 @@ module.exports = CpyError; /***/ }), -/* 908 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -105213,7 +106083,7 @@ module.exports = NestedError; /***/ }), -/* 909 */ +/* 914 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/renovate.json5 b/renovate.json5 index ecc9b3b2ceb624..f069e961c0f2be 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -46,35 +46,6 @@ enabled: false, }, packageRules: [ - { - groupSlug: 'eslint', - groupName: 'eslint related packages', - packagePatterns: [ - '(\\b|_)eslint(\\b|_)', - ], - }, - { - groupSlug: 'babel', - groupName: 'babel related packages', - packagePatterns: [ - '(\\b|_)babel(\\b|_)', - ], - packageNames: [ - 'core-js', - '@types/core-js', - '@babel/preset-react', - '@types/babel__preset-react', - '@babel/preset-typescript', - '@types/babel__preset-typescript', - ], - }, - { - groupSlug: 'jest', - groupName: 'jest related packages', - packagePatterns: [ - '(\\b|_)jest(\\b|_)', - ], - }, { groupSlug: '@elastic/charts', groupName: '@elastic/charts related packages', @@ -88,31 +59,11 @@ masterIssueApproval: false, }, { - groupSlug: 'mocha', - groupName: 'mocha related packages', - packagePatterns: [ - '(\\b|_)mocha(\\b|_)', - ], - }, - { - groupSlug: 'karma', - groupName: 'karma related packages', - packagePatterns: [ - '(\\b|_)karma(\\b|_)', - ], - }, - { - groupSlug: 'gulp', - groupName: 'gulp related packages', - packagePatterns: [ - '(\\b|_)gulp(\\b|_)', - ], - }, - { - groupSlug: 'grunt', - groupName: 'grunt related packages', - packagePatterns: [ - '(\\b|_)grunt(\\b|_)', + groupSlug: '@reach/router', + groupName: '@reach/router related packages', + packageNames: [ + '@reach/router', + '@types/reach__router', ], }, { @@ -122,126 +73,6 @@ '(\\b|_)angular(\\b|_)', ], }, - { - groupSlug: 'd3', - groupName: 'd3 related packages', - packagePatterns: [ - '(\\b|_)d3(\\b|_)', - ], - }, - { - groupSlug: 'react', - groupName: 'react related packages', - packagePatterns: [ - '(\\b|_)react(\\b|_)', - '(\\b|_)redux(\\b|_)', - '(\\b|_)enzyme(\\b|_)', - ], - packageNames: [ - 'ngreact', - '@types/ngreact', - 'recompose', - '@types/recompose', - 'prop-types', - '@types/prop-types', - 'typescript-fsa-reducers', - '@types/typescript-fsa-reducers', - 'reselect', - '@types/reselect', - ], - }, - { - groupSlug: 'moment', - groupName: 'moment related packages', - packagePatterns: [ - '(\\b|_)moment(\\b|_)', - ], - }, - { - groupSlug: 'graphql', - groupName: 'graphql related packages', - packagePatterns: [ - '(\\b|_)graphql(\\b|_)', - '(\\b|_)apollo(\\b|_)', - ], - }, - { - groupSlug: 'webpack', - groupName: 'webpack related packages', - packagePatterns: [ - '(\\b|_)webpack(\\b|_)', - '(\\b|_)loader(\\b|_)', - '(\\b|_)acorn(\\b|_)', - '(\\b|_)terser(\\b|_)', - ], - packageNames: [ - 'mini-css-extract-plugin', - '@types/mini-css-extract-plugin', - 'chokidar', - '@types/chokidar', - ], - }, - { - groupSlug: 'vega', - groupName: 'vega related packages', - packagePatterns: [ - '(\\b|_)vega(\\b|_)', - ], - enabled: false, - }, - { - groupSlug: 'language server', - groupName: 'language server related packages', - packageNames: [ - 'vscode-jsonrpc', - '@types/vscode-jsonrpc', - 'vscode-languageserver', - '@types/vscode-languageserver', - 'vscode-languageserver-types', - '@types/vscode-languageserver-types', - ], - }, - { - groupSlug: 'hapi', - groupName: 'hapi related packages', - packagePatterns: [ - '(\\b|_)hapi(\\b|_)', - ], - packageNames: [ - 'hapi', - '@types/hapi', - 'joi', - '@types/joi', - 'boom', - '@types/boom', - 'hoek', - '@types/hoek', - 'h2o2', - '@types/h2o2', - '@elastic/good', - '@types/elastic__good', - 'good-squeeze', - '@types/good-squeeze', - 'inert', - '@types/inert', - ], - }, - { - groupSlug: 'dragselect', - groupName: 'dragselect related packages', - packageNames: [ - 'dragselect', - '@types/dragselect', - ], - labels: [ - 'release_note:skip', - 'Team:Operations', - 'renovate', - 'v8.0.0', - 'v7.6.0', - ':ml', - ], - }, { groupSlug: 'api-documenter', groupName: 'api-documenter related packages', @@ -254,47 +85,34 @@ enabled: false, }, { - groupSlug: 'jsts', - groupName: 'jsts related packages', + groupSlug: 'archiver', + groupName: 'archiver related packages', packageNames: [ - 'jsts', - '@types/jsts', - ], - allowedVersions: '^1.6.2', - }, - { - groupSlug: 'storybook', - groupName: 'storybook related packages', - packagePatterns: [ - '(\\b|_)storybook(\\b|_)', + 'archiver', + '@types/archiver', ], }, { - groupSlug: 'typescript', - groupName: 'typescript related packages', + groupSlug: 'babel', + groupName: 'babel related packages', packagePatterns: [ - '(\\b|_)ts(\\b|_)', - '(\\b|_)typescript(\\b|_)', + '(\\b|_)babel(\\b|_)', ], packageNames: [ - 'tslib', - '@types/tslib', - ], - }, - { - groupSlug: 'json-stable-stringify', - groupName: 'json-stable-stringify related packages', - packageNames: [ - 'json-stable-stringify', - '@types/json-stable-stringify', + 'core-js', + '@types/core-js', + '@babel/preset-react', + '@types/babel__preset-react', + '@babel/preset-typescript', + '@types/babel__preset-typescript', ], }, { - groupSlug: 'lodash.clonedeep', - groupName: 'lodash.clonedeep related packages', + groupSlug: 'base64-js', + groupName: 'base64-js related packages', packageNames: [ - 'lodash.clonedeep', - '@types/lodash.clonedeep', + 'base64-js', + '@types/base64-js', ], }, { @@ -321,6 +139,14 @@ '@types/cheerio', ], }, + { + groupSlug: 'chroma-js', + groupName: 'chroma-js related packages', + packageNames: [ + 'chroma-js', + '@types/chroma-js', + ], + }, { groupSlug: 'chromedriver', groupName: 'chromedriver related packages', @@ -338,403 +164,441 @@ ], }, { - groupSlug: 'dedent', - groupName: 'dedent related packages', + groupSlug: 'cmd-shim', + groupName: 'cmd-shim related packages', packageNames: [ - 'dedent', - '@types/dedent', + 'cmd-shim', + '@types/cmd-shim', ], }, { - groupSlug: 'delete-empty', - groupName: 'delete-empty related packages', + groupSlug: 'color', + groupName: 'color related packages', packageNames: [ - 'delete-empty', - '@types/delete-empty', + 'color', + '@types/color', ], }, { - groupSlug: 'elasticsearch', - groupName: 'elasticsearch related packages', + groupSlug: 'cpy', + groupName: 'cpy related packages', packageNames: [ - 'elasticsearch', - '@types/elasticsearch', + 'cpy', + '@types/cpy', ], }, { - groupSlug: 'fetch-mock', - groupName: 'fetch-mock related packages', + groupSlug: 'cytoscape', + groupName: 'cytoscape related packages', packageNames: [ - 'fetch-mock', - '@types/fetch-mock', + 'cytoscape', + '@types/cytoscape', ], }, { - groupSlug: 'getopts', - groupName: 'getopts related packages', - packageNames: [ - 'getopts', - '@types/getopts', + groupSlug: 'd3', + groupName: 'd3 related packages', + packagePatterns: [ + '(\\b|_)d3(\\b|_)', ], }, { - groupSlug: 'glob', - groupName: 'glob related packages', + groupSlug: 'dedent', + groupName: 'dedent related packages', packageNames: [ - 'glob', - '@types/glob', + 'dedent', + '@types/dedent', ], }, { - groupSlug: 'globby', - groupName: 'globby related packages', + groupSlug: 'delete-empty', + groupName: 'delete-empty related packages', packageNames: [ - 'globby', - '@types/globby', + 'delete-empty', + '@types/delete-empty', ], }, { - groupSlug: 'has-ansi', - groupName: 'has-ansi related packages', + groupSlug: 'dragselect', + groupName: 'dragselect related packages', packageNames: [ - 'has-ansi', - '@types/has-ansi', + 'dragselect', + '@types/dragselect', + ], + labels: [ + 'release_note:skip', + 'Team:Operations', + 'renovate', + 'v8.0.0', + 'v7.6.0', + ':ml', ], }, { - groupSlug: 'history', - groupName: 'history related packages', + groupSlug: 'elasticsearch', + groupName: 'elasticsearch related packages', packageNames: [ - 'history', - '@types/history', + 'elasticsearch', + '@types/elasticsearch', ], }, { - groupSlug: 'jquery', - groupName: 'jquery related packages', - packageNames: [ - 'jquery', - '@types/jquery', + groupSlug: 'eslint', + groupName: 'eslint related packages', + packagePatterns: [ + '(\\b|_)eslint(\\b|_)', ], }, { - groupSlug: 'js-yaml', - groupName: 'js-yaml related packages', + groupSlug: 'fancy-log', + groupName: 'fancy-log related packages', packageNames: [ - 'js-yaml', - '@types/js-yaml', + 'fancy-log', + '@types/fancy-log', ], }, { - groupSlug: 'json5', - groupName: 'json5 related packages', + groupSlug: 'fetch-mock', + groupName: 'fetch-mock related packages', packageNames: [ - 'json5', - '@types/json5', + 'fetch-mock', + '@types/fetch-mock', ], }, { - groupSlug: 'license-checker', - groupName: 'license-checker related packages', + groupSlug: 'file-saver', + groupName: 'file-saver related packages', packageNames: [ - 'license-checker', - '@types/license-checker', + 'file-saver', + '@types/file-saver', ], }, { - groupSlug: 'listr', - groupName: 'listr related packages', + groupSlug: 'getopts', + groupName: 'getopts related packages', packageNames: [ - 'listr', - '@types/listr', + 'getopts', + '@types/getopts', ], }, { - groupSlug: 'lodash', - groupName: 'lodash related packages', + groupSlug: 'getos', + groupName: 'getos related packages', packageNames: [ - 'lodash', - '@types/lodash', + 'getos', + '@types/getos', ], }, { - groupSlug: 'lru-cache', - groupName: 'lru-cache related packages', + groupSlug: 'git-url-parse', + groupName: 'git-url-parse related packages', packageNames: [ - 'lru-cache', - '@types/lru-cache', + 'git-url-parse', + '@types/git-url-parse', ], }, { - groupSlug: 'markdown-it', - groupName: 'markdown-it related packages', + groupSlug: 'glob', + groupName: 'glob related packages', packageNames: [ - 'markdown-it', - '@types/markdown-it', + 'glob', + '@types/glob', ], }, { - groupSlug: 'minimatch', - groupName: 'minimatch related packages', + groupSlug: 'globby', + groupName: 'globby related packages', packageNames: [ - 'minimatch', - '@types/minimatch', + 'globby', + '@types/globby', ], }, { - groupSlug: 'mustache', - groupName: 'mustache related packages', - packageNames: [ - 'mustache', - '@types/mustache', + groupSlug: 'graphql', + groupName: 'graphql related packages', + packagePatterns: [ + '(\\b|_)graphql(\\b|_)', + '(\\b|_)apollo(\\b|_)', ], }, { - groupSlug: 'node', - groupName: 'node related packages', - packageNames: [ - 'node', - '@types/node', + groupSlug: 'grunt', + groupName: 'grunt related packages', + packagePatterns: [ + '(\\b|_)grunt(\\b|_)', ], }, { - groupSlug: 'opn', - groupName: 'opn related packages', + groupSlug: 'gulp', + groupName: 'gulp related packages', + packagePatterns: [ + '(\\b|_)gulp(\\b|_)', + ], + }, + { + groupSlug: 'hapi', + groupName: 'hapi related packages', + packagePatterns: [ + '(\\b|_)hapi(\\b|_)', + ], packageNames: [ - 'opn', - '@types/opn', + 'hapi', + '@types/hapi', + 'joi', + '@types/joi', + 'boom', + '@types/boom', + 'hoek', + '@types/hoek', + 'h2o2', + '@types/h2o2', + '@elastic/good', + '@types/elastic__good', + 'good-squeeze', + '@types/good-squeeze', + 'inert', + '@types/inert', ], }, { - groupSlug: 'pngjs', - groupName: 'pngjs related packages', + groupSlug: 'has-ansi', + groupName: 'has-ansi related packages', packageNames: [ - 'pngjs', - '@types/pngjs', + 'has-ansi', + '@types/has-ansi', ], }, { - groupSlug: 'podium', - groupName: 'podium related packages', + groupSlug: 'history', + groupName: 'history related packages', packageNames: [ - 'podium', - '@types/podium', + 'history', + '@types/history', ], }, { - groupSlug: '@reach/router', - groupName: '@reach/router related packages', + groupSlug: 'indent-string', + groupName: 'indent-string related packages', packageNames: [ - '@reach/router', - '@types/reach__router', + 'indent-string', + '@types/indent-string', ], }, { - groupSlug: 'request', - groupName: 'request related packages', + groupSlug: 'intl-relativeformat', + groupName: 'intl-relativeformat related packages', packageNames: [ - 'request', - '@types/request', + 'intl-relativeformat', + '@types/intl-relativeformat', ], }, { - groupSlug: 'selenium-webdriver', - groupName: 'selenium-webdriver related packages', - packageNames: [ - 'selenium-webdriver', - '@types/selenium-webdriver', + groupSlug: 'jest', + groupName: 'jest related packages', + packagePatterns: [ + '(\\b|_)jest(\\b|_)', ], }, { - groupSlug: 'semver', - groupName: 'semver related packages', + groupSlug: 'jquery', + groupName: 'jquery related packages', packageNames: [ - 'semver', - '@types/semver', + 'jquery', + '@types/jquery', ], }, { - groupSlug: 'sinon', - groupName: 'sinon related packages', + groupSlug: 'js-yaml', + groupName: 'js-yaml related packages', packageNames: [ - 'sinon', - '@types/sinon', + 'js-yaml', + '@types/js-yaml', ], }, { - groupSlug: 'strip-ansi', - groupName: 'strip-ansi related packages', + groupSlug: 'jsdom', + groupName: 'jsdom related packages', packageNames: [ - 'strip-ansi', - '@types/strip-ansi', + 'jsdom', + '@types/jsdom', ], }, { - groupSlug: 'styled-components', - groupName: 'styled-components related packages', + groupSlug: 'json-stable-stringify', + groupName: 'json-stable-stringify related packages', packageNames: [ - 'styled-components', - '@types/styled-components', + 'json-stable-stringify', + '@types/json-stable-stringify', ], }, { - groupSlug: 'supertest', - groupName: 'supertest related packages', + groupSlug: 'json5', + groupName: 'json5 related packages', packageNames: [ - 'supertest', - '@types/supertest', + 'json5', + '@types/json5', ], }, { - groupSlug: 'supertest-as-promised', - groupName: 'supertest-as-promised related packages', + groupSlug: 'jsonwebtoken', + groupName: 'jsonwebtoken related packages', packageNames: [ - 'supertest-as-promised', - '@types/supertest-as-promised', + 'jsonwebtoken', + '@types/jsonwebtoken', ], }, { - groupSlug: 'type-detect', - groupName: 'type-detect related packages', + groupSlug: 'jsts', + groupName: 'jsts related packages', packageNames: [ - 'type-detect', - '@types/type-detect', + 'jsts', + '@types/jsts', ], + allowedVersions: '^1.6.2', }, { - groupSlug: 'uuid', - groupName: 'uuid related packages', - packageNames: [ - 'uuid', - '@types/uuid', + groupSlug: 'karma', + groupName: 'karma related packages', + packagePatterns: [ + '(\\b|_)karma(\\b|_)', ], }, { - groupSlug: 'vinyl-fs', - groupName: 'vinyl-fs related packages', + groupSlug: 'language server', + groupName: 'language server related packages', packageNames: [ - 'vinyl-fs', - '@types/vinyl-fs', + 'vscode-jsonrpc', + '@types/vscode-jsonrpc', + 'vscode-languageserver', + '@types/vscode-languageserver', + 'vscode-languageserver-types', + '@types/vscode-languageserver-types', ], }, { - groupSlug: 'zen-observable', - groupName: 'zen-observable related packages', + groupSlug: 'license-checker', + groupName: 'license-checker related packages', packageNames: [ - 'zen-observable', - '@types/zen-observable', + 'license-checker', + '@types/license-checker', ], }, { - groupSlug: 'archiver', - groupName: 'archiver related packages', + groupSlug: 'listr', + groupName: 'listr related packages', packageNames: [ - 'archiver', - '@types/archiver', + 'listr', + '@types/listr', ], }, { - groupSlug: 'base64-js', - groupName: 'base64-js related packages', + groupSlug: 'lodash', + groupName: 'lodash related packages', packageNames: [ - 'base64-js', - '@types/base64-js', + 'lodash', + '@types/lodash', ], }, { - groupSlug: 'chroma-js', - groupName: 'chroma-js related packages', + groupSlug: 'lodash.clonedeep', + groupName: 'lodash.clonedeep related packages', packageNames: [ - 'chroma-js', - '@types/chroma-js', + 'lodash.clonedeep', + '@types/lodash.clonedeep', ], }, { - groupSlug: 'color', - groupName: 'color related packages', + groupSlug: 'lodash.clonedeepwith', + groupName: 'lodash.clonedeepwith related packages', packageNames: [ - 'color', - '@types/color', + 'lodash.clonedeepwith', + '@types/lodash.clonedeepwith', ], }, { - groupSlug: 'cytoscape', - groupName: 'cytoscape related packages', + groupSlug: 'log-symbols', + groupName: 'log-symbols related packages', packageNames: [ - 'cytoscape', - '@types/cytoscape', + 'log-symbols', + '@types/log-symbols', ], }, { - groupSlug: 'fancy-log', - groupName: 'fancy-log related packages', + groupSlug: 'lru-cache', + groupName: 'lru-cache related packages', packageNames: [ - 'fancy-log', - '@types/fancy-log', + 'lru-cache', + '@types/lru-cache', ], }, { - groupSlug: 'file-saver', - groupName: 'file-saver related packages', + groupSlug: 'mapbox-gl', + groupName: 'mapbox-gl related packages', packageNames: [ - 'file-saver', - '@types/file-saver', + 'mapbox-gl', + '@types/mapbox-gl', ], }, { - groupSlug: 'getos', - groupName: 'getos related packages', + groupSlug: 'markdown-it', + groupName: 'markdown-it related packages', packageNames: [ - 'getos', - '@types/getos', + 'markdown-it', + '@types/markdown-it', ], }, { - groupSlug: 'git-url-parse', - groupName: 'git-url-parse related packages', + groupSlug: 'memoize-one', + groupName: 'memoize-one related packages', packageNames: [ - 'git-url-parse', - '@types/git-url-parse', + 'memoize-one', + '@types/memoize-one', ], }, { - groupSlug: 'jsdom', - groupName: 'jsdom related packages', + groupSlug: 'mime', + groupName: 'mime related packages', packageNames: [ - 'jsdom', - '@types/jsdom', + 'mime', + '@types/mime', ], }, { - groupSlug: 'jsonwebtoken', - groupName: 'jsonwebtoken related packages', + groupSlug: 'minimatch', + groupName: 'minimatch related packages', packageNames: [ - 'jsonwebtoken', - '@types/jsonwebtoken', + 'minimatch', + '@types/minimatch', ], }, { - groupSlug: 'mapbox-gl', - groupName: 'mapbox-gl related packages', - packageNames: [ - 'mapbox-gl', - '@types/mapbox-gl', + groupSlug: 'mocha', + groupName: 'mocha related packages', + packagePatterns: [ + '(\\b|_)mocha(\\b|_)', ], }, { - groupSlug: 'memoize-one', - groupName: 'memoize-one related packages', + groupSlug: 'moment', + groupName: 'moment related packages', + packagePatterns: [ + '(\\b|_)moment(\\b|_)', + ], + }, + { + groupSlug: 'mustache', + groupName: 'mustache related packages', packageNames: [ - 'memoize-one', - '@types/memoize-one', + 'mustache', + '@types/mustache', ], }, { - groupSlug: 'mime', - groupName: 'mime related packages', + groupSlug: 'ncp', + groupName: 'ncp related packages', packageNames: [ - 'mime', - '@types/mime', + 'ncp', + '@types/ncp', ], }, { @@ -745,6 +609,14 @@ '@types/nock', ], }, + { + groupSlug: 'node', + groupName: 'node related packages', + packageNames: [ + 'node', + '@types/node', + ], + }, { groupSlug: 'node-fetch', groupName: 'node-fetch related packages', @@ -769,6 +641,22 @@ '@types/object-hash', ], }, + { + groupSlug: 'opn', + groupName: 'opn related packages', + packageNames: [ + 'opn', + '@types/opn', + ], + }, + { + groupSlug: 'ora', + groupName: 'ora related packages', + packageNames: [ + 'ora', + '@types/ora', + ], + }, { groupSlug: 'papaparse', groupName: 'papaparse related packages', @@ -777,6 +665,30 @@ '@types/papaparse', ], }, + { + groupSlug: 'parse-link-header', + groupName: 'parse-link-header related packages', + packageNames: [ + 'parse-link-header', + '@types/parse-link-header', + ], + }, + { + groupSlug: 'pngjs', + groupName: 'pngjs related packages', + packageNames: [ + 'pngjs', + '@types/pngjs', + ], + }, + { + groupSlug: 'podium', + groupName: 'podium related packages', + packageNames: [ + 'podium', + '@types/podium', + ], + }, { groupSlug: 'proper-lockfile', groupName: 'proper-lockfile related packages', @@ -793,6 +705,35 @@ '@types/puppeteer', ], }, + { + groupSlug: 'react', + groupName: 'react related packages', + packagePatterns: [ + '(\\b|_)react(\\b|_)', + '(\\b|_)redux(\\b|_)', + '(\\b|_)enzyme(\\b|_)', + ], + packageNames: [ + 'ngreact', + '@types/ngreact', + 'recompose', + '@types/recompose', + 'prop-types', + '@types/prop-types', + 'typescript-fsa-reducers', + '@types/typescript-fsa-reducers', + 'reselect', + '@types/reselect', + ], + }, + { + groupSlug: 'read-pkg', + groupName: 'read-pkg related packages', + packageNames: [ + 'read-pkg', + '@types/read-pkg', + ], + }, { groupSlug: 'reduce-reducers', groupName: 'reduce-reducers related packages', @@ -802,123 +743,166 @@ ], }, { - groupSlug: 'tar-fs', - groupName: 'tar-fs related packages', + groupSlug: 'request', + groupName: 'request related packages', packageNames: [ - 'tar-fs', - '@types/tar-fs', + 'request', + '@types/request', ], }, { - groupSlug: 'tinycolor2', - groupName: 'tinycolor2 related packages', + groupSlug: 'selenium-webdriver', + groupName: 'selenium-webdriver related packages', packageNames: [ - 'tinycolor2', - '@types/tinycolor2', + 'selenium-webdriver', + '@types/selenium-webdriver', ], }, { - groupSlug: 'xml-crypto', - groupName: 'xml-crypto related packages', + groupSlug: 'semver', + groupName: 'semver related packages', packageNames: [ - 'xml-crypto', - '@types/xml-crypto', + 'semver', + '@types/semver', ], }, { - groupSlug: 'xml2js', - groupName: 'xml2js related packages', + groupSlug: 'sinon', + groupName: 'sinon related packages', packageNames: [ - 'xml2js', - '@types/xml2js', + 'sinon', + '@types/sinon', ], }, { - groupSlug: 'intl-relativeformat', - groupName: 'intl-relativeformat related packages', + groupSlug: 'storybook', + groupName: 'storybook related packages', + packagePatterns: [ + '(\\b|_)storybook(\\b|_)', + ], + }, + { + groupSlug: 'strip-ansi', + groupName: 'strip-ansi related packages', packageNames: [ - 'intl-relativeformat', - '@types/intl-relativeformat', + 'strip-ansi', + '@types/strip-ansi', ], }, { - groupSlug: 'cmd-shim', - groupName: 'cmd-shim related packages', + groupSlug: 'strong-log-transformer', + groupName: 'strong-log-transformer related packages', packageNames: [ - 'cmd-shim', - '@types/cmd-shim', + 'strong-log-transformer', + '@types/strong-log-transformer', ], }, { - groupSlug: 'cpy', - groupName: 'cpy related packages', + groupSlug: 'styled-components', + groupName: 'styled-components related packages', packageNames: [ - 'cpy', - '@types/cpy', + 'styled-components', + '@types/styled-components', ], }, { - groupSlug: 'indent-string', - groupName: 'indent-string related packages', + groupSlug: 'supertest', + groupName: 'supertest related packages', packageNames: [ - 'indent-string', - '@types/indent-string', + 'supertest', + '@types/supertest', ], }, { - groupSlug: 'lodash.clonedeepwith', - groupName: 'lodash.clonedeepwith related packages', + groupSlug: 'supertest-as-promised', + groupName: 'supertest-as-promised related packages', packageNames: [ - 'lodash.clonedeepwith', - '@types/lodash.clonedeepwith', + 'supertest-as-promised', + '@types/supertest-as-promised', ], }, { - groupSlug: 'log-symbols', - groupName: 'log-symbols related packages', + groupSlug: 'tar-fs', + groupName: 'tar-fs related packages', packageNames: [ - 'log-symbols', - '@types/log-symbols', + 'tar-fs', + '@types/tar-fs', ], }, { - groupSlug: 'ncp', - groupName: 'ncp related packages', + groupSlug: 'tempy', + groupName: 'tempy related packages', packageNames: [ - 'ncp', - '@types/ncp', + 'tempy', + '@types/tempy', ], }, { - groupSlug: 'ora', - groupName: 'ora related packages', + groupSlug: 'tinycolor2', + groupName: 'tinycolor2 related packages', packageNames: [ - 'ora', - '@types/ora', + 'tinycolor2', + '@types/tinycolor2', ], }, { - groupSlug: 'read-pkg', - groupName: 'read-pkg related packages', + groupSlug: 'type-detect', + groupName: 'type-detect related packages', packageNames: [ - 'read-pkg', - '@types/read-pkg', + 'type-detect', + '@types/type-detect', ], }, { - groupSlug: 'strong-log-transformer', - groupName: 'strong-log-transformer related packages', + groupSlug: 'typescript', + groupName: 'typescript related packages', + packagePatterns: [ + '(\\b|_)ts(\\b|_)', + '(\\b|_)typescript(\\b|_)', + ], packageNames: [ - 'strong-log-transformer', - '@types/strong-log-transformer', + 'tslib', + '@types/tslib', ], }, { - groupSlug: 'tempy', - groupName: 'tempy related packages', + groupSlug: 'uuid', + groupName: 'uuid related packages', packageNames: [ - 'tempy', - '@types/tempy', + 'uuid', + '@types/uuid', + ], + }, + { + groupSlug: 'vega', + groupName: 'vega related packages', + packagePatterns: [ + '(\\b|_)vega(\\b|_)', + ], + enabled: false, + }, + { + groupSlug: 'vinyl-fs', + groupName: 'vinyl-fs related packages', + packageNames: [ + 'vinyl-fs', + '@types/vinyl-fs', + ], + }, + { + groupSlug: 'webpack', + groupName: 'webpack related packages', + packagePatterns: [ + '(\\b|_)webpack(\\b|_)', + '(\\b|_)loader(\\b|_)', + '(\\b|_)acorn(\\b|_)', + '(\\b|_)terser(\\b|_)', + ], + packageNames: [ + 'mini-css-extract-plugin', + '@types/mini-css-extract-plugin', + 'chokidar', + '@types/chokidar', ], }, { @@ -938,11 +922,27 @@ ], }, { - groupSlug: 'parse-link-header', - groupName: 'parse-link-header related packages', + groupSlug: 'xml-crypto', + groupName: 'xml-crypto related packages', packageNames: [ - 'parse-link-header', - '@types/parse-link-header', + 'xml-crypto', + '@types/xml-crypto', + ], + }, + { + groupSlug: 'xml2js', + groupName: 'xml2js related packages', + packageNames: [ + 'xml2js', + '@types/xml2js', + ], + }, + { + groupSlug: 'zen-observable', + groupName: 'zen-observable related packages', + packageNames: [ + 'zen-observable', + '@types/zen-observable', ], }, { diff --git a/scripts/functional_tests.js b/scripts/functional_tests.js index 4472891e580fb9..fc88f2657018f0 100644 --- a/scripts/functional_tests.js +++ b/scripts/functional_tests.js @@ -17,12 +17,23 @@ * under the License. */ -require('../src/setup_node_env'); -require('@kbn/test').runTestsCli([ +// eslint-disable-next-line no-restricted-syntax +const alwaysImportedTests = [ require.resolve('../test/functional/config.js'), - require.resolve('../test/api_integration/config.js'), require.resolve('../test/plugin_functional/config.js'), - require.resolve('../test/interpreter_functional/config.ts'), require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'), +]; +// eslint-disable-next-line no-restricted-syntax +const onlyNotInCoverageTests = [ + require.resolve('../test/api_integration/config.js'), + require.resolve('../test/interpreter_functional/config.ts'), require.resolve('../test/examples/config.js'), +]; + +require('../src/setup_node_env'); +require('@kbn/test').runTestsCli([ + // eslint-disable-next-line no-restricted-syntax + ...alwaysImportedTests, + // eslint-disable-next-line no-restricted-syntax + ...(!!process.env.CODE_COVERAGE ? [] : onlyNotInCoverageTests), ]); diff --git a/src/core/MIGRATION.md b/src/core/MIGRATION.md index 1c78de966c46f7..b70ac610f24a79 100644 --- a/src/core/MIGRATION.md +++ b/src/core/MIGRATION.md @@ -1194,6 +1194,7 @@ In server code, `core` can be accessed from either `server.newPlatform` or `kbnS | `request.getBasePath()` | [`core.http.basePath.get`](/docs/development/core/server/kibana-plugin-server.httpservicesetup.basepath.md) | | | `server.plugins.elasticsearch.getCluster('data')` | [`context.elasticsearch.dataClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | | `server.plugins.elasticsearch.getCluster('admin')` | [`context.elasticsearch.adminClient`](/docs/development/core/server/kibana-plugin-server.iscopedclusterclient.md) | | +| `server.plugins.elasticsearch.createCluster(...)` | [`core.elasticsearch.createClient`](/docs/development/core/server/kibana-plugin-server.elasticsearchservicesetup.createclient.md) | | | `server.savedObjects.setScopedSavedObjectsClientFactory` | [`core.savedObjects.setClientFactory`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.setclientfactory.md) | | | `server.savedObjects.addScopedSavedObjectsClientWrapperFactory` | [`core.savedObjects.addClientWrapper`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.addclientwrapper.md) | | | `server.savedObjects.getSavedObjectsRepository` | [`core.savedObjects.createInternalRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createinternalrepository.md) [`core.savedObjects.createScopedRepository`](/docs/development/core/server/kibana-plugin-server.savedobjectsservicesetup.createscopedrepository.md) | | diff --git a/src/core/server/elasticsearch/cluster_client.ts b/src/core/server/elasticsearch/cluster_client.ts index d43ab9d546ed2e..2352677b8d3e0a 100644 --- a/src/core/server/elasticsearch/cluster_client.ts +++ b/src/core/server/elasticsearch/cluster_client.ts @@ -89,15 +89,35 @@ export interface FakeRequest { } /** - * Represents an Elasticsearch cluster API client and allows to call API on behalf - * of the internal Kibana user and the actual user that is derived from the request - * headers (via `asScoped(...)`). + * Represents an Elasticsearch cluster API client created by the platform. + * It allows to call API on behalf of the internal Kibana user and + * the actual user that is derived from the request headers (via `asScoped(...)`). * * See {@link ClusterClient}. * * @public */ -export type IClusterClient = Pick; +export type IClusterClient = Pick; + +/** + * Represents an Elasticsearch cluster API client created by a plugin. + * It allows to call API on behalf of the internal Kibana user and + * the actual user that is derived from the request headers (via `asScoped(...)`). + * + * See {@link ClusterClient}. + * + * @public + */ +export type ICustomClusterClient = Pick; + +/** + A user credentials container. + * It accommodates the necessary auth credentials to impersonate the current user. + * + * @public + * See {@link KibanaRequest}. + */ +export type ScopeableRequest = KibanaRequest | LegacyRequest | FakeRequest; /** * {@inheritDoc IClusterClient} @@ -174,7 +194,7 @@ export class ClusterClient implements IClusterClient { * @param request - Request the `IScopedClusterClient` instance will be scoped to. * Supports request optionality, Legacy.Request & FakeRequest for BWC with LegacyPlatform */ - public asScoped(request?: KibanaRequest | LegacyRequest | FakeRequest): IScopedClusterClient { + public asScoped(request?: ScopeableRequest): IScopedClusterClient { // It'd have been quite expensive to create and configure client for every incoming // request since it involves parsing of the config, reading of the SSL certificate and // key files etc. Moreover scoped client needs two Elasticsearch JS clients at the same diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index d935d1a66eccfd..1b52f22c4da09a 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -18,33 +18,67 @@ */ import { BehaviorSubject } from 'rxjs'; -import { IClusterClient } from './cluster_client'; +import { IClusterClient, ICustomClusterClient } from './cluster_client'; import { IScopedClusterClient } from './scoped_cluster_client'; import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchService } from './elasticsearch_service'; -import { InternalElasticsearchServiceSetup } from './types'; +import { InternalElasticsearchServiceSetup, ElasticsearchServiceSetup } from './types'; const createScopedClusterClientMock = (): jest.Mocked => ({ callAsInternalUser: jest.fn(), callAsCurrentUser: jest.fn(), }); -const createClusterClientMock = (): jest.Mocked => ({ - callAsInternalUser: jest.fn(), - asScoped: jest.fn().mockImplementation(createScopedClusterClientMock), +const createCustomClusterClientMock = (): jest.Mocked => ({ + ...createClusterClientMock(), close: jest.fn(), }); +function createClusterClientMock() { + const client: jest.Mocked = { + callAsInternalUser: jest.fn(), + asScoped: jest.fn(), + }; + client.asScoped.mockReturnValue(createScopedClusterClientMock()); + return client; +} + +type MockedElasticSearchServiceSetup = jest.Mocked< + ElasticsearchServiceSetup & { + adminClient: jest.Mocked; + dataClient: jest.Mocked; + } +>; + const createSetupContractMock = () => { - const setupContract: jest.Mocked = { + const setupContract: MockedElasticSearchServiceSetup = { + createClient: jest.fn(), + adminClient: createClusterClientMock(), + dataClient: createClusterClientMock(), + }; + setupContract.createClient.mockReturnValue(createCustomClusterClientMock()); + setupContract.adminClient.asScoped.mockReturnValue(createScopedClusterClientMock()); + setupContract.dataClient.asScoped.mockReturnValue(createScopedClusterClientMock()); + return setupContract; +}; + +type MockedInternalElasticSearchServiceSetup = jest.Mocked< + InternalElasticsearchServiceSetup & { + adminClient: jest.Mocked; + dataClient: jest.Mocked; + } +>; +const createInternalSetupContractMock = () => { + const setupContract: MockedInternalElasticSearchServiceSetup = { + ...createSetupContractMock(), legacy: { config$: new BehaviorSubject({} as ElasticsearchConfig), }, - - createClient: jest.fn().mockImplementation(createClusterClientMock), - adminClient$: new BehaviorSubject((createClusterClientMock() as unknown) as IClusterClient), - dataClient$: new BehaviorSubject((createClusterClientMock() as unknown) as IClusterClient), + adminClient$: new BehaviorSubject(createClusterClientMock()), + dataClient$: new BehaviorSubject(createClusterClientMock()), }; + setupContract.adminClient.asScoped.mockReturnValue(createScopedClusterClientMock()); + setupContract.dataClient.asScoped.mockReturnValue(createScopedClusterClientMock()); return setupContract; }; @@ -55,14 +89,16 @@ const createMock = () => { start: jest.fn(), stop: jest.fn(), }; - mocked.setup.mockResolvedValue(createSetupContractMock()); + mocked.setup.mockResolvedValue(createInternalSetupContractMock()); mocked.stop.mockResolvedValue(); return mocked; }; export const elasticsearchServiceMock = { create: createMock, - createSetupContract: createSetupContractMock, + createInternalSetup: createInternalSetupContractMock, + createSetup: createSetupContractMock, createClusterClient: createClusterClientMock, + createCustomClusterClient: createCustomClusterClientMock, createScopedClusterClient: createScopedClusterClientMock, }; diff --git a/src/core/server/elasticsearch/elasticsearch_service.test.ts b/src/core/server/elasticsearch/elasticsearch_service.test.ts index 6c4a1f263bc713..aefc8b1fdf047e 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.test.ts @@ -30,6 +30,7 @@ import { loggingServiceMock } from '../logging/logging_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchService } from './elasticsearch_service'; +import { elasticsearchServiceMock } from './elasticsearch_service.mock'; let elasticsearchService: ElasticsearchService; const configService = configServiceMock.create(); @@ -69,6 +70,27 @@ describe('#setup', () => { ); }); + it('returns data and admin client as a part of the contract', async () => { + const mockAdminClusterClientInstance = elasticsearchServiceMock.createClusterClient(); + const mockDataClusterClientInstance = elasticsearchServiceMock.createClusterClient(); + MockClusterClient.mockImplementationOnce( + () => mockAdminClusterClientInstance + ).mockImplementationOnce(() => mockDataClusterClientInstance); + + const setupContract = await elasticsearchService.setup(deps); + + const adminClient = setupContract.adminClient; + const dataClient = setupContract.dataClient; + + expect(mockAdminClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(0); + await adminClient.callAsInternalUser('any'); + expect(mockAdminClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1); + + expect(mockDataClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(0); + await dataClient.callAsInternalUser('any'); + expect(mockDataClusterClientInstance.callAsInternalUser).toHaveBeenCalledTimes(1); + }); + it('returns data and admin client observables as a part of the contract', async () => { const mockAdminClusterClientInstance = { close: jest.fn() }; const mockDataClusterClientInstance = { close: jest.fn() }; diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index be0a817c54146c..de32e7f6cf2258 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -18,17 +18,18 @@ */ import { ConnectableObservable, Observable, Subscription } from 'rxjs'; -import { filter, first, map, publishReplay, switchMap } from 'rxjs/operators'; +import { filter, first, map, publishReplay, switchMap, take } from 'rxjs/operators'; import { CoreService } from '../../types'; import { merge } from '../../utils'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; -import { ClusterClient } from './cluster_client'; +import { ClusterClient, ScopeableRequest } from './cluster_client'; import { ElasticsearchClientConfig } from './elasticsearch_client_config'; import { ElasticsearchConfig, ElasticsearchConfigType } from './elasticsearch_config'; import { InternalHttpServiceSetup, GetAuthHeaders } from '../http/'; import { InternalElasticsearchServiceSetup } from './types'; +import { CallAPIOptions } from './api_types'; /** @internal */ interface CoreClusterClients { @@ -94,11 +95,67 @@ export class ElasticsearchService implements CoreService clients.adminClient)); + const dataClient$ = clients$.pipe(map(clients => clients.dataClient)); + + const adminClient = { + async callAsInternalUser( + endpoint: string, + clientParams: Record = {}, + options?: CallAPIOptions + ) { + const client = await adminClient$.pipe(take(1)).toPromise(); + return await client.callAsInternalUser(endpoint, clientParams, options); + }, + asScoped(request: ScopeableRequest) { + return { + callAsInternalUser: adminClient.callAsInternalUser, + async callAsCurrentUser( + endpoint: string, + clientParams: Record = {}, + options?: CallAPIOptions + ) { + const client = await adminClient$.pipe(take(1)).toPromise(); + return await client + .asScoped(request) + .callAsCurrentUser(endpoint, clientParams, options); + }, + }; + }, + }; + const dataClient = { + async callAsInternalUser( + endpoint: string, + clientParams: Record = {}, + options?: CallAPIOptions + ) { + const client = await dataClient$.pipe(take(1)).toPromise(); + return await client.callAsInternalUser(endpoint, clientParams, options); + }, + asScoped(request: ScopeableRequest) { + return { + callAsInternalUser: dataClient.callAsInternalUser, + async callAsCurrentUser( + endpoint: string, + clientParams: Record = {}, + options?: CallAPIOptions + ) { + const client = await dataClient$.pipe(take(1)).toPromise(); + return await client + .asScoped(request) + .callAsCurrentUser(endpoint, clientParams, options); + }, + }; + }, + }; + return { legacy: { config$: clients$.pipe(map(clients => clients.config)) }, - adminClient$: clients$.pipe(map(clients => clients.adminClient)), - dataClient$: clients$.pipe(map(clients => clients.dataClient)), + adminClient$, + dataClient$, + adminClient, + dataClient, createClient: (type: string, clientConfig: Partial = {}) => { const finalConfig = merge({}, config, clientConfig); diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts index 1f99f86d9887bb..5d64fadfaa1842 100644 --- a/src/core/server/elasticsearch/index.ts +++ b/src/core/server/elasticsearch/index.ts @@ -18,7 +18,13 @@ */ export { ElasticsearchService } from './elasticsearch_service'; -export { IClusterClient, ClusterClient, FakeRequest } from './cluster_client'; +export { + ClusterClient, + FakeRequest, + IClusterClient, + ICustomClusterClient, + ScopeableRequest, +} from './cluster_client'; export { IScopedClusterClient, ScopedClusterClient, Headers } from './scoped_cluster_client'; export { ElasticsearchClientConfig } from './elasticsearch_client_config'; export { config } from './elasticsearch_config'; diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts index 505b57c7c9e8ec..22340bf3f2fc62 100644 --- a/src/core/server/elasticsearch/types.ts +++ b/src/core/server/elasticsearch/types.ts @@ -20,7 +20,7 @@ import { Observable } from 'rxjs'; import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchClientConfig } from './elasticsearch_client_config'; -import { IClusterClient } from './cluster_client'; +import { IClusterClient, ICustomClusterClient } from './cluster_client'; /** * @public @@ -46,29 +46,29 @@ export interface ElasticsearchServiceSetup { readonly createClient: ( type: string, clientConfig?: Partial - ) => IClusterClient; + ) => ICustomClusterClient; /** - * Observable of clients for the `admin` cluster. Observable emits when Elasticsearch config changes on the Kibana - * server. See {@link IClusterClient}. + * A client for the `admin` cluster. All Elasticsearch config value changes are processed under the hood. + * See {@link IClusterClient}. * - * @exmaple + * @example * ```js - * const client = await elasticsearch.adminClient$.pipe(take(1)).toPromise(); + * const client = core.elasticsearch.adminClient; * ``` */ - readonly adminClient$: Observable; + readonly adminClient: IClusterClient; /** - * Observable of clients for the `data` cluster. Observable emits when Elasticsearch config changes on the Kibana - * server. See {@link IClusterClient}. + * A client for the `data` cluster. All Elasticsearch config value changes are processed under the hood. + * See {@link IClusterClient}. * - * @exmaple + * @example * ```js - * const client = await elasticsearch.dataClient$.pipe(take(1)).toPromise(); + * const client = core.elasticsearch.dataClient; * ``` */ - readonly dataClient$: Observable; + readonly dataClient: IClusterClient; } /** @internal */ @@ -77,4 +77,7 @@ export interface InternalElasticsearchServiceSetup extends ElasticsearchServiceS readonly legacy: { readonly config$: Observable; }; + + readonly adminClient$: Observable; + readonly dataClient$: Observable; } diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 953fa0738597c0..eccf3985fc495a 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -74,6 +74,7 @@ export { CspConfig, ICspConfig } from './csp'; export { ClusterClient, IClusterClient, + ICustomClusterClient, Headers, ScopedClusterClient, IScopedClusterClient, @@ -83,6 +84,7 @@ export { ElasticsearchServiceSetup, APICaller, FakeRequest, + ScopeableRequest, } from './elasticsearch'; export * from './elasticsearch/api_types'; export { diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index e17de7364ce59c..cc36b90ec526d7 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -249,8 +249,8 @@ export class LegacyService implements CoreService { capabilities: setupDeps.core.capabilities, context: setupDeps.core.context, elasticsearch: { - adminClient$: setupDeps.core.elasticsearch.adminClient$, - dataClient$: setupDeps.core.elasticsearch.dataClient$, + adminClient: setupDeps.core.elasticsearch.adminClient, + dataClient: setupDeps.core.elasticsearch.dataClient, createClient: setupDeps.core.elasticsearch.createClient, }, http: { diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 53849b040c413c..073d380d3aa67b 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -107,7 +107,7 @@ function createCoreSetupMock() { const mock: MockedKeys = { capabilities: capabilitiesServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), - elasticsearch: elasticsearchServiceMock.createSetupContract(), + elasticsearch: elasticsearchServiceMock.createSetup(), http: httpMock, savedObjects: savedObjectsServiceMock.createSetupContract(), uiSettings: uiSettingsMock, @@ -131,7 +131,7 @@ function createInternalCoreSetupMock() { const setupDeps: InternalCoreSetup = { capabilities: capabilitiesServiceMock.createSetupContract(), context: contextServiceMock.createSetupContract(), - elasticsearch: elasticsearchServiceMock.createSetupContract(), + elasticsearch: elasticsearchServiceMock.createInternalSetup(), http: httpServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), savedObjects: savedObjectsServiceMock.createSetupContract(), diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 6e9a7967e9ecaf..6d82a8d3ec6cf1 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -145,8 +145,8 @@ export function createPluginSetupContext( createContextContainer: deps.context.createContextContainer, }, elasticsearch: { - adminClient$: deps.elasticsearch.adminClient$, - dataClient$: deps.elasticsearch.dataClient$, + adminClient: deps.elasticsearch.adminClient, + dataClient: deps.elasticsearch.dataClient, createClient: deps.elasticsearch.createClient, }, http: { diff --git a/src/core/server/rendering/views/styles.tsx b/src/core/server/rendering/views/styles.tsx index f41627bcfe07ff..dfcb4213d90f71 100644 --- a/src/core/server/rendering/views/styles.tsx +++ b/src/core/server/rendering/views/styles.tsx @@ -28,8 +28,6 @@ interface Props { } export const Styles: FunctionComponent = ({ darkMode }) => { - const themeBackground = darkMode ? '#25262e' : '#f5f7fa'; - return ( \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js index 0909b6947895cf..f7e654fd3c76dd 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/_view.js @@ -29,7 +29,7 @@ import { uiModules } from 'ui/modules'; import { fatalError, toastNotifications } from 'ui/notify'; import 'ui/accessibility/kbn_ui_ace_keyboard_mode'; import { SavedObjectsClientProvider } from 'ui/saved_objects'; -import { isNumeric } from 'ui/utils/numeric'; +import { isNumeric } from './lib/numeric'; import { canViewInApp } from './lib/in_app_url'; import { castEsToKbnFieldTypeName } from '../../../../../../../plugins/data/public'; diff --git a/src/legacy/ui/public/utils/sort_prefix_first.ts b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.test.ts similarity index 62% rename from src/legacy/ui/public/utils/sort_prefix_first.ts rename to src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.test.ts index 4d1a8d7f398668..bb749de8dcb715 100644 --- a/src/legacy/ui/public/utils/sort_prefix_first.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.test.ts @@ -17,17 +17,20 @@ * under the License. */ -import { partition } from 'lodash'; +import { keysToCamelCaseShallow } from './case_conversion'; -export function sortPrefixFirst(array: any[], prefix?: string | number, property?: string): any[] { - if (!prefix) { - return array; - } - const lowerCasePrefix = ('' + prefix).toLowerCase(); +describe('keysToCamelCaseShallow', () => { + test("should convert all of an object's keys to camel case", () => { + const data = { + camelCase: 'camelCase', + 'kebab-case': 'kebabCase', + snake_case: 'snakeCase', + }; - const partitions = partition(array, entry => { - const value = ('' + (property ? entry[property] : entry)).toLowerCase(); - return value.startsWith(lowerCasePrefix); + const result = keysToCamelCaseShallow(data); + + expect(result.camelCase).toBe('camelCase'); + expect(result.kebabCase).toBe('kebabCase'); + expect(result.snakeCase).toBe('snakeCase'); }); - return [...partitions[0], ...partitions[1]]; -} +}); diff --git a/src/legacy/core_plugins/kbn_doc_views/index.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.ts similarity index 82% rename from src/legacy/core_plugins/kbn_doc_views/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.ts index 9078a82d633811..718530eb3b6022 100644 --- a/src/legacy/core_plugins/kbn_doc_views/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/case_conversion.ts @@ -17,10 +17,8 @@ * under the License. */ -export default function(kibana) { - return new kibana.Plugin({ - uiExports: { - docViews: ['plugins/kbn_doc_views/kbn_doc_views'], - }, - }); +import { mapKeys, camelCase } from 'lodash'; + +export function keysToCamelCaseShallow(object: Record) { + return mapKeys(object, (value, key) => camelCase(key)); } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.js index f982966d1e3145..caf2b5f5034405 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/find_objects.js @@ -18,7 +18,7 @@ */ import { kfetch } from 'ui/kfetch'; -import { keysToCamelCaseShallow } from 'ui/utils/case_conversion'; +import { keysToCamelCaseShallow } from './case_conversion'; export async function findObjects(findOptions) { const response = await kfetch({ @@ -26,5 +26,6 @@ export async function findObjects(findOptions) { pathname: '/api/kibana/management/saved_objects/_find', query: findOptions, }); + return keysToCamelCaseShallow(response); } diff --git a/src/legacy/ui/public/utils/numeric.ts b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/numeric.ts similarity index 86% rename from src/legacy/ui/public/utils/numeric.ts rename to src/legacy/core_plugins/kibana/public/management/sections/objects/lib/numeric.ts index 1342498cf5dc3a..c7bc6c26a378ff 100644 --- a/src/legacy/ui/public/utils/numeric.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/numeric.ts @@ -17,8 +17,8 @@ * under the License. */ -import _ from 'lodash'; +import { isNaN } from 'lodash'; export function isNumeric(v: any): boolean { - return !_.isNaN(v) && (typeof v === 'number' || (!Array.isArray(v) && !_.isNaN(parseFloat(v)))); + return !isNaN(v) && (typeof v === 'number' || (!Array.isArray(v) && !isNaN(parseFloat(v)))); } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/settings/lib/get_category_name.js b/src/legacy/core_plugins/kibana/public/management/sections/settings/lib/get_category_name.js index 0155b10f60218e..c64b332e8ebee3 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/settings/lib/get_category_name.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/settings/lib/get_category_name.js @@ -17,9 +17,10 @@ * under the License. */ -import { StringUtils } from 'ui/utils/string_utils'; import { i18n } from '@kbn/i18n'; +const upperFirst = (str = '') => str.replace(/^./, str => str.toUpperCase()); + const names = { general: i18n.translate('kbn.management.settings.categoryNames.generalLabel', { defaultMessage: 'General', @@ -51,5 +52,5 @@ const names = { }; export function getCategoryName(category) { - return category ? names[category] || StringUtils.upperFirst(category) : ''; + return category ? names[category] || upperFirst(category) : ''; } diff --git a/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json b/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json index 25ce0cb58a0ca8..090586a612d4fa 100644 --- a/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json +++ b/src/legacy/core_plugins/kibana/server/tutorials/apm/index_pattern.json @@ -1,7 +1,7 @@ { "attributes": { "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, diff --git a/src/legacy/core_plugins/kibana/server/tutorials/envoyproxy_metrics/index.js b/src/legacy/core_plugins/kibana/server/tutorials/envoyproxy_metrics/index.js new file mode 100644 index 00000000000000..4e5301149b35b0 --- /dev/null +++ b/src/legacy/core_plugins/kibana/server/tutorials/envoyproxy_metrics/index.js @@ -0,0 +1,60 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category'; +import { + onPremInstructions, + cloudInstructions, + onPremCloudInstructions, +} from '../../../common/tutorials/metricbeat_instructions'; + +export function envoyproxyMetricsSpecProvider(server, context) { + const moduleName = 'envoyproxy'; + return { + id: 'envoyproxyMetrics', + name: i18n.translate('kbn.server.tutorials.envoyproxyMetrics.nameTitle', { + defaultMessage: 'Envoy Proxy metrics', + }), + category: TUTORIAL_CATEGORY.METRICS, + shortDescription: i18n.translate('kbn.server.tutorials.envoyproxyMetrics.shortDescription', { + defaultMessage: 'Fetch monitoring metrics from Envoy Proxy.', + }), + longDescription: i18n.translate('kbn.server.tutorials.envoyproxyMetrics.longDescription', { + defaultMessage: + 'The `envoyproxy` Metricbeat module fetches monitoring metrics from Envoy Proxy. \ +[Learn more]({learnMoreLink}).', + values: { + learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-envoyproxy.html', + }, + }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/envoyproxy.svg', + artifacts: { + dashboards: [], + exportedFields: { + documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-envoyproxy.html', + }, + }, + completionTimeMinutes: 10, + // previewImagePath: '/plugins/kibana/home/tutorial_resources/envoyproxy_metrics/screenshot.png', + onPrem: onPremInstructions(moduleName, null, null, null, context), + elasticCloud: cloudInstructions(moduleName), + onPremElasticCloud: onPremCloudInstructions(moduleName), + }; +} diff --git a/src/legacy/core_plugins/kibana/server/tutorials/register.js b/src/legacy/core_plugins/kibana/server/tutorials/register.js index eb06c97629b178..ecc2d1df8c3883 100644 --- a/src/legacy/core_plugins/kibana/server/tutorials/register.js +++ b/src/legacy/core_plugins/kibana/server/tutorials/register.js @@ -83,6 +83,7 @@ import { awsLogsSpecProvider } from './aws_logs'; import { activemqLogsSpecProvider } from './activemq_logs'; import { activemqMetricsSpecProvider } from './activemq_metrics'; import { azureMetricsSpecProvider } from './azure_metrics'; +import { envoyproxyMetricsSpecProvider } from './envoyproxy_metrics'; export function registerTutorials(server) { server.newPlatform.setup.plugins.home.tutorials.registerTutorial(systemLogsSpecProvider); @@ -154,4 +155,5 @@ export function registerTutorials(server) { server.newPlatform.setup.plugins.home.tutorials.registerTutorial(activemqLogsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(activemqMetricsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(azureMetricsSpecProvider); + server.newPlatform.setup.plugins.home.tutorials.registerTutorial(envoyproxyMetricsSpecProvider); } diff --git a/src/legacy/ui/public/utils/supports.js b/src/legacy/core_plugins/tile_map/public/css_filters.js similarity index 64% rename from src/legacy/ui/public/utils/supports.js rename to src/legacy/core_plugins/tile_map/public/css_filters.js index 19c5f34d97f36f..63d6a358059b3a 100644 --- a/src/legacy/ui/public/utils/supports.js +++ b/src/legacy/core_plugins/tile_map/public/css_filters.js @@ -22,22 +22,21 @@ import _ from 'lodash'; /** * just a place to put feature detection checks */ -export const supports = { - cssFilters: (function() { - const e = document.createElement('img'); - const rules = ['webkitFilter', 'mozFilter', 'msFilter', 'filter']; - const test = 'grayscale(1)'; - rules.forEach(function(rule) { - e.style[rule] = test; - }); +export const supportsCssFilters = (function() { + const e = document.createElement('img'); + const rules = ['webkitFilter', 'mozFilter', 'msFilter', 'filter']; + const test = 'grayscale(1)'; - document.body.appendChild(e); - const styles = window.getComputedStyle(e); - const can = _(styles) - .pick(rules) - .includes(test); - document.body.removeChild(e); + rules.forEach(function(rule) { + e.style[rule] = test; + }); - return can; - })(), -}; + document.body.appendChild(e); + const styles = window.getComputedStyle(e); + const can = _(styles) + .pick(rules) + .includes(test); + document.body.removeChild(e); + + return can; +})(); diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_type.js b/src/legacy/core_plugins/tile_map/public/tile_map_type.js index f2354614ac41ac..f2e6469e768e7e 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_type.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_type.js @@ -20,7 +20,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { supports } from 'ui/utils/supports'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps'; import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson'; @@ -29,6 +28,7 @@ import { createTileMapVisualization } from './tile_map_visualization'; import { Status } from '../../visualizations/public'; import { TileMapOptions } from './components/tile_map_options'; import { MapTypes } from './map_types'; +import { supportsCssFilters } from './css_filters'; export function createTileMapTypeDefinition(dependencies) { const CoordinateMapsVisualization = createTileMapVisualization(dependencies); @@ -44,7 +44,7 @@ export function createTileMapTypeDefinition(dependencies) { defaultMessage: 'Plot latitude and longitude coordinates on a map', }), visConfig: { - canDesaturate: !!supports.cssFilters, + canDesaturate: Boolean(supportsCssFilters), defaults: { colorSchema: 'Yellow to Red', mapType: 'Scaled Circle Markers', diff --git a/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js b/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js index c0092ca49c4f34..111db0a83ffc41 100644 --- a/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js +++ b/src/legacy/core_plugins/timelion/public/directives/saved_object_finder.js @@ -19,12 +19,12 @@ import _ from 'lodash'; import rison from 'rison-node'; -import { keyMap } from 'ui/utils/key_map'; import { uiModules } from 'ui/modules'; import 'ui/directives/input_focus'; import 'ui/directives/paginate'; import savedObjectFinderTemplate from './saved_object_finder.html'; import { savedSheetLoader } from '../services/saved_sheets'; +import { keyMap } from 'ui/directives/key_map'; const module = uiModules.get('kibana'); diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/lib/validate_interval.js b/src/legacy/core_plugins/vis_type_timeseries/public/lib/validate_interval.js index 46dab92f3b2459..2dbcdc4749a660 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/lib/validate_interval.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/lib/validate_interval.js @@ -17,9 +17,9 @@ * under the License. */ -import { parseInterval } from 'ui/utils/parse_interval'; import { GTE_INTERVAL_RE } from '../../common/interval_regexp'; import { i18n } from '@kbn/i18n'; +import { parseInterval } from '../../../../../plugins/data/public'; export function validateInterval(bounds, panel, maxBuckets) { const { interval } = panel; diff --git a/src/legacy/core_plugins/vis_type_vega/public/components/vega_actions_menu.tsx b/src/legacy/core_plugins/vis_type_vega/public/components/vega_actions_menu.tsx index 71a88b47a8be3e..3d7fda990b2ae8 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/components/vega_actions_menu.tsx +++ b/src/legacy/core_plugins/vis_type_vega/public/components/vega_actions_menu.tsx @@ -34,12 +34,12 @@ function VegaActionsMenu({ formatHJson, formatJson }: VegaActionsMenuProps) { const onHJsonCLick = useCallback(() => { formatHJson(); setIsPopoverOpen(false); - }, [isPopoverOpen, formatHJson]); + }, [formatHJson]); const onJsonCLick = useCallback(() => { formatJson(); setIsPopoverOpen(false); - }, [isPopoverOpen, formatJson]); + }, [formatJson]); const closePopover = useCallback(() => setIsPopoverOpen(false), []); diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js index a18cb7de5a61b9..a53e8e0498c42a 100644 --- a/src/legacy/server/config/schema.js +++ b/src/legacy/server/config/schema.js @@ -181,7 +181,7 @@ export default () => .default('localhost'), watchPrebuild: Joi.boolean().default(false), watchProxyTimeout: Joi.number().default(10 * 60000), - useBundleCache: Joi.boolean().default(Joi.ref('$prod')), + useBundleCache: Joi.boolean().default(!!process.env.CODE_COVERAGE ? true : Joi.ref('$prod')), sourceMaps: Joi.when('$prod', { is: true, then: Joi.boolean().valid(false), diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts index 0af6dacee59c81..8da1b3b05fa764 100644 --- a/src/legacy/server/kbn_server.d.ts +++ b/src/legacy/server/kbn_server.d.ts @@ -129,7 +129,7 @@ export interface KibanaCore { plugins: PluginsSetup; }; startDeps: { - core: CoreSetup; + core: CoreStart; plugins: Record; }; logger: LoggerFactory; diff --git a/src/legacy/server/saved_objects/saved_objects_mixin.test.js b/src/legacy/server/saved_objects/saved_objects_mixin.test.js index 0e96189db4650e..691878cf66d27a 100644 --- a/src/legacy/server/saved_objects/saved_objects_mixin.test.js +++ b/src/legacy/server/saved_objects/saved_objects_mixin.test.js @@ -106,12 +106,7 @@ describe('Saved Objects Mixin', () => { newPlatform: { __internals: { elasticsearch: { - adminClient$: { - pipe: jest.fn().mockImplementation(() => ({ - toPromise: () => - Promise.resolve({ adminClient: { callAsInternalUser: mockCallCluster } }), - })), - }, + adminClient: { callAsInternalUser: mockCallCluster }, }, }, }, diff --git a/src/legacy/server/status/lib/case_conversion.test.ts b/src/legacy/server/status/lib/case_conversion.test.ts new file mode 100644 index 00000000000000..a231ee0ba4b0fd --- /dev/null +++ b/src/legacy/server/status/lib/case_conversion.test.ts @@ -0,0 +1,36 @@ +/* + * 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 { keysToSnakeCaseShallow } from './case_conversion'; + +describe('keysToSnakeCaseShallow', () => { + test("should convert all of an object's keys to snake case", () => { + const data = { + camelCase: 'camel_case', + 'kebab-case': 'kebab_case', + snake_case: 'snake_case', + }; + + const result = keysToSnakeCaseShallow(data); + + expect(result.camel_case).toBe('camel_case'); + expect(result.kebab_case).toBe('kebab_case'); + expect(result.snake_case).toBe('snake_case'); + }); +}); diff --git a/src/legacy/ui/public/utils/parse_interval.d.ts b/src/legacy/server/status/lib/case_conversion.ts similarity index 82% rename from src/legacy/ui/public/utils/parse_interval.d.ts rename to src/legacy/server/status/lib/case_conversion.ts index 9d78b4ef6cddfe..a3ae15028daeb2 100644 --- a/src/legacy/ui/public/utils/parse_interval.d.ts +++ b/src/legacy/server/status/lib/case_conversion.ts @@ -17,6 +17,8 @@ * under the License. */ -import moment from 'moment'; +import { mapKeys, snakeCase } from 'lodash'; -export function parseInterval(interval: string): moment.Duration | null; +export function keysToSnakeCaseShallow(object: Record) { + return mapKeys(object, (value, key) => snakeCase(key)); +} diff --git a/src/legacy/server/status/lib/metrics.js b/src/legacy/server/status/lib/metrics.js index 322d8d8bd9ab44..2631b245e72ab4 100644 --- a/src/legacy/server/status/lib/metrics.js +++ b/src/legacy/server/status/lib/metrics.js @@ -20,7 +20,7 @@ import os from 'os'; import v8 from 'v8'; import { get, isObject, merge } from 'lodash'; -import { keysToSnakeCaseShallow } from '../../../utils/case_conversion'; +import { keysToSnakeCaseShallow } from './case_conversion'; import { getAllStats as cGroupStats } from './cgroup'; import { getOSInfo } from './get_os_info'; diff --git a/src/legacy/ui/public/utils/key_map.ts b/src/legacy/ui/public/directives/key_map.ts similarity index 100% rename from src/legacy/ui/public/utils/key_map.ts rename to src/legacy/ui/public/directives/key_map.ts diff --git a/src/legacy/ui/public/directives/watch_multi/watch_multi.js b/src/legacy/ui/public/directives/watch_multi/watch_multi.js index 2a2ffe24cba9c2..54b5cf08a93978 100644 --- a/src/legacy/ui/public/directives/watch_multi/watch_multi.js +++ b/src/legacy/ui/public/directives/watch_multi/watch_multi.js @@ -19,7 +19,6 @@ import _ from 'lodash'; import { uiModules } from '../../modules'; -import { callEach } from '../../utils/function'; export function watchMultiDecorator($provide) { $provide.decorator('$rootScope', function($delegate) { @@ -112,7 +111,9 @@ export function watchMultiDecorator($provide) { ) ); - return _.partial(callEach, unwatchers); + return function() { + unwatchers.forEach(listener => listener()); + }; }; function normalizeExpression($scope, expr) { diff --git a/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts b/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts new file mode 100644 index 00000000000000..fc4ca8469382ad --- /dev/null +++ b/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts @@ -0,0 +1,63 @@ +/* + * 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 { groupBy } from 'lodash'; +import { organizeBy } from './organize_by'; + +describe('organizeBy', () => { + test('it works', () => { + const col = [ + { + name: 'one', + roles: ['user', 'admin', 'owner'], + }, + { + name: 'two', + roles: ['user'], + }, + { + name: 'three', + roles: ['user'], + }, + { + name: 'four', + roles: ['user', 'admin'], + }, + ]; + + const resp = organizeBy(col, 'roles'); + expect(resp).toHaveProperty('user'); + expect(resp.user.length).toBe(4); + + expect(resp).toHaveProperty('admin'); + expect(resp.admin.length).toBe(2); + + expect(resp).toHaveProperty('owner'); + expect(resp.owner.length).toBe(1); + }); + + test('behaves just like groupBy in normal scenarios', () => { + const col = [{ name: 'one' }, { name: 'two' }, { name: 'three' }, { name: 'four' }]; + + const orgs = organizeBy(col, 'name'); + const groups = groupBy(col, 'name'); + + expect(orgs).toEqual(groups); + }); +}); diff --git a/src/legacy/ui/public/indexed_array/helpers/organize_by.ts b/src/legacy/ui/public/indexed_array/helpers/organize_by.ts new file mode 100644 index 00000000000000..e923767c892cd5 --- /dev/null +++ b/src/legacy/ui/public/indexed_array/helpers/organize_by.ts @@ -0,0 +1,61 @@ +/* + * 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 { each, isFunction } from 'lodash'; + +/** + * Like _.groupBy, but allows specifying multiple groups for a + * single object. + * + * organizeBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') + * // Object {1: Array[2], 2: Array[1], 3: Array[1], 4: Array[1]} + * + * _.groupBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') + * // Object {'1,2,3': Array[1], '1,4': Array[1]} + * + * @param {array} collection - the list of values to organize + * @param {Function} callback - either a property name, or a callback. + * @return {object} + */ +export function organizeBy(collection: object[], callback: ((obj: object) => string) | string) { + const buckets: { [key: string]: object[] } = {}; + + function add(key: string, obj: object) { + if (!buckets[key]) { + buckets[key] = []; + } + buckets[key].push(obj); + } + + each(collection, (obj: Record) => { + const keys = isFunction(callback) ? callback(obj) : obj[callback]; + + if (!Array.isArray(keys)) { + add(keys, obj); + return; + } + + let length = keys.length; + while (length-- > 0) { + add(keys[length], obj); + } + }); + + return buckets; +} diff --git a/src/legacy/ui/public/indexed_array/indexed_array.js b/src/legacy/ui/public/indexed_array/indexed_array.js index 96b37a1423be09..39c79b2f021a39 100644 --- a/src/legacy/ui/public/indexed_array/indexed_array.js +++ b/src/legacy/ui/public/indexed_array/indexed_array.js @@ -19,7 +19,7 @@ import _ from 'lodash'; import { inflector } from './inflector'; -import { organizeBy } from '../utils/collection'; +import { organizeBy } from './helpers/organize_by'; const pathGetter = _(_.get) .rearg(1, 0) diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 3d4292cef27f4c..06424ea48a40f9 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -148,6 +148,8 @@ export const npStart = { legacy: { getSection: () => ({ register: sinon.fake(), + deregister: sinon.fake(), + hasItem: sinon.fake(), }), }, }, diff --git a/src/legacy/ui/public/registry/doc_views.ts b/src/legacy/ui/public/registry/doc_views.ts deleted file mode 100644 index bf1e8416ae66d8..00000000000000 --- a/src/legacy/ui/public/registry/doc_views.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { convertDirectiveToRenderFn } from './doc_views_helpers'; -import { DocView, DocViewInput, ElasticSearchHit, DocViewInputFn } from './doc_views_types'; - -export { DocViewRenderProps, DocView, DocViewRenderFn } from './doc_views_types'; - -export interface DocViewsRegistry { - docViews: DocView[]; - addDocView: (docView: DocViewInput) => void; - getDocViewsSorted: (hit: ElasticSearchHit) => DocView[]; -} - -export const docViews: DocView[] = []; - -/** - * Extends and adds the given doc view to the registry array - */ -export function addDocView(docView: DocViewInput) { - if (docView.directive) { - // convert angular directive to render function for backwards compatibility - docView.render = convertDirectiveToRenderFn(docView.directive); - } - if (typeof docView.shouldShow !== 'function') { - docView.shouldShow = () => true; - } - docViews.push(docView as DocView); -} - -/** - * Empty array of doc views for testing - */ -export function emptyDocViews() { - docViews.length = 0; -} - -/** - * Returns a sorted array of doc_views for rendering tabs - */ -export function getDocViewsSorted(hit: ElasticSearchHit): DocView[] { - return docViews - .filter(docView => docView.shouldShow(hit)) - .sort((a, b) => (Number(a.order) > Number(b.order) ? 1 : -1)); -} -/** - * Provider for compatibility with 3rd Party plugins - */ -export const DocViewsRegistryProvider = { - register: (docViewRaw: DocViewInput | DocViewInputFn) => { - const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw; - addDocView(docView); - }, -}; diff --git a/src/legacy/utils/case_conversion.ts b/src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts similarity index 62% rename from src/legacy/utils/case_conversion.ts rename to src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts index e1c1317b26b142..5b108a4cc0180f 100644 --- a/src/legacy/utils/case_conversion.ts +++ b/src/legacy/ui/public/saved_objects/helpers/string_utils.test.ts @@ -16,17 +16,17 @@ * specific language governing permissions and limitations * under the License. */ +import { StringUtils } from './string_utils'; -import _ from 'lodash'; +describe('StringUtils class', () => { + describe('static upperFirst', () => { + test('should converts the first character of string to upper case', () => { + expect(StringUtils.upperFirst()).toBe(''); + expect(StringUtils.upperFirst('')).toBe(''); -export function keysToSnakeCaseShallow(object: Record) { - return _.mapKeys(object, (value, key) => { - return _.snakeCase(key); + expect(StringUtils.upperFirst('Fred')).toBe('Fred'); + expect(StringUtils.upperFirst('fred')).toBe('Fred'); + expect(StringUtils.upperFirst('FRED')).toBe('FRED'); + }); }); -} - -export function keysToCamelCaseShallow(object: Record) { - return _.mapKeys(object, (value, key) => { - return _.camelCase(key); - }); -} +}); diff --git a/src/legacy/ui/public/utils/string_utils.ts b/src/legacy/ui/public/saved_objects/helpers/string_utils.ts similarity index 94% rename from src/legacy/ui/public/utils/string_utils.ts rename to src/legacy/ui/public/saved_objects/helpers/string_utils.ts index 22a57aeb07933a..fb10b792b7e69a 100644 --- a/src/legacy/ui/public/utils/string_utils.ts +++ b/src/legacy/ui/public/saved_objects/helpers/string_utils.ts @@ -23,7 +23,7 @@ export class StringUtils { * @param str {string} * @returns {string} */ - public static upperFirst(str: string): string { + public static upperFirst(str: string = ''): string { return str ? str.charAt(0).toUpperCase() + str.slice(1) : ''; } } diff --git a/src/legacy/ui/public/saved_objects/saved_object_loader.ts b/src/legacy/ui/public/saved_objects/saved_object_loader.ts index eb880ce5380c0c..7784edc9ad5288 100644 --- a/src/legacy/ui/public/saved_objects/saved_object_loader.ts +++ b/src/legacy/ui/public/saved_objects/saved_object_loader.ts @@ -18,7 +18,7 @@ */ import { SavedObject } from 'ui/saved_objects/types'; import { ChromeStart, SavedObjectsClientContract, SavedObjectsFindOptions } from 'kibana/public'; -import { StringUtils } from '../utils/string_utils'; +import { StringUtils } from './helpers/string_utils'; /** * The SavedObjectLoader class provides some convenience functions diff --git a/src/legacy/ui/public/state_management/app_state.js b/src/legacy/ui/public/state_management/app_state.js index 4bf386febf8361..e253d49c04131a 100644 --- a/src/legacy/ui/public/state_management/app_state.js +++ b/src/legacy/ui/public/state_management/app_state.js @@ -31,7 +31,6 @@ import { uiModules } from '../modules'; import { StateProvider } from './state'; import '../persisted_state'; import { createLegacyClass } from '../utils/legacy_class'; -import { callEach } from '../utils/function'; const urlParam = '_a'; @@ -62,7 +61,8 @@ export function AppStateProvider(Private, $location, $injector) { AppState.prototype.destroy = function() { AppState.Super.prototype.destroy.call(this); AppState.getAppState._set(null); - callEach(eventUnsubscribers); + + eventUnsubscribers.forEach(listener => listener()); }; /** diff --git a/src/legacy/ui/public/state_management/state.js b/src/legacy/ui/public/state_management/state.js index a9898303fa5be5..289d4b8006cba9 100644 --- a/src/legacy/ui/public/state_management/state.js +++ b/src/legacy/ui/public/state_management/state.js @@ -29,12 +29,11 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import angular from 'angular'; import rison from 'rison-node'; -import { applyDiff } from '../utils/diff_object'; +import { applyDiff } from './utils/diff_object'; import { EventsProvider } from '../events'; import { fatalError, toastNotifications } from '../notify'; import './config_provider'; import { createLegacyClass } from '../utils/legacy_class'; -import { callEach } from '../utils/function'; import { hashedItemStore, isStateHash, @@ -66,7 +65,7 @@ export function StateProvider( this._hashedItemStore = _hashedItemStore; // When the URL updates we need to fetch the values from the URL - this._cleanUpListeners = _.partial(callEach, [ + this._cleanUpListeners = [ // partial route update, no app reload $rootScope.$on('$routeUpdate', () => { this.fetch(); @@ -85,7 +84,7 @@ export function StateProvider( this.fetch(); } }), - ]); + ]; // Initialize the State with fetch this.fetch(); @@ -242,7 +241,9 @@ export function StateProvider( */ State.prototype.destroy = function() { this.off(); // removes all listeners - this._cleanUpListeners(); // Removes the $routeUpdate listener + + // Removes the $routeUpdate listener + this._cleanUpListeners.forEach(listener => listener(this)); }; State.prototype.setDefaults = function(defaults) { diff --git a/src/legacy/ui/public/utils/__tests__/diff_object.js b/src/legacy/ui/public/state_management/utils/diff_object.test.ts similarity index 57% rename from src/legacy/ui/public/utils/__tests__/diff_object.js rename to src/legacy/ui/public/state_management/utils/diff_object.test.ts index 8459aa60436b16..d93fa0fe5a1697 100644 --- a/src/legacy/ui/public/utils/__tests__/diff_object.js +++ b/src/legacy/ui/public/state_management/utils/diff_object.test.ts @@ -17,76 +17,88 @@ * under the License. */ -import expect from '@kbn/expect'; -import _ from 'lodash'; -import { applyDiff } from '../diff_object'; +import { cloneDeep } from 'lodash'; +import { applyDiff } from './diff_object'; -describe('ui/utils/diff_object', function() { - it('should list the removed keys', function() { +describe('diff_object', () => { + test('should list the removed keys', () => { const target = { test: 'foo' }; const source = { foo: 'test' }; const results = applyDiff(target, source); - expect(results).to.have.property('removed'); - expect(results.removed).to.eql(['test']); + + expect(results).toHaveProperty('removed'); + expect(results.removed).toEqual(['test']); }); - it('should list the changed keys', function() { + test('should list the changed keys', () => { const target = { foo: 'bar' }; const source = { foo: 'test' }; const results = applyDiff(target, source); - expect(results).to.have.property('changed'); - expect(results.changed).to.eql(['foo']); + + expect(results).toHaveProperty('changed'); + expect(results.changed).toEqual(['foo']); }); - it('should list the added keys', function() { + test('should list the added keys', () => { const target = {}; const source = { foo: 'test' }; const results = applyDiff(target, source); - expect(results).to.have.property('added'); - expect(results.added).to.eql(['foo']); + + expect(results).toHaveProperty('added'); + expect(results.added).toEqual(['foo']); }); - it('should list all the keys that are change or removed', function() { + test('should list all the keys that are change or removed', () => { const target = { foo: 'bar', test: 'foo' }; const source = { foo: 'test' }; const results = applyDiff(target, source); - expect(results).to.have.property('keys'); - expect(results.keys).to.eql(['foo', 'test']); + + expect(results).toHaveProperty('keys'); + expect(results.keys).toEqual(['foo', 'test']); }); - it('should ignore functions', function() { + test('should ignore functions', () => { const target = { foo: 'bar', test: 'foo' }; - const source = { foo: 'test', fn: _.noop }; + const source = { foo: 'test', fn: () => {} }; + applyDiff(target, source); - expect(target).to.not.have.property('fn'); + + expect(target).not.toHaveProperty('fn'); }); - it('should ignore underscores', function() { + test('should ignore underscores', () => { const target = { foo: 'bar', test: 'foo' }; const source = { foo: 'test', _private: 'foo' }; + applyDiff(target, source); - expect(target).to.not.have.property('_private'); + + expect(target).not.toHaveProperty('_private'); }); - it('should ignore dollar signs', function() { + test('should ignore dollar signs', () => { const target = { foo: 'bar', test: 'foo' }; const source = { foo: 'test', $private: 'foo' }; + applyDiff(target, source); - expect(target).to.not.have.property('$private'); + + expect(target).not.toHaveProperty('$private'); }); - it('should not list any changes for similar objects', function() { + test('should not list any changes for similar objects', () => { const target = { foo: 'bar', test: 'foo' }; const source = { foo: 'bar', test: 'foo', $private: 'foo' }; const results = applyDiff(target, source); - expect(results.changed).to.be.empty(); + + expect(results.changed).toEqual([]); }); - it('should only change keys that actually changed', function() { + test('should only change keys that actually changed', () => { const obj = { message: 'foo' }; - const target = { obj: obj, message: 'foo' }; - const source = { obj: _.cloneDeep(obj), message: 'test' }; + const target = { obj, message: 'foo' }; + const source = { obj: cloneDeep(obj), message: 'test' }; + applyDiff(target, source); - expect(target.obj).to.be(obj); + + expect(target.obj).toBe(obj); }); }); diff --git a/src/legacy/ui/public/state_management/utils/diff_object.ts b/src/legacy/ui/public/state_management/utils/diff_object.ts new file mode 100644 index 00000000000000..2590e2271f7718 --- /dev/null +++ b/src/legacy/ui/public/state_management/utils/diff_object.ts @@ -0,0 +1,75 @@ +/* + * 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 { keys, isFunction, difference, filter, union, pick, each, assign, isEqual } from 'lodash'; + +export interface IDiffObject { + removed: string[]; + added: string[]; + changed: string[]; + keys: string[]; +} + +/** + * Filter the private vars + * @param {string} key The keys + * @returns {boolean} + */ +const filterPrivateAndMethods = function(obj: Record) { + return function(key: string) { + if (isFunction(obj[key])) return false; + if (key.charAt(0) === '$') return false; + return key.charAt(0) !== '_'; + }; +}; + +export function applyDiff(target: Record, source: Record) { + const diff: IDiffObject = { + removed: [], + added: [], + changed: [], + keys: [], + }; + + const targetKeys = keys(target).filter(filterPrivateAndMethods(target)); + const sourceKeys = keys(source).filter(filterPrivateAndMethods(source)); + + // Find the keys to be removed + diff.removed = difference(targetKeys, sourceKeys); + + // Find the keys to be added + diff.added = difference(sourceKeys, targetKeys); + + // Find the keys that will be changed + diff.changed = filter(sourceKeys, key => !isEqual(target[key], source[key])); + + // Make a list of all the keys that are changing + diff.keys = union(diff.changed, diff.removed, diff.added); + + // Remove all the keys + each(diff.removed, key => { + delete target[key]; + }); + + // Assign the changed to the source to the target + assign(target, pick(source, diff.changed)); + // Assign the added to the source to the target + assign(target, pick(source, diff.added)); + + return diff; +} diff --git a/src/legacy/ui/public/time_buckets/time_buckets.js b/src/legacy/ui/public/time_buckets/time_buckets.js index 92de88b47e3c30..96ba4bfb2ac2c8 100644 --- a/src/legacy/ui/public/time_buckets/time_buckets.js +++ b/src/legacy/ui/public/time_buckets/time_buckets.js @@ -20,13 +20,12 @@ import _ from 'lodash'; import moment from 'moment'; import { npStart } from 'ui/new_platform'; -import { parseInterval } from '../utils/parse_interval'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; import { convertDurationToNormalizedEsInterval, convertIntervalToEsInterval, } from './calc_es_interval'; -import { FIELD_FORMAT_IDS } from '../../../../plugins/data/public'; +import { FIELD_FORMAT_IDS, parseInterval } from '../../../../plugins/data/public'; const getConfig = (...args) => npStart.core.uiSettings.get(...args); diff --git a/src/legacy/ui/public/utils/__tests__/collection.js b/src/legacy/ui/public/utils/__tests__/collection.js deleted file mode 100644 index 402f0387e53cea..00000000000000 --- a/src/legacy/ui/public/utils/__tests__/collection.js +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { groupBy } from 'lodash'; -import { move, pushAll, organizeBy } from '../collection'; - -describe('collection', () => { - describe('move', function() { - it('accepts previous from->to syntax', function() { - const list = [1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1]; - - expect(list[3]).to.be(1); - expect(list[8]).to.be(8); - - move(list, 8, 3); - - expect(list[8]).to.be(1); - expect(list[3]).to.be(8); - }); - - it('moves an object up based on a function callback', function() { - const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; - - expect(list[4]).to.be(0); - expect(list[5]).to.be(1); - expect(list[6]).to.be(0); - - move(list, 5, false, function(v) { - return v === 0; - }); - - expect(list[4]).to.be(1); - expect(list[5]).to.be(0); - expect(list[6]).to.be(0); - }); - - it('moves an object down based on a function callback', function() { - const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; - - expect(list[4]).to.be(0); - expect(list[5]).to.be(1); - expect(list[6]).to.be(0); - - move(list, 5, true, function(v) { - return v === 0; - }); - - expect(list[4]).to.be(0); - expect(list[5]).to.be(0); - expect(list[6]).to.be(1); - }); - - it('moves an object up based on a where callback', function() { - const list = [ - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - ]; - - expect(list[4]).to.have.property('v', 0); - expect(list[5]).to.have.property('v', 1); - expect(list[6]).to.have.property('v', 0); - - move(list, 5, false, { v: 0 }); - - expect(list[4]).to.have.property('v', 1); - expect(list[5]).to.have.property('v', 0); - expect(list[6]).to.have.property('v', 0); - }); - - it('moves an object down based on a where callback', function() { - const list = [ - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - ]; - - expect(list[4]).to.have.property('v', 0); - expect(list[5]).to.have.property('v', 1); - expect(list[6]).to.have.property('v', 0); - - move(list, 5, true, { v: 0 }); - - expect(list[4]).to.have.property('v', 0); - expect(list[5]).to.have.property('v', 0); - expect(list[6]).to.have.property('v', 1); - }); - - it('moves an object down based on a pluck callback', function() { - const list = [ - { id: 0, normal: true }, - { id: 1, normal: true }, - { id: 2, normal: true }, - { id: 3, normal: true }, - { id: 4, normal: true }, - { id: 5, normal: false }, - { id: 6, normal: true }, - { id: 7, normal: true }, - { id: 8, normal: true }, - { id: 9, normal: true }, - ]; - - expect(list[4]).to.have.property('id', 4); - expect(list[5]).to.have.property('id', 5); - expect(list[6]).to.have.property('id', 6); - - move(list, 5, true, 'normal'); - - expect(list[4]).to.have.property('id', 4); - expect(list[5]).to.have.property('id', 6); - expect(list[6]).to.have.property('id', 5); - }); - }); - - describe('pushAll', function() { - it('pushes an entire array into another', function() { - const a = [1, 2, 3, 4]; - const b = [5, 6, 7, 8]; - - const output = pushAll(b, a); - expect(output).to.be(a); - expect(a).to.eql([1, 2, 3, 4, 5, 6, 7, 8]); - expect(b).to.eql([5, 6, 7, 8]); - }); - }); - - describe('organizeBy', function() { - it('it works', function() { - const col = [ - { - name: 'one', - roles: ['user', 'admin', 'owner'], - }, - { - name: 'two', - roles: ['user'], - }, - { - name: 'three', - roles: ['user'], - }, - { - name: 'four', - roles: ['user', 'admin'], - }, - ]; - - const resp = organizeBy(col, 'roles'); - expect(resp).to.have.property('user'); - expect(resp.user).to.have.length(4); - - expect(resp).to.have.property('admin'); - expect(resp.admin).to.have.length(2); - - expect(resp).to.have.property('owner'); - expect(resp.owner).to.have.length(1); - }); - - it('behaves just like groupBy in normal scenarios', function() { - const col = [{ name: 'one' }, { name: 'two' }, { name: 'three' }, { name: 'four' }]; - - const orgs = organizeBy(col, 'name'); - const groups = groupBy(col, 'name'); - expect(orgs).to.eql(groups); - }); - }); -}); diff --git a/src/legacy/ui/public/utils/__tests__/parse_interval.js b/src/legacy/ui/public/utils/__tests__/parse_interval.js deleted file mode 100644 index a33b0ab958072c..00000000000000 --- a/src/legacy/ui/public/utils/__tests__/parse_interval.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { parseInterval } from '../parse_interval'; -import expect from '@kbn/expect'; - -describe('parseInterval', function() { - it('should correctly parse an interval containing unit and value', function() { - let duration = parseInterval('1d'); - expect(duration.as('d')).to.be(1); - - duration = parseInterval('2y'); - expect(duration.as('y')).to.be(2); - - duration = parseInterval('5M'); - expect(duration.as('M')).to.be(5); - - duration = parseInterval('5m'); - expect(duration.as('m')).to.be(5); - - duration = parseInterval('250ms'); - expect(duration.as('ms')).to.be(250); - - duration = parseInterval('100s'); - expect(duration.as('s')).to.be(100); - - duration = parseInterval('23d'); - expect(duration.as('d')).to.be(23); - - duration = parseInterval('52w'); - expect(duration.as('w')).to.be(52); - }); - - it('should correctly parse fractional intervals containing unit and value', function() { - let duration = parseInterval('1.5w'); - expect(duration.as('w')).to.be(1.5); - - duration = parseInterval('2.35y'); - expect(duration.as('y')).to.be(2.35); - }); - - it('should correctly bubble up intervals which are less than 1', function() { - let duration = parseInterval('0.5y'); - expect(duration.as('d')).to.be(183); - - duration = parseInterval('0.5d'); - expect(duration.as('h')).to.be(12); - }); - - it('should correctly parse a unit in an interval only', function() { - let duration = parseInterval('ms'); - expect(duration.as('ms')).to.be(1); - - duration = parseInterval('d'); - expect(duration.as('d')).to.be(1); - - duration = parseInterval('m'); - expect(duration.as('m')).to.be(1); - - duration = parseInterval('y'); - expect(duration.as('y')).to.be(1); - - duration = parseInterval('M'); - expect(duration.as('M')).to.be(1); - }); - - it('should return null for an invalid interval', function() { - let duration = parseInterval(''); - expect(duration).to.not.be.ok(); - - duration = parseInterval(null); - expect(duration).to.not.be.ok(); - - duration = parseInterval('234asdf'); - expect(duration).to.not.be.ok(); - }); -}); diff --git a/src/legacy/ui/public/utils/__tests__/sort_prefix_first.js b/src/legacy/ui/public/utils/__tests__/sort_prefix_first.js deleted file mode 100644 index 721de95cbd27db..00000000000000 --- a/src/legacy/ui/public/utils/__tests__/sort_prefix_first.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { sortPrefixFirst } from '../sort_prefix_first'; - -describe('sortPrefixFirst', function() { - it('should return the original unmodified array if no prefix is provided', function() { - const array = ['foo', 'bar', 'baz']; - const result = sortPrefixFirst(array); - expect(result).to.be(array); - expect(result).to.eql(['foo', 'bar', 'baz']); - }); - - it('should sort items that match the prefix first without modifying the original array', function() { - const array = ['foo', 'bar', 'baz']; - const result = sortPrefixFirst(array, 'b'); - expect(result).to.not.be(array); - expect(result).to.eql(['bar', 'baz', 'foo']); - expect(array).to.eql(['foo', 'bar', 'baz']); - }); - - it('should not modify the order of the array other than matching prefix without modifying the original array', function() { - const array = ['foo', 'bar', 'baz', 'qux', 'quux']; - const result = sortPrefixFirst(array, 'b'); - expect(result).to.not.be(array); - expect(result).to.eql(['bar', 'baz', 'foo', 'qux', 'quux']); - expect(array).to.eql(['foo', 'bar', 'baz', 'qux', 'quux']); - }); - - it('should sort objects by property if provided', function() { - const array = [ - { name: 'foo' }, - { name: 'bar' }, - { name: 'baz' }, - { name: 'qux' }, - { name: 'quux' }, - ]; - const result = sortPrefixFirst(array, 'b', 'name'); - expect(result).to.not.be(array); - expect(result).to.eql([ - { name: 'bar' }, - { name: 'baz' }, - { name: 'foo' }, - { name: 'qux' }, - { name: 'quux' }, - ]); - expect(array).to.eql([ - { name: 'foo' }, - { name: 'bar' }, - { name: 'baz' }, - { name: 'qux' }, - { name: 'quux' }, - ]); - }); - - it('should handle numbers', function() { - const array = [1, 50, 5]; - const result = sortPrefixFirst(array, 5); - expect(result).to.not.be(array); - expect(result).to.eql([50, 5, 1]); - }); - - it('should handle mixed case', function() { - const array = ['Date Histogram', 'Histogram']; - const prefix = 'histo'; - const result = sortPrefixFirst(array, prefix); - expect(result).to.not.be(array); - expect(result).to.eql(['Histogram', 'Date Histogram']); - }); -}); diff --git a/src/legacy/ui/public/utils/collection.test.ts b/src/legacy/ui/public/utils/collection.test.ts new file mode 100644 index 00000000000000..0841e3554c0d09 --- /dev/null +++ b/src/legacy/ui/public/utils/collection.test.ts @@ -0,0 +1,141 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { move } from './collection'; + +describe('collection', () => { + describe('move', () => { + test('accepts previous from->to syntax', () => { + const list = [1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1]; + + expect(list[3]).toBe(1); + expect(list[8]).toBe(8); + + move(list, 8, 3); + + expect(list[8]).toBe(1); + expect(list[3]).toBe(8); + }); + + test('moves an object up based on a function callback', () => { + const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; + + expect(list[4]).toBe(0); + expect(list[5]).toBe(1); + expect(list[6]).toBe(0); + + move(list, 5, false, (v: any) => v === 0); + + expect(list[4]).toBe(1); + expect(list[5]).toBe(0); + expect(list[6]).toBe(0); + }); + + test('moves an object down based on a function callback', () => { + const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; + + expect(list[4]).toBe(0); + expect(list[5]).toBe(1); + expect(list[6]).toBe(0); + + move(list, 5, true, (v: any) => v === 0); + + expect(list[4]).toBe(0); + expect(list[5]).toBe(0); + expect(list[6]).toBe(1); + }); + + test('moves an object up based on a where callback', () => { + const list = [ + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 0 }, + { v: 1 }, + { v: 0 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + ]; + + expect(list[4]).toHaveProperty('v', 0); + expect(list[5]).toHaveProperty('v', 1); + expect(list[6]).toHaveProperty('v', 0); + + move(list, 5, false, { v: 0 }); + + expect(list[4]).toHaveProperty('v', 1); + expect(list[5]).toHaveProperty('v', 0); + expect(list[6]).toHaveProperty('v', 0); + }); + + test('moves an object down based on a where callback', () => { + const list = [ + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 0 }, + { v: 1 }, + { v: 0 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + { v: 1 }, + ]; + + expect(list[4]).toHaveProperty('v', 0); + expect(list[5]).toHaveProperty('v', 1); + expect(list[6]).toHaveProperty('v', 0); + + move(list, 5, true, { v: 0 }); + + expect(list[4]).toHaveProperty('v', 0); + expect(list[5]).toHaveProperty('v', 0); + expect(list[6]).toHaveProperty('v', 1); + }); + + test('moves an object down based on a pluck callback', () => { + const list = [ + { id: 0, normal: true }, + { id: 1, normal: true }, + { id: 2, normal: true }, + { id: 3, normal: true }, + { id: 4, normal: true }, + { id: 5, normal: false }, + { id: 6, normal: true }, + { id: 7, normal: true }, + { id: 8, normal: true }, + { id: 9, normal: true }, + ]; + + expect(list[4]).toHaveProperty('id', 4); + expect(list[5]).toHaveProperty('id', 5); + expect(list[6]).toHaveProperty('id', 6); + + move(list, 5, true, 'normal'); + + expect(list[4]).toHaveProperty('id', 4); + expect(list[5]).toHaveProperty('id', 6); + expect(list[6]).toHaveProperty('id', 5); + }); + }); +}); diff --git a/src/legacy/ui/public/utils/collection.ts b/src/legacy/ui/public/utils/collection.ts index 61a7388575c936..45e5a0704c37bb 100644 --- a/src/legacy/ui/public/utils/collection.ts +++ b/src/legacy/ui/public/utils/collection.ts @@ -33,10 +33,10 @@ import _ from 'lodash'; * @return {array} - the objs argument */ export function move( - objs: object[], + objs: any[], obj: object | number, below: number | boolean, - qualifier: (object: object, index: number) => any + qualifier?: ((object: object, index: number) => any) | Record | string ): object[] { const origI = _.isNumber(obj) ? obj : objs.indexOf(obj); if (origI === -1) { @@ -50,7 +50,7 @@ export function move( } below = !!below; - qualifier = _.callback(qualifier); + qualifier = qualifier && _.callback(qualifier); const above = !below; const finder = below ? _.findIndex : _.findLastIndex; @@ -63,7 +63,7 @@ export function move( if (above && otherI >= origI) { return; } - return !!qualifier(otherAgg, otherI); + return Boolean(_.isFunction(qualifier) && qualifier(otherAgg, otherI)); }); if (targetI === -1) { @@ -74,68 +74,3 @@ export function move( objs.splice(targetI, 0, objs.splice(origI, 1)[0]); return objs; } - -/** - * Like _.groupBy, but allows specifying multiple groups for a - * single object. - * - * organizeBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') - * // Object {1: Array[2], 2: Array[1], 3: Array[1], 4: Array[1]} - * - * _.groupBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') - * // Object {'1,2,3': Array[1], '1,4': Array[1]} - * - * @param {array} collection - the list of values to organize - * @param {Function} callback - either a property name, or a callback. - * @return {object} - */ -export function organizeBy(collection: object[], callback: (obj: object) => string | string) { - const buckets: { [key: string]: object[] } = {}; - const prop = typeof callback === 'function' ? false : callback; - - function add(key: string, obj: object) { - if (!buckets[key]) { - buckets[key] = []; - } - buckets[key].push(obj); - } - - _.each(collection, (obj: object) => { - const keys = prop === false ? callback(obj) : obj[prop]; - - if (!Array.isArray(keys)) { - add(keys, obj); - return; - } - - let length = keys.length; - while (length-- > 0) { - add(keys[length], obj); - } - }); - - return buckets; -} - -/** - * Efficient and safe version of [].push(dest, source); - * - * @param {Array} source - the array to pull values from - * @param {Array} dest - the array to push values into - * @return {Array} dest - */ -export function pushAll(source: any[], dest: any[]): any[] { - const start = dest.length; - const adding = source.length; - - // allocate - http://goo.gl/e2i0S0 - dest.length = start + adding; - - // fill sparse positions - let i = -1; - while (++i < adding) { - dest[start + i] = source[i]; - } - - return dest; -} diff --git a/src/legacy/ui/public/utils/diff_object.js b/src/legacy/ui/public/utils/diff_object.js deleted file mode 100644 index ddad5e0ae42a01..00000000000000 --- a/src/legacy/ui/public/utils/diff_object.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import angular from 'angular'; - -export function applyDiff(target, source) { - const diff = {}; - - /** - * Filter the private vars - * @param {string} key The keys - * @returns {boolean} - */ - const filterPrivateAndMethods = function(obj) { - return function(key) { - if (_.isFunction(obj[key])) return false; - if (key.charAt(0) === '$') return false; - return key.charAt(0) !== '_'; - }; - }; - - const targetKeys = _.keys(target).filter(filterPrivateAndMethods(target)); - const sourceKeys = _.keys(source).filter(filterPrivateAndMethods(source)); - - // Find the keys to be removed - diff.removed = _.difference(targetKeys, sourceKeys); - - // Find the keys to be added - diff.added = _.difference(sourceKeys, targetKeys); - - // Find the keys that will be changed - diff.changed = _.filter(sourceKeys, function(key) { - return !angular.equals(target[key], source[key]); - }); - - // Make a list of all the keys that are changing - diff.keys = _.union(diff.changed, diff.removed, diff.added); - - // Remove all the keys - _.each(diff.removed, function(key) { - delete target[key]; - }); - - // Assign the changed to the source to the target - _.assign(target, _.pick(source, diff.changed)); - // Assign the added to the source to the target - _.assign(target, _.pick(source, diff.added)); - - return diff; -} diff --git a/src/legacy/ui/public/utils/math.test.ts b/src/legacy/ui/public/utils/math.test.ts deleted file mode 100644 index 13f090e77647b0..00000000000000 --- a/src/legacy/ui/public/utils/math.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { greatestCommonDivisor, leastCommonMultiple } from './math'; - -describe('math utils', () => { - describe('greatestCommonDivisor', () => { - const tests: Array<[number, number, number]> = [ - [3, 5, 1], - [30, 36, 6], - [5, 1, 1], - [9, 9, 9], - [40, 20, 20], - [3, 0, 3], - [0, 5, 5], - [0, 0, 0], - [-9, -3, 3], - [-24, 8, 8], - [22, -7, 1], - ]; - - tests.map(([a, b, expected]) => { - it(`should return ${expected} for greatestCommonDivisor(${a}, ${b})`, () => { - expect(greatestCommonDivisor(a, b)).toBe(expected); - }); - }); - }); - - describe('leastCommonMultiple', () => { - const tests: Array<[number, number, number]> = [ - [3, 5, 15], - [1, 1, 1], - [5, 6, 30], - [3, 9, 9], - [8, 20, 40], - [5, 5, 5], - [0, 5, 0], - [-4, -5, 20], - [-2, -3, 6], - [-8, 2, 8], - [-8, 5, 40], - ]; - - tests.map(([a, b, expected]) => { - it(`should return ${expected} for leastCommonMultiple(${a}, ${b})`, () => { - expect(leastCommonMultiple(a, b)).toBe(expected); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/vis/editors/config/editor_config_providers.ts b/src/legacy/ui/public/vis/editors/config/editor_config_providers.ts index 31fc0d90be1016..c7fb937b974245 100644 --- a/src/legacy/ui/public/vis/editors/config/editor_config_providers.ts +++ b/src/legacy/ui/public/vis/editors/config/editor_config_providers.ts @@ -21,7 +21,7 @@ import { TimeIntervalParam } from 'ui/vis/editors/config/types'; import { AggConfig } from '../..'; import { AggType } from '../../../agg_types'; import { IndexPattern } from '../../../../../../plugins/data/public'; -import { leastCommonMultiple } from '../../../utils/math'; +import { leastCommonMultiple } from '../../lib/least_common_multiple'; import { parseEsInterval } from '../../../../../core_plugins/data/public'; import { leastCommonInterval } from '../../lib/least_common_interval'; import { EditorConfig, EditorParamConfig, FixedParam, NumericIntervalParam } from './types'; diff --git a/src/legacy/ui/public/vis/lib/least_common_interval.ts b/src/legacy/ui/public/vis/lib/least_common_interval.ts index dfdb099249228f..244bc1d0111e3b 100644 --- a/src/legacy/ui/public/vis/lib/least_common_interval.ts +++ b/src/legacy/ui/public/vis/lib/least_common_interval.ts @@ -18,7 +18,7 @@ */ import dateMath from '@elastic/datemath'; -import { leastCommonMultiple } from '../../utils/math'; +import { leastCommonMultiple } from './least_common_multiple'; import { parseEsInterval } from '../../../../core_plugins/data/public'; /** diff --git a/src/legacy/ui/public/utils/case_conversion.ts b/src/legacy/ui/public/vis/lib/least_common_multiple.test.ts similarity index 60% rename from src/legacy/ui/public/utils/case_conversion.ts rename to src/legacy/ui/public/vis/lib/least_common_multiple.test.ts index e0c4cad6b4e940..d07ac96b5f4d5b 100644 --- a/src/legacy/ui/public/utils/case_conversion.ts +++ b/src/legacy/ui/public/vis/lib/least_common_multiple.test.ts @@ -17,20 +17,26 @@ * under the License. */ -// TODO: This file is copied from src/legacy/utils/case_conversion.ts -// because TS-imports from utils in ui are currently not possible. -// When the build process is updated, this file can be removed +import { leastCommonMultiple } from './least_common_multiple'; -import _ from 'lodash'; +describe('leastCommonMultiple', () => { + const tests: Array<[number, number, number]> = [ + [3, 5, 15], + [1, 1, 1], + [5, 6, 30], + [3, 9, 9], + [8, 20, 40], + [5, 5, 5], + [0, 5, 0], + [-4, -5, 20], + [-2, -3, 6], + [-8, 2, 8], + [-8, 5, 40], + ]; -export function keysToSnakeCaseShallow(object: Record) { - return _.mapKeys(object, (value, key) => { - return _.snakeCase(key); + tests.map(([a, b, expected]) => { + test(`should return ${expected} for leastCommonMultiple(${a}, ${b})`, () => { + expect(leastCommonMultiple(a, b)).toBe(expected); + }); }); -} - -export function keysToCamelCaseShallow(object: Record) { - return _.mapKeys(object, (value, key) => { - return _.camelCase(key); - }); -} +}); diff --git a/src/legacy/ui/public/utils/math.ts b/src/legacy/ui/public/vis/lib/least_common_multiple.ts similarity index 95% rename from src/legacy/ui/public/utils/math.ts rename to src/legacy/ui/public/vis/lib/least_common_multiple.ts index e0cab4236e2b8d..dedddbf22ab44b 100644 --- a/src/legacy/ui/public/utils/math.ts +++ b/src/legacy/ui/public/vis/lib/least_common_multiple.ts @@ -24,8 +24,10 @@ * This method does not properly work for fractional (non integer) numbers. If you * pass in fractional numbers there usually will be an output, but that's not necessarily * the greatest common divisor of those two numbers. + * + * @private */ -export function greatestCommonDivisor(a: number, b: number): number { +function greatestCommonDivisor(a: number, b: number): number { return a === 0 ? Math.abs(b) : greatestCommonDivisor(b % a, a); } @@ -36,6 +38,8 @@ export function greatestCommonDivisor(a: number, b: number): number { * Since this calculation suffers from rounding issues in decimal values, this method * won't work for passing in fractional (non integer) numbers. It will return a value, * but that value won't necessarily be the mathematical correct least common multiple. + * + * @internal */ export function leastCommonMultiple(a: number, b: number): number { return Math.abs((a * b) / greatestCommonDivisor(a, b)); diff --git a/src/legacy/utils/case_conversion.test.ts b/src/legacy/utils/case_conversion.test.ts deleted file mode 100644 index 27498f38789800..00000000000000 --- a/src/legacy/utils/case_conversion.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { keysToCamelCaseShallow, keysToSnakeCaseShallow } from './case_conversion'; - -describe('keysToSnakeCaseShallow', () => { - it("should convert all of an object's keys to snake case", () => { - const result = keysToSnakeCaseShallow({ - camelCase: 'camel_case', - 'kebab-case': 'kebab_case', - snake_case: 'snake_case', - }); - - expect(result).toMatchInlineSnapshot(` -Object { - "camel_case": "camel_case", - "kebab_case": "kebab_case", - "snake_case": "snake_case", -} -`); - }); -}); - -describe('keysToCamelCaseShallow', () => { - it("should convert all of an object's keys to camel case", () => { - const result = keysToCamelCaseShallow({ - camelCase: 'camelCase', - 'kebab-case': 'kebabCase', - snake_case: 'snakeCase', - }); - - expect(result).toMatchInlineSnapshot(` -Object { - "camelCase": "camelCase", - "kebabCase": "kebabCase", - "snakeCase": "snakeCase", -} -`); - }); -}); diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss index b446f1e57a895d..bb958406769698 100644 --- a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss +++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/_dashboard_viewport.scss @@ -1,9 +1,7 @@ .dshDashboardViewport { - height: 100%; width: 100%; } .dshDashboardViewport-withMargins { width: 100%; - height: 100%; } diff --git a/src/plugins/data/common/utils/index.ts b/src/plugins/data/common/utils/index.ts index 7196c96989e97d..c5f1276feb81dd 100644 --- a/src/plugins/data/common/utils/index.ts +++ b/src/plugins/data/common/utils/index.ts @@ -18,3 +18,4 @@ */ export { shortenDottedString } from './shorten_dotted_string'; +export { parseInterval } from './parse_interval'; diff --git a/src/plugins/data/common/utils/parse_interval.test.ts b/src/plugins/data/common/utils/parse_interval.test.ts new file mode 100644 index 00000000000000..0c02b02a25af0d --- /dev/null +++ b/src/plugins/data/common/utils/parse_interval.test.ts @@ -0,0 +1,119 @@ +/* + * 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 { Duration, unitOfTime } from 'moment'; +import { parseInterval } from './parse_interval'; + +const validateDuration = (duration: Duration | null, unit: unitOfTime.Base, value: number) => { + expect(duration).toBeDefined(); + + if (duration) { + expect(duration.as(unit)).toBe(value); + } +}; + +describe('parseInterval', () => { + describe('integer', () => { + test('should correctly parse 1d interval', () => { + validateDuration(parseInterval('1d'), 'd', 1); + }); + + test('should correctly parse 2y interval', () => { + validateDuration(parseInterval('2y'), 'y', 2); + }); + + test('should correctly parse 5M interval', () => { + validateDuration(parseInterval('5M'), 'M', 5); + }); + + test('should correctly parse 5m interval', () => { + validateDuration(parseInterval('5m'), 'm', 5); + }); + + test('should correctly parse 250ms interval', () => { + validateDuration(parseInterval('250ms'), 'ms', 250); + }); + + test('should correctly parse 100s interval', () => { + validateDuration(parseInterval('100s'), 's', 100); + }); + + test('should correctly parse 23d interval', () => { + validateDuration(parseInterval('23d'), 'd', 23); + }); + + test('should correctly parse 52w interval', () => { + validateDuration(parseInterval('52w'), 'w', 52); + }); + }); + + describe('fractional interval', () => { + test('should correctly parse fractional 2.35y interval', () => { + validateDuration(parseInterval('2.35y'), 'y', 2.35); + }); + + test('should correctly parse fractional 1.5w interval', () => { + validateDuration(parseInterval('1.5w'), 'w', 1.5); + }); + }); + + describe('less than 1', () => { + test('should correctly bubble up 0.5h interval which are less than 1', () => { + validateDuration(parseInterval('0.5h'), 'm', 30); + }); + + test('should correctly bubble up 0.5d interval which are less than 1', () => { + validateDuration(parseInterval('0.5d'), 'h', 12); + }); + }); + + describe('unit in an interval only', () => { + test('should correctly parse ms interval', () => { + validateDuration(parseInterval('ms'), 'ms', 1); + }); + + test('should correctly parse d interval', () => { + validateDuration(parseInterval('d'), 'd', 1); + }); + + test('should correctly parse m interval', () => { + validateDuration(parseInterval('m'), 'm', 1); + }); + + test('should correctly parse y interval', () => { + validateDuration(parseInterval('y'), 'y', 1); + }); + + test('should correctly parse M interval', () => { + validateDuration(parseInterval('M'), 'M', 1); + }); + }); + + test('should return null for an invalid interval', () => { + let duration = parseInterval(''); + expect(duration).toBeNull(); + + // @ts-ignore + duration = parseInterval(null); + expect(duration).toBeNull(); + + duration = parseInterval('234asdf'); + expect(duration).toBeNull(); + }); +}); diff --git a/src/legacy/ui/public/utils/parse_interval.js b/src/plugins/data/common/utils/parse_interval.ts similarity index 86% rename from src/legacy/ui/public/utils/parse_interval.js rename to src/plugins/data/common/utils/parse_interval.ts index fe484985ef101a..ef1d89e400b729 100644 --- a/src/legacy/ui/public/utils/parse_interval.js +++ b/src/plugins/data/common/utils/parse_interval.ts @@ -17,14 +17,14 @@ * under the License. */ -import _ from 'lodash'; -import moment from 'moment'; +import { find } from 'lodash'; +import moment, { unitOfTime } from 'moment'; import dateMath from '@elastic/datemath'; // Assume interval is in the form (value)(unit), such as "1h" const INTERVAL_STRING_RE = new RegExp('^([0-9\\.]*)\\s*(' + dateMath.units.join('|') + ')$'); -export function parseInterval(interval) { +export function parseInterval(interval: string): moment.Duration | null { const matches = String(interval) .trim() .match(INTERVAL_STRING_RE); @@ -33,7 +33,7 @@ export function parseInterval(interval) { try { const value = parseFloat(matches[1]) || 1; - const unit = matches[2]; + const unit = matches[2] as unitOfTime.Base; const duration = moment.duration(value, unit); @@ -44,9 +44,10 @@ export function parseInterval(interval) { // adding 0.5 days until we hit the end date. However, since there is a bug in moment, when you add 0.5 days to // the start date, you get the same exact date (instead of being ahead by 12 hours). So instead of returning // a duration corresponding to 0.5 hours, we return a duration corresponding to 12 hours. - const selectedUnit = _.find(dateMath.units, function(unit) { - return Math.abs(duration.as(unit)) >= 1; - }); + const selectedUnit = find( + dateMath.units, + u => Math.abs(duration.as(u)) >= 1 + ) as unitOfTime.Base; return moment.duration(duration.as(selectedUnit), selectedUnit); } catch (e) { diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 967887764237d2..4b330600417e7d 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -90,6 +90,8 @@ export { castEsToKbnFieldTypeName, getKbnFieldType, getKbnTypeNames, + // utils + parseInterval, } from '../common'; // Export plugin after all other imports diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index fe96c494bd9ff4..3cd088744a4391 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -18,7 +18,7 @@ */ import { PluginInitializerContext } from '../../../core/server'; -import { DataServerPlugin } from './plugin'; +import { DataServerPlugin, DataPluginSetup } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new DataServerPlugin(initializerContext); @@ -47,6 +47,8 @@ export { // timefilter RefreshInterval, TimeRange, + // utils + parseInterval, } from '../common'; /** @@ -91,4 +93,4 @@ export { getKbnTypeNames, } from '../common'; -export { DataServerPlugin as Plugin }; +export { DataServerPlugin as Plugin, DataPluginSetup as PluginSetup }; diff --git a/src/plugins/inspector/public/views/data/components/download_options.tsx b/src/plugins/inspector/public/views/data/components/download_options.tsx index 6d21dcdafa84d5..e7bfbed23c0746 100644 --- a/src/plugins/inspector/public/views/data/components/download_options.tsx +++ b/src/plugins/inspector/public/views/data/components/download_options.tsx @@ -20,6 +20,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { DataViewColumn, DataViewRow } from '../types'; @@ -66,8 +67,14 @@ class DataDownloadOptions extends Component { + let filename = this.props.title; + if (!filename || filename.length === 0) { + filename = i18n.translate('inspector.data.downloadOptionsUnsavedFilename', { + defaultMessage: 'unsaved', + }); + } exportAsCsv({ - filename: `${this.props.title}.csv`, + filename: `${filename}.csv`, columns: this.props.columns, rows: this.props.rows, csvSeparator: this.props.csvSeparator, diff --git a/src/plugins/testbed/server/index.ts b/src/plugins/testbed/server/index.ts index 586254603567bb..4873fe0926472b 100644 --- a/src/plugins/testbed/server/index.ts +++ b/src/plugins/testbed/server/index.ts @@ -17,7 +17,7 @@ * under the License. */ -import { map, mergeMap } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { schema, TypeOf } from '@kbn/config-schema'; import { @@ -108,9 +108,7 @@ class Plugin { return `Some exposed data derived from config: ${configValue.secret}`; }) ), - pingElasticsearch$: core.elasticsearch.adminClient$.pipe( - mergeMap(client => client.callAsInternalUser('ping')) - ), + pingElasticsearch: () => core.elasticsearch.adminClient.callAsInternalUser('ping'), }; } diff --git a/src/test_utils/public/simulate_keys.js b/src/test_utils/public/simulate_keys.js index 8ee4a79e6bc7c4..a876d67325c053 100644 --- a/src/test_utils/public/simulate_keys.js +++ b/src/test_utils/public/simulate_keys.js @@ -20,7 +20,7 @@ import $ from 'jquery'; import _ from 'lodash'; import Bluebird from 'bluebird'; -import { keyMap } from 'ui/utils/key_map'; +import { keyMap } from 'ui/directives/key_map'; const reverseKeyMap = _.mapValues(_.invert(keyMap), _.ary(_.parseInt, 1)); /** diff --git a/tasks/config/run.js b/tasks/config/run.js index a29061c9a72405..857895d75595cb 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -152,6 +152,7 @@ module.exports = function(grunt) { args: [ 'nyc', '--reporter=html', + '--reporter=json-summary', '--report-dir=./target/kibana-coverage/mocha', NODE, 'scripts/mocha', diff --git a/tasks/function_test_groups.js b/tasks/function_test_groups.js index 7854e2cd49837b..7b7293dc9a037d 100644 --- a/tasks/function_test_groups.js +++ b/tasks/function_test_groups.js @@ -29,6 +29,21 @@ const TEST_TAGS = safeLoad(JOBS_YAML) .JOB.filter(id => id.startsWith('kibana-ciGroup')) .map(id => id.replace(/^kibana-/, '')); +const getDefaultArgs = tag => { + return [ + 'scripts/functional_tests', + '--include-tag', + tag, + '--config', + 'test/functional/config.js', + '--config', + 'test/ui_capabilities/newsfeed_err/config.ts', + // '--config', 'test/functional/config.firefox.js', + '--bail', + '--debug', + ]; +}; + export function getFunctionalTestGroupRunConfigs({ kibanaInstallDir } = {}) { return { // include a run task for each test group @@ -38,18 +53,8 @@ export function getFunctionalTestGroupRunConfigs({ kibanaInstallDir } = {}) { [`functionalTests_${tag}`]: { cmd: process.execPath, args: [ - 'scripts/functional_tests', - '--include-tag', - tag, - '--config', - 'test/functional/config.js', - '--config', - 'test/ui_capabilities/newsfeed_err/config.ts', - // '--config', 'test/functional/config.firefox.js', - '--bail', - '--debug', - '--kibana-install-dir', - kibanaInstallDir, + ...getDefaultArgs(tag), + ...(!!process.env.CODE_COVERAGE ? [] : ['--kibana-install-dir', kibanaInstallDir]), ], }, }), diff --git a/test/api_integration/apis/ui_metric/ui_metric.js b/test/api_integration/apis/ui_metric/ui_metric.js index 5b02ba7e72430f..5ddbd8649589c7 100644 --- a/test/api_integration/apis/ui_metric/ui_metric.js +++ b/test/api_integration/apis/ui_metric/ui_metric.js @@ -25,15 +25,13 @@ export default function({ getService }) { const es = getService('legacyEs'); const createStatsMetric = eventName => ({ - key: ReportManager.createMetricKey({ appName: 'myApp', type: METRIC_TYPE.CLICK, eventName }), eventName, appName: 'myApp', type: METRIC_TYPE.CLICK, - stats: { sum: 1, avg: 1, min: 1, max: 1 }, + count: 1, }); const createUserAgentMetric = appName => ({ - key: ReportManager.createMetricKey({ appName, type: METRIC_TYPE.USER_AGENT }), appName, type: METRIC_TYPE.USER_AGENT, userAgent: @@ -42,12 +40,9 @@ export default function({ getService }) { describe('ui_metric API', () => { it('increments the count field in the document defined by the {app}/{action_type} path', async () => { + const reportManager = new ReportManager(); const uiStatsMetric = createStatsMetric('myEvent'); - const report = { - uiStatsMetrics: { - [uiStatsMetric.key]: uiStatsMetric, - }, - }; + const { report } = reportManager.assignReports([uiStatsMetric]); await supertest .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') @@ -61,21 +56,18 @@ export default function({ getService }) { }); it('supports multiple events', async () => { + const reportManager = new ReportManager(); const userAgentMetric = createUserAgentMetric('kibana'); const uiStatsMetric1 = createStatsMetric('myEvent'); const hrTime = process.hrtime(); const nano = hrTime[0] * 1000000000 + hrTime[1]; const uniqueEventName = `myEvent${nano}`; const uiStatsMetric2 = createStatsMetric(uniqueEventName); - const report = { - userAgent: { - [userAgentMetric.key]: userAgentMetric, - }, - uiStatsMetrics: { - [uiStatsMetric1.key]: uiStatsMetric1, - [uiStatsMetric2.key]: uiStatsMetric2, - }, - }; + const { report } = reportManager.assignReports([ + userAgentMetric, + uiStatsMetric1, + uiStatsMetric2, + ]); await supertest .post('/api/ui_metric/report') .set('kbn-xsrf', 'kibana') diff --git a/test/functional/apps/dashboard/dashboard_state.js b/test/functional/apps/dashboard/dashboard_state.js index 3caab3db44cb33..3b9e404e9b94d1 100644 --- a/test/functional/apps/dashboard/dashboard_state.js +++ b/test/functional/apps/dashboard/dashboard_state.js @@ -27,7 +27,14 @@ import { } from '../../../../src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_constants'; export default function({ getService, getPageObjects }) { - const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'discover']); + const PageObjects = getPageObjects([ + 'dashboard', + 'visualize', + 'header', + 'discover', + 'tileMap', + 'visChart', + ]); const testSubjects = getService('testSubjects'); const browser = getService('browser'); const queryBar = getService('queryBar'); @@ -58,14 +65,14 @@ export default function({ getService, getPageObjects }) { await PageObjects.dashboard.switchToEditMode(); - await PageObjects.visualize.openLegendOptionColors('Count'); - await PageObjects.visualize.selectNewLegendColorChoice('#EA6460'); + await PageObjects.visChart.openLegendOptionColors('Count'); + await PageObjects.visChart.selectNewLegendColorChoice('#EA6460'); await PageObjects.dashboard.saveDashboard('Overridden colors'); await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.dashboard.loadSavedDashboard('Overridden colors'); - const colorChoiceRetained = await PageObjects.visualize.doesSelectedLegendColorExist( + const colorChoiceRetained = await PageObjects.visChart.doesSelectedLegendColorExist( '#EA6460' ); @@ -153,10 +160,10 @@ export default function({ getService, getPageObjects }) { await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickEdit(); - await PageObjects.visualize.clickMapZoomIn(); - await PageObjects.visualize.clickMapZoomIn(); - await PageObjects.visualize.clickMapZoomIn(); - await PageObjects.visualize.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); await PageObjects.visualize.saveVisualizationExpectSuccess('Visualization TileMap'); @@ -225,8 +232,8 @@ export default function({ getService, getPageObjects }) { describe('for embeddable config color parameters on a visualization', () => { it('updates a pie slice color on a soft refresh', async function() { await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME); - await PageObjects.visualize.openLegendOptionColors('80,000'); - await PageObjects.visualize.selectNewLegendColorChoice('#F9D9F9'); + await PageObjects.visChart.openLegendOptionColors('80,000'); + await PageObjects.visChart.selectNewLegendColorChoice('#F9D9F9'); const currentUrl = await browser.getCurrentUrl(); const newUrl = currentUrl.replace('F9D9F9', 'FFFFFF'); await browser.get(newUrl.toString(), false); @@ -248,7 +255,7 @@ export default function({ getService, getPageObjects }) { // Unskip once https://github.com/elastic/kibana/issues/15736 is fixed. it.skip('and updates the pie slice legend color', async function() { await retry.try(async () => { - const colorExists = await PageObjects.visualize.doesSelectedLegendColorExist('#FFFFFF'); + const colorExists = await PageObjects.visChart.doesSelectedLegendColorExist('#FFFFFF'); expect(colorExists).to.be(true); }); }); @@ -269,7 +276,7 @@ export default function({ getService, getPageObjects }) { // Unskip once https://github.com/elastic/kibana/issues/15736 is fixed. it.skip('resets the legend color as well', async function() { await retry.try(async () => { - const colorExists = await PageObjects.visualize.doesSelectedLegendColorExist('#57c17b'); + const colorExists = await PageObjects.visChart.doesSelectedLegendColorExist('#57c17b'); expect(colorExists).to.be(true); }); }); diff --git a/test/functional/apps/getting_started/_shakespeare.js b/test/functional/apps/getting_started/_shakespeare.js index 04d81f4b46083d..5af1676cf423fb 100644 --- a/test/functional/apps/getting_started/_shakespeare.js +++ b/test/functional/apps/getting_started/_shakespeare.js @@ -23,7 +23,14 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const esArchiver = getService('esArchiver'); const retry = getService('retry'); - const PageObjects = getPageObjects(['console', 'common', 'settings', 'visualize']); + const PageObjects = getPageObjects([ + 'console', + 'common', + 'settings', + 'visualize', + 'visEditor', + 'visChart', + ]); // https://www.elastic.co/guide/en/kibana/current/tutorial-load-dataset.html @@ -63,11 +70,11 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVerticalBarChart(); await PageObjects.visualize.clickNewSearch('shakes*'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); const expectedChartValues = [111396]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Count'); + const data = await PageObjects.visChart.getBarChartData('Count'); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data[0] - expectedChartValues[0]).to.be.lessThan(5); @@ -84,22 +91,22 @@ export default function({ getService, getPageObjects }) { it('should configure metric Unique Count Speaking Parts', async function() { log.debug('Metric = Unique Count, speaker, Speaking Parts'); // this first change to the YAxis metric agg uses the default aggIndex of 1 - await PageObjects.visualize.selectYAxisAggregation( + await PageObjects.visEditor.selectYAxisAggregation( 'Unique Count', 'speaker', 'Speaking Parts' ); // then increment the aggIndex for the next one we create aggIndex = aggIndex + 1; - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const expectedChartValues = [935]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Speaking Parts'); + const data = await PageObjects.visChart.getBarChartData('Speaking Parts'); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); }); - const title = await PageObjects.visualize.getYAxisTitle(); + const title = await PageObjects.visChart.getYAxisTitle(); expect(title).to.be('Speaking Parts'); }); @@ -110,23 +117,23 @@ export default function({ getService, getPageObjects }) { 5. Click Apply changes images/apply-changes-button.png to view the results. */ it('should configure Terms aggregation on play_name', async function() { - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Aggregation = Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); aggIndex = aggIndex + 1; log.debug('Field = play_name'); - await PageObjects.visualize.selectField('play_name'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('play_name'); + await PageObjects.visEditor.clickGo(); const expectedChartValues = [71, 65, 62, 55, 55]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Speaking Parts'); + const data = await PageObjects.visChart.getBarChartData('Speaking Parts'); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); }); - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); expect(labels).to.eql([ 'Richard III', 'Henry VI Part 2', @@ -145,21 +152,21 @@ export default function({ getService, getPageObjects }) { 2. Choose the Max aggregation and select the speech_number field. */ it('should configure Max aggregation metric on speech_number', async function() { - await PageObjects.visualize.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); log.debug('Aggregation = Max'); - await PageObjects.visualize.selectYAxisAggregation( + await PageObjects.visEditor.selectYAxisAggregation( 'Max', 'speech_number', 'Max Speaking Parts', aggIndex ); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const expectedChartValues = [71, 65, 62, 55, 55]; const expectedChartValues2 = [177, 106, 153, 132, 162]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Speaking Parts'); - const data2 = await PageObjects.visualize.getBarChartData('Max Speaking Parts'); + const data = await PageObjects.visChart.getBarChartData('Speaking Parts'); + const data2 = await PageObjects.visChart.getBarChartData('Max Speaking Parts'); log.debug('data=' + data); log.debug('data.length=' + data.length); log.debug('data2=' + data2); @@ -168,7 +175,7 @@ export default function({ getService, getPageObjects }) { expect(data2).to.eql(expectedChartValues2); }); - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); expect(labels).to.eql([ 'Richard III', 'Henry VI Part 2', @@ -184,15 +191,15 @@ export default function({ getService, getPageObjects }) { 4. Click Apply changes images/apply-changes-button.png. Your chart should now look like this: */ it('should configure change options to normal bars', async function() { - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.selectChartMode('normal'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.selectChartMode('normal'); + await PageObjects.visEditor.clickGo(); const expectedChartValues = [71, 65, 62, 55, 55]; const expectedChartValues2 = [177, 106, 153, 132, 162]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Speaking Parts'); - const data2 = await PageObjects.visualize.getBarChartData('Max Speaking Parts'); + const data = await PageObjects.visChart.getBarChartData('Speaking Parts'); + const data2 = await PageObjects.visChart.getBarChartData('Max Speaking Parts'); log.debug('data=' + data); log.debug('data.length=' + data.length); log.debug('data2=' + data2); @@ -210,15 +217,15 @@ export default function({ getService, getPageObjects }) { Save this chart with the name Bar Example. */ it('should change the Y-Axis extents', async function() { - await PageObjects.visualize.setAxisExtents('50', '250'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setAxisExtents('50', '250'); + await PageObjects.visEditor.clickGo(); // same values as previous test except scaled down by the 50 for Y-Axis min const expectedChartValues = [21, 15, 12, 5, 5]; const expectedChartValues2 = [127, 56, 103, 82, 112]; await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData('Speaking Parts'); - const data2 = await PageObjects.visualize.getBarChartData('Max Speaking Parts'); + const data = await PageObjects.visChart.getBarChartData('Speaking Parts'); + const data2 = await PageObjects.visChart.getBarChartData('Max Speaking Parts'); log.debug('data=' + data); log.debug('data.length=' + data.length); log.debug('data2=' + data2); diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index b8fa253f721047..e52cfdf478c33c 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -24,7 +24,14 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const browser = getService('browser'); const retry = getService('retry'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'header', + 'timePicker', + ]); describe('area charts', function indexPatternCreation() { const vizName1 = 'Visualization AreaChart Name Test'; @@ -38,17 +45,17 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Click X-axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Click Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); log.debug('Check field value'); - const fieldValues = await PageObjects.visualize.getField(); + const fieldValues = await PageObjects.visEditor.getField(); log.debug('fieldValue = ' + fieldValues); expect(fieldValues[0]).to.be('@timestamp'); - const intervalValue = await PageObjects.visualize.getInterval(); + const intervalValue = await PageObjects.visEditor.getInterval(); log.debug('intervalValue = ' + intervalValue); expect(intervalValue[0]).to.be('Auto'); - return PageObjects.visualize.clickGo(); + return PageObjects.visEditor.clickGo(); }; before(initAreaChart); @@ -70,7 +77,7 @@ export default function({ getService, getPageObjects }) { it('should save and load', async function() { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -113,14 +120,14 @@ export default function({ getService, getPageObjects }) { ]; await retry.try(async function tryingForTime() { - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); log.debug('X-Axis labels = ' + labels); expect(labels).to.eql(xAxisLabels); }); - const labels = await PageObjects.visualize.getYAxisLabels(); + const labels = await PageObjects.visChart.getYAxisLabels(); log.debug('Y-Axis labels = ' + labels); expect(labels).to.eql(yAxisLabels); - const paths = await PageObjects.visualize.getAreaChartData('Count'); + const paths = await PageObjects.visChart.getAreaChartData('Count'); log.debug('expectedAreaChartData = ' + expectedAreaChartData); log.debug('actual chart data = ' + paths); expect(paths).to.eql(expectedAreaChartData); @@ -185,9 +192,9 @@ export default function({ getService, getPageObjects }) { ['2015-09-20 19:00', '55'], ]; - await PageObjects.visualize.toggleOpenEditor(2); - await PageObjects.visualize.setInterval('Second'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2); + await PageObjects.visEditor.setInterval('Second'); + await PageObjects.visEditor.clickGo(); await inspector.open(); await inspector.expectTableData(expectedTableData); await inspector.close(); @@ -217,9 +224,9 @@ export default function({ getService, getPageObjects }) { ['2015-09-20 19:00', '0.015'], ]; - await PageObjects.visualize.toggleAdvancedParams('2'); - await PageObjects.visualize.toggleScaleMetrics(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleAdvancedParams('2'); + await PageObjects.visEditor.toggleScaleMetrics(); + await PageObjects.visEditor.clickGo(); await inspector.open(); await inspector.expectTableData(expectedTableData); await inspector.close(); @@ -249,11 +256,11 @@ export default function({ getService, getPageObjects }) { ['2015-09-20 19:00', '55', '2.053KB'], ]; - await PageObjects.visualize.clickBucket('Y-axis', 'metrics'); - await PageObjects.visualize.selectAggregation('Top Hit', 'metrics'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.selectAggregateWith('average'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.selectAggregation('Top Hit', 'metrics'); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.selectAggregateWith('average'); + await PageObjects.visEditor.clickGo(); await inspector.open(); await inspector.expectTableData(expectedTableData); await inspector.close(); @@ -285,13 +292,13 @@ export default function({ getService, getPageObjects }) { const axisId = 'ValueAxis-1'; it('should show ticks on selecting log scale', async () => { - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions(axisId); - await PageObjects.visualize.selectYAxisScaleType(axisId, 'log'); - await PageObjects.visualize.clickYAxisAdvancedOptions(axisId); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions(axisId); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'log'); + await PageObjects.visEditor.clickYAxisAdvancedOptions(axisId); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -317,9 +324,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting log scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -345,10 +352,10 @@ export default function({ getService, getPageObjects }) { }); it('should show ticks on selecting square root scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'square root'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'square root'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '0', '200', @@ -364,18 +371,18 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting square root scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); it('should show ticks on selecting linear scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'linear'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'linear'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); const expectedLabels = [ '0', @@ -392,9 +399,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting linear scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); @@ -412,16 +419,16 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch('long-window-logstash-*'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); log.debug('Click X-axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Click Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Yearly'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Yearly'); + await PageObjects.visEditor.clickGo(); // This svg area is composed by 7 years (2013 - 2019). // 7 points are used to draw the upper line (usually called y1) // 7 points compose the lower line (usually called y0) - const paths = await PageObjects.visualize.getAreaChartPaths('Count'); + const paths = await PageObjects.visChart.getAreaChartPaths('Count'); log.debug('actual chart data = ' + paths); const numberOfSegments = 7 * 2; expect(paths.length).to.eql(numberOfSegments); @@ -435,17 +442,17 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch('long-window-logstash-*'); await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); log.debug('Click X-axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Click Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Monthly'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Monthly'); + await PageObjects.visEditor.clickGo(); // This svg area is composed by 67 months 3 (2013) + 5 * 12 + 4 (2019) // 67 points are used to draw the upper line (usually called y1) // 67 points compose the lower line (usually called y0) const numberOfSegments = 67 * 2; - const paths = await PageObjects.visualize.getAreaChartPaths('Count'); + const paths = await PageObjects.visChart.getAreaChartPaths('Count'); log.debug('actual chart data = ' + paths); expect(paths.length).to.eql(numberOfSegments); }); diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index e8fe8fb656877f..0a9ff1e77a2ef1 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -22,9 +22,10 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const log = getService('log'); const inspector = getService('inspector'); + const testSubjects = getService('testSubjects'); const retry = getService('retry'); const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'timePicker', 'visEditor', 'visChart']); describe('data table', function indexPatternCreation() { const vizName1 = 'Visualization DataTable'; @@ -38,27 +39,27 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split rows'); - await PageObjects.visualize.clickBucket('Split rows'); + await PageObjects.visEditor.clickBucket('Split rows'); log.debug('Aggregation = Histogram'); - await PageObjects.visualize.selectAggregation('Histogram'); + await PageObjects.visEditor.selectAggregation('Histogram'); log.debug('Field = bytes'); - await PageObjects.visualize.selectField('bytes'); + await PageObjects.visEditor.selectField('bytes'); log.debug('Interval = 2000'); - await PageObjects.visualize.setNumericInterval('2000'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setInterval('2000', { type: 'numeric' }); + await PageObjects.visEditor.clickGo(); }); it('should allow applying changed params', async () => { - await PageObjects.visualize.setNumericInterval('1', { append: true }); - const interval = await PageObjects.visualize.getNumericInterval(); + await PageObjects.visEditor.setInterval('1', { type: 'numeric', append: true }); + const interval = await PageObjects.visEditor.getNumericInterval(); expect(interval).to.be('20001'); - const isApplyButtonEnabled = await PageObjects.visualize.isApplyEnabled(); + const isApplyButtonEnabled = await PageObjects.visEditor.isApplyEnabled(); expect(isApplyButtonEnabled).to.be(true); }); it('should allow reseting changed params', async () => { - await PageObjects.visualize.clickReset(); - const interval = await PageObjects.visualize.getNumericInterval(); + await PageObjects.visEditor.clickReset(); + const interval = await PageObjects.visEditor.getNumericInterval(); expect(interval).to.be('2000'); }); @@ -66,7 +67,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -96,7 +97,7 @@ export default function({ getService, getPageObjects }) { it('should show percentage columns', async () => { async function expectValidTableData() { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '≥ 0 and < 1000', '1,351 64.7%', @@ -110,16 +111,16 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Range'); - await PageObjects.visualize.selectField('bytes'); - await PageObjects.visualize.clickGo(); - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Range'); + await PageObjects.visEditor.selectField('bytes'); + await PageObjects.visEditor.clickGo(); + await PageObjects.visEditor.clickOptionsTab(); + await PageObjects.visEditor.setSelectByOptionText( 'datatableVisualizationPercentageCol', 'Count' ); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await expectValidTableData(); @@ -128,20 +129,20 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(SAVE_NAME); await PageObjects.visualize.loadSavedVisualization(SAVE_NAME); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); await expectValidTableData(); // check that it works after selecting a column that's deleted - await PageObjects.visualize.clickData(); - await PageObjects.visualize.clickBucket('Metric', 'metrics'); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.removeDimension(1); - await PageObjects.visualize.clickGo(); - await PageObjects.visualize.clickOptionsTab(); - - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickDataTab(); + await PageObjects.visEditor.clickBucket('Metric', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.removeDimension(1); + await PageObjects.visEditor.clickGo(); + await PageObjects.visEditor.clickOptionsTab(); + + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '≥ 0 and < 1000', '344.094B', @@ -155,12 +156,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Metric', 'metrics'); - await PageObjects.visualize.selectAggregation('Average Bucket', 'metrics'); - await PageObjects.visualize.selectAggregation('Terms', 'metrics', 'buckets'); - await PageObjects.visualize.selectField('geo.src', 'metrics', 'buckets'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Metric', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average Bucket', 'metrics'); + await PageObjects.visEditor.selectAggregation('Terms', 'metrics', 'buckets'); + await PageObjects.visEditor.selectField('geo.src', 'metrics', 'buckets'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql(['14,004 1,412.6']); }); @@ -170,12 +171,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Daily'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Daily'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql([ '2015-09-20', @@ -192,12 +193,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Daily'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Daily'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '2015-09-20', '4,757', @@ -210,15 +211,15 @@ export default function({ getService, getPageObjects }) { it('should correctly filter for applied time filter on the main timefield', async () => { await filterBar.addFilter('@timestamp', 'is between', '2015-09-19', '2015-09-21'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); }); it('should correctly filter for pinned filters', async () => { await filterBar.toggleFilterPinned('@timestamp'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); }); @@ -227,11 +228,11 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickMetricEditor(); - await PageObjects.visualize.selectAggregation('Top Hit', 'metrics'); - await PageObjects.visualize.selectField('agent.raw', 'metrics'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickMetricEditor(); + await PageObjects.visEditor.selectAggregation('Top Hit', 'metrics'); + await PageObjects.visEditor.selectField('agent.raw', 'metrics'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data); expect(data.length).to.be.greaterThan(0); }); @@ -241,11 +242,11 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Range'); - await PageObjects.visualize.selectField('bytes'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Range'); + await PageObjects.visEditor.selectField('bytes'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '≥ 0 and < 1000', '1,351', @@ -260,19 +261,19 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('extension.raw'); - await PageObjects.visualize.setSize(2); - await PageObjects.visualize.clickGo(); - - await PageObjects.visualize.toggleOtherBucket(); - await PageObjects.visualize.toggleMissingBucket(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('extension.raw'); + await PageObjects.visEditor.setSize(2); + await PageObjects.visEditor.clickGo(); + + await PageObjects.visEditor.toggleOtherBucket(); + await PageObjects.visEditor.toggleMissingBucket(); + await PageObjects.visEditor.clickGo(); }); it('should show correct data', async () => { - const data = await PageObjects.visualize.getTableVisContent(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['jpg', '9,109'], ['css', '2,159'], @@ -281,9 +282,9 @@ export default function({ getService, getPageObjects }) { }); it('should apply correct filter', async () => { - await PageObjects.visualize.filterOnTableCell(1, 3); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - const data = await PageObjects.visualize.getTableVisContent(); + await PageObjects.visChart.filterOnTableCell(1, 3); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['png', '1,373'], ['gif', '918'], @@ -298,20 +299,20 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('extension.raw'); - await PageObjects.visualize.setSize(2); - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('geo.dest'); - await PageObjects.visualize.toggleOpenEditor(3, 'false'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('extension.raw'); + await PageObjects.visEditor.setSize(2); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('geo.dest'); + await PageObjects.visEditor.toggleOpenEditor(3, 'false'); + await PageObjects.visEditor.clickGo(); }); it('should show correct data without showMetricsAtAllLevels', async () => { - const data = await PageObjects.visualize.getTableVisContent(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['jpg', 'CN', '1,718'], ['jpg', 'IN', '1,511'], @@ -327,10 +328,10 @@ export default function({ getService, getPageObjects }) { }); it('should show correct data without showMetricsAtAllLevels even if showPartialRows is selected', async () => { - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.checkCheckbox('showPartialRows'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisContent(); + await PageObjects.visEditor.clickOptionsTab(); + await testSubjects.setCheckbox('showPartialRows', 'check'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['jpg', 'CN', '1,718'], ['jpg', 'IN', '1,511'], @@ -346,10 +347,10 @@ export default function({ getService, getPageObjects }) { }); it('should show metrics on each level', async () => { - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisContent(); + await PageObjects.visEditor.clickOptionsTab(); + await testSubjects.setCheckbox('showMetricsAtAllLevels', 'check'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['jpg', '9,109', 'CN', '1,718'], ['jpg', '9,109', 'IN', '1,511'], @@ -365,12 +366,12 @@ export default function({ getService, getPageObjects }) { }); it('should show metrics other than count on each level', async () => { - await PageObjects.visualize.clickData(); - await PageObjects.visualize.clickBucket('Metric', 'metrics'); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisContent(); + await PageObjects.visEditor.clickDataTab(); + await PageObjects.visEditor.clickBucket('Metric', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ ['jpg', '9,109', '5.469KB', 'CN', '1,718', '5.477KB'], ['jpg', '9,109', '5.469KB', 'IN', '1,511', '5.456KB'], @@ -392,26 +393,26 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split table'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('extension.raw'); - await PageObjects.visualize.setSize(2); - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('geo.dest'); - await PageObjects.visualize.setSize(3, 3); - await PageObjects.visualize.toggleOpenEditor(3, 'false'); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('geo.src'); - await PageObjects.visualize.setSize(3, 4); - await PageObjects.visualize.toggleOpenEditor(4, 'false'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split table'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('extension.raw'); + await PageObjects.visEditor.setSize(2); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('geo.dest'); + await PageObjects.visEditor.setSize(3, 3); + await PageObjects.visEditor.toggleOpenEditor(3, 'false'); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('geo.src'); + await PageObjects.visEditor.setSize(3, 4); + await PageObjects.visEditor.toggleOpenEditor(4, 'false'); + await PageObjects.visEditor.clickGo(); }); it('should have a splitted table', async () => { - const data = await PageObjects.visualize.getTableVisContent(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ [ ['CN', 'CN', '330'], @@ -439,10 +440,10 @@ export default function({ getService, getPageObjects }) { }); it('should show metrics for split bucket when using showMetricsAtAllLevels', async () => { - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisContent(); + await PageObjects.visEditor.clickOptionsTab(); + await testSubjects.setCheckbox('showMetricsAtAllLevels', 'check'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ [ ['CN', '1,718', 'CN', '330'], diff --git a/test/functional/apps/visualize/_data_table_nontimeindex.js b/test/functional/apps/visualize/_data_table_nontimeindex.js index 77478f5d10edcb..3db3cd094a81b4 100644 --- a/test/functional/apps/visualize/_data_table_nontimeindex.js +++ b/test/functional/apps/visualize/_data_table_nontimeindex.js @@ -25,7 +25,7 @@ export default function({ getService, getPageObjects }) { const retry = getService('retry'); const filterBar = getService('filterBar'); const renderable = getService('renderable'); - const PageObjects = getPageObjects(['common', 'visualize', 'header']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'header', 'visChart']); describe.skip('data table with index without time filter', function indexPatternCreation() { const vizName1 = 'Visualization DataTable without time filter'; @@ -40,27 +40,27 @@ export default function({ getService, getPageObjects }) { PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED ); log.debug('Bucket = Split Rows'); - await PageObjects.visualize.clickBucket('Split rows'); + await PageObjects.visEditor.clickBucket('Split rows'); log.debug('Aggregation = Histogram'); - await PageObjects.visualize.selectAggregation('Histogram'); + await PageObjects.visEditor.selectAggregation('Histogram'); log.debug('Field = bytes'); - await PageObjects.visualize.selectField('bytes'); + await PageObjects.visEditor.selectField('bytes'); log.debug('Interval = 2000'); - await PageObjects.visualize.setNumericInterval('2000'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setInterval('2000', { type: 'numeric' }); + await PageObjects.visEditor.clickGo(); }); it('should allow applying changed params', async () => { - await PageObjects.visualize.setNumericInterval('1', { append: true }); - const interval = await PageObjects.visualize.getNumericInterval(); + await PageObjects.visEditor.setInterval('1', { type: 'numeric', append: true }); + const interval = await PageObjects.visEditor.getNumericInterval(); expect(interval).to.be('20001'); - const isApplyButtonEnabled = await PageObjects.visualize.isApplyEnabled(); + const isApplyButtonEnabled = await PageObjects.visEditor.isApplyEnabled(); expect(isApplyButtonEnabled).to.be(true); }); it('should allow reseting changed params', async () => { - await PageObjects.visualize.clickReset(); - const interval = await PageObjects.visualize.getNumericInterval(); + await PageObjects.visEditor.clickReset(); + const interval = await PageObjects.visEditor.getNumericInterval(); expect(interval).to.be('2000'); }); @@ -68,7 +68,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -102,12 +102,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch( PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED ); - await PageObjects.visualize.clickBucket('Metric', 'metrics'); - await PageObjects.visualize.selectAggregation('Average Bucket', 'metrics'); - await PageObjects.visualize.selectAggregation('Terms', 'metrics', 'buckets'); - await PageObjects.visualize.selectField('geo.src', 'metrics', 'buckets'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Metric', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average Bucket', 'metrics'); + await PageObjects.visEditor.selectAggregation('Terms', 'metrics', 'buckets'); + await PageObjects.visEditor.selectField('geo.src', 'metrics', 'buckets'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql(['14,004 1,412.6']); }); @@ -118,12 +118,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch( PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED ); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Daily'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Daily'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql([ '2015-09-20', @@ -141,12 +141,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch( PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED ); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setInterval('Daily'); - await PageObjects.visualize.clickGo(); - const data = await PageObjects.visualize.getTableVisData(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('Daily'); + await PageObjects.visEditor.clickGo(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '2015-09-20', '4,757', @@ -161,7 +161,7 @@ export default function({ getService, getPageObjects }) { await filterBar.addFilter('@timestamp', 'is between', '2015-09-19', '2015-09-21'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); }); @@ -169,7 +169,7 @@ export default function({ getService, getPageObjects }) { await filterBar.toggleFilterPinned('@timestamp'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql(['2015-09-20', '4,757']); }); }); diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js index c16dc3b1279ff7..940aa3eb5d4628 100644 --- a/test/functional/apps/visualize/_embedding_chart.js +++ b/test/functional/apps/visualize/_embedding_chart.js @@ -24,7 +24,13 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const renderable = getService('renderable'); const embedding = getService('embedding'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects([ + 'visualize', + 'visEditor', + 'visChart', + 'header', + 'timePicker', + ]); describe('embedding', () => { describe('a data table', () => { @@ -33,22 +39,22 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickDataTable(); await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Date Histogram'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split rows'); - await PageObjects.visualize.selectAggregation('Histogram'); - await PageObjects.visualize.selectField('bytes'); - await PageObjects.visualize.setNumericInterval('2000', undefined, 3); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split rows'); + await PageObjects.visEditor.selectAggregation('Histogram'); + await PageObjects.visEditor.selectField('bytes'); + await PageObjects.visEditor.setInterval('2000', { type: 'numeric', aggNth: 3 }); + await PageObjects.visEditor.clickGo(); }); it('should allow opening table vis in embedded mode', async () => { await embedding.openInEmbeddedMode(); await renderable.waitForRender(); - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql([ '2015-09-20 00:00', @@ -89,7 +95,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql([ '2015-09-21 00:00', @@ -126,11 +132,11 @@ export default function({ getService, getPageObjects }) { }); it('should allow to change timerange from the visualization in embedded mode', async () => { - await PageObjects.visualize.filterOnTableCell(1, 7); + await PageObjects.visChart.filterOnTableCell(1, 7); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); log.debug(data.split('\n')); expect(data.trim().split('\n')).to.be.eql([ '03:00', diff --git a/test/functional/apps/visualize/_experimental_vis.js b/test/functional/apps/visualize/_experimental_vis.js index ae364244b4f5c0..2ce15cf913eff1 100644 --- a/test/functional/apps/visualize/_experimental_vis.js +++ b/test/functional/apps/visualize/_experimental_vis.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default ({ getService, getPageObjects }) => { const log = getService('log'); - const PageObjects = getPageObjects(['common', 'visualize']); + const PageObjects = getPageObjects(['visualize']); describe('visualize app', function() { this.tags('smoke'); diff --git a/test/functional/apps/visualize/_gauge_chart.js b/test/functional/apps/visualize/_gauge_chart.js index 2b9033d1ae9d2b..7ebb4548f967ba 100644 --- a/test/functional/apps/visualize/_gauge_chart.js +++ b/test/functional/apps/visualize/_gauge_chart.js @@ -24,7 +24,7 @@ export default function({ getService, getPageObjects }) { const retry = getService('retry'); const inspector = getService('inspector'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); // FLAKY: https://github.com/elastic/kibana/issues/45089 describe('gauge chart', function indexPatternCreation() { @@ -50,24 +50,24 @@ export default function({ getService, getPageObjects }) { // initial metric of "Count" is selected by default return retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getGaugeValue(); + const metricValue = await PageObjects.visChart.getGaugeValue(); expect(expectedCount).to.eql(metricValue); }); }); it('should show Split Gauges', async function() { log.debug('Bucket = Split Group'); - await PageObjects.visualize.clickBucket('Split group'); + await PageObjects.visEditor.clickBucket('Split group'); log.debug('Aggregation = Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Field = machine.os.raw'); - await PageObjects.visualize.selectField('machine.os.raw'); + await PageObjects.visEditor.selectField('machine.os.raw'); log.debug('Size = 4'); - await PageObjects.visualize.setSize('4'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setSize('4'); + await PageObjects.visEditor.clickGo(); await retry.try(async () => { - expect(await PageObjects.visualize.getGaugeValue()).to.eql([ + expect(await PageObjects.visChart.getGaugeValue()).to.eql([ '2,904', 'win 8', '2,858', @@ -83,34 +83,34 @@ export default function({ getService, getPageObjects }) { it('should show correct values for fields with fieldFormatters', async function() { const expectedTexts = ['2,904', 'win 8: Count', '0B', 'win 8: Min bytes']; - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.setSize('1'); - await PageObjects.visualize.clickBucket('Metric', 'metrics'); - await PageObjects.visualize.selectAggregation('Min', 'metrics'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.setSize('1'); + await PageObjects.visEditor.clickBucket('Metric', 'metrics'); + await PageObjects.visEditor.selectAggregation('Min', 'metrics'); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getGaugeValue(); + const metricValue = await PageObjects.visChart.getGaugeValue(); expect(expectedTexts).to.eql(metricValue); }); }); it('should format the metric correctly in percentage mode', async function() { await initGaugeVis(); - await PageObjects.visualize.clickMetricEditor(); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.clickOptionsTab(); + await PageObjects.visEditor.clickMetricEditor(); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.clickOptionsTab(); await testSubjects.setValue('gaugeColorRange2__to', '10000'); await testSubjects.click('gaugePercentageMode'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { const expectedTexts = ['57.273%', 'Average bytes']; - const metricValue = await PageObjects.visualize.getGaugeValue(); + const metricValue = await PageObjects.visChart.getGaugeValue(); expect(expectedTexts).to.eql(metricValue); }); }); diff --git a/test/functional/apps/visualize/_heatmap_chart.js b/test/functional/apps/visualize/_heatmap_chart.js index 226e490a6b2321..2cea861d0f64df 100644 --- a/test/functional/apps/visualize/_heatmap_chart.js +++ b/test/functional/apps/visualize/_heatmap_chart.js @@ -22,7 +22,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const log = getService('log'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); describe('heatmap chart', function indexPatternCreation() { this.tags('smoke'); @@ -36,20 +36,20 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-Axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); log.debug('Field = @timestamp'); - await PageObjects.visualize.selectField('@timestamp'); + await PageObjects.visEditor.selectField('@timestamp'); // leaving Interval set to Auto - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }); it('should save and load', async function() { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -87,16 +87,16 @@ export default function({ getService, getPageObjects }) { }); it('should show 4 color ranges as default colorNumbers param', async function() { - const legends = await PageObjects.visualize.getLegendEntries(); + const legends = await PageObjects.visChart.getLegendEntries(); const expectedLegends = ['0 - 400', '400 - 800', '800 - 1,200', '1,200 - 1,600']; expect(legends).to.eql(expectedLegends); }); it('should show 6 color ranges if changed on options', async function() { - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.changeHeatmapColorNumbers(6); - await PageObjects.visualize.clickGo(); - const legends = await PageObjects.visualize.getLegendEntries(); + await PageObjects.visEditor.clickOptionsTab(); + await PageObjects.visEditor.changeHeatmapColorNumbers(6); + await PageObjects.visEditor.clickGo(); + const legends = await PageObjects.visChart.getLegendEntries(); const expectedLegends = [ '0 - 267', '267 - 534', @@ -108,23 +108,23 @@ export default function({ getService, getPageObjects }) { expect(legends).to.eql(expectedLegends); }); it('should show 6 custom color ranges', async function() { - await PageObjects.visualize.clickOptionsTab(); - await PageObjects.visualize.clickEnableCustomRanges(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); - await PageObjects.visualize.clickAddRange(); + await PageObjects.visEditor.clickOptionsTab(); + await PageObjects.visEditor.clickEnableCustomRanges(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); + await PageObjects.visEditor.clickAddRange(); log.debug('customize 2 last ranges'); - await PageObjects.visualize.setCustomRangeByIndex(6, '650', '720'); - await PageObjects.visualize.setCustomRangeByIndex(7, '800', '905'); + await PageObjects.visEditor.setCustomRangeByIndex(6, '650', '720'); + await PageObjects.visEditor.setCustomRangeByIndex(7, '800', '905'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); - const legends = await PageObjects.visualize.getLegendEntries(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); + const legends = await PageObjects.visChart.getLegendEntries(); const expectedLegends = [ '0 - 100', '100 - 200', diff --git a/test/functional/apps/visualize/_histogram_request_start.js b/test/functional/apps/visualize/_histogram_request_start.js index a601e370a568f6..a74fa8856a3b3c 100644 --- a/test/functional/apps/visualize/_histogram_request_start.js +++ b/test/functional/apps/visualize/_histogram_request_start.js @@ -22,7 +22,13 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const log = getService('log'); const retry = getService('retry'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'timePicker', + ]); describe('histogram agg onSearchRequestStart', function() { before(async function() { @@ -33,21 +39,21 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split Rows'); - await PageObjects.visualize.clickBucket('Split rows'); + await PageObjects.visEditor.clickBucket('Split rows'); log.debug('Aggregation = Histogram'); - await PageObjects.visualize.selectAggregation('Histogram'); + await PageObjects.visEditor.selectAggregation('Histogram'); log.debug('Field = machine.ram'); - await PageObjects.visualize.selectField('machine.ram'); + await PageObjects.visEditor.selectField('machine.ram'); }); describe('interval parameter uses autoBounds', function() { it('should use provided value when number of generated buckets is less than histogram:maxBars', async function() { const providedInterval = 2400000000; log.debug(`Interval = ${providedInterval}`); - await PageObjects.visualize.setNumericInterval(providedInterval); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setInterval(providedInterval, { type: 'numeric' }); + await PageObjects.visEditor.clickGo(); await retry.try(async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); const dataArray = data.replace(/,/g, '').split('\n'); expect(dataArray.length).to.eql(20); const bucketStart = parseInt(dataArray[0], 10); @@ -60,11 +66,11 @@ export default function({ getService, getPageObjects }) { it('should scale value to round number when number of generated buckets is greater than histogram:maxBars', async function() { const providedInterval = 100; log.debug(`Interval = ${providedInterval}`); - await PageObjects.visualize.setNumericInterval(providedInterval); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setInterval(providedInterval, { type: 'numeric' }); + await PageObjects.visEditor.clickGo(); await PageObjects.common.sleep(1000); //fix this await retry.try(async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); const dataArray = data.replace(/,/g, '').split('\n'); expect(dataArray.length).to.eql(20); const bucketStart = parseInt(dataArray[0], 10); diff --git a/test/functional/apps/visualize/_inspector.js b/test/functional/apps/visualize/_inspector.js index 76b75f62b5f2ab..84f955d9c78792 100644 --- a/test/functional/apps/visualize/_inspector.js +++ b/test/functional/apps/visualize/_inspector.js @@ -21,7 +21,7 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const inspector = getService('inspector'); const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); describe('inspector', function describeIndexTests() { this.tags('smoke'); @@ -39,10 +39,10 @@ export default function({ getService, getPageObjects }) { await inspector.expectTableHeaders(['Count']); log.debug('Add Average Metric on machine.ram field'); - await PageObjects.visualize.clickBucket('Y-axis', 'metrics'); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); - await PageObjects.visualize.selectField('machine.ram', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectField('machine.ram', 'metrics'); + await PageObjects.visEditor.clickGo(); await inspector.open(); await inspector.expectTableHeaders(['Count', 'Average machine.ram']); }); @@ -50,23 +50,23 @@ export default function({ getService, getPageObjects }) { describe('filtering on inspector table values', function() { before(async function() { log.debug('Add X-axis terms agg on machine.os.raw'); - await PageObjects.visualize.clickBucket('X-axis'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.setSize(2); - await PageObjects.visualize.toggleOtherBucket(3); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('X-axis'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.setSize(2); + await PageObjects.visEditor.toggleOtherBucket(3); + await PageObjects.visEditor.clickGo(); }); beforeEach(async function() { await inspector.open(); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); }); afterEach(async function() { await inspector.close(); await filterBar.removeFilter('machine.os.raw'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); }); it('should allow filtering for values', async function() { diff --git a/test/functional/apps/visualize/_line_chart.js b/test/functional/apps/visualize/_line_chart.js index 6a0a21e76924de..becf66f0fd5b1b 100644 --- a/test/functional/apps/visualize/_line_chart.js +++ b/test/functional/apps/visualize/_line_chart.js @@ -24,7 +24,13 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'timePicker', + ]); describe('line charts', function() { const vizName1 = 'Visualization LineChart'; @@ -37,14 +43,14 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Split chart'); - await PageObjects.visualize.clickBucket('Split chart'); + await PageObjects.visEditor.clickBucket('Split chart'); log.debug('Aggregation = Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Field = extension'); - await PageObjects.visualize.selectField('extension.raw'); + await PageObjects.visEditor.selectField('extension.raw'); log.debug('switch from Rows to Columns'); - await PageObjects.visualize.clickSplitDirection('Columns'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickSplitDirection('Columns'); + await PageObjects.visEditor.clickGo(); }; before(initLineChart); @@ -60,7 +66,7 @@ export default function({ getService, getPageObjects }) { // sleep a bit before trying to get the chart data await PageObjects.common.sleep(3000); - const data = await PageObjects.visualize.getLineChartData(); + const data = await PageObjects.visChart.getLineChartData(); log.debug('data=' + data); const tolerance = 10; // the y-axis scale is 10000 so 10 is 0.1% for (let x = 0; x < data.length; x++) { @@ -91,10 +97,10 @@ export default function({ getService, getPageObjects }) { const expectedChartData = ['png 1,373', 'php 445', 'jpg 9,109', 'gif 918', 'css 2,159']; log.debug('Order By = Term'); - await PageObjects.visualize.selectOrderByMetric(2, '_key'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectOrderByMetric(2, '_key'); + await PageObjects.visEditor.clickGo(); await retry.try(async function() { - const data = await PageObjects.visualize.getLineChartData(); + const data = await PageObjects.visChart.getLineChartData(); log.debug('data=' + data); const tolerance = 10; // the y-axis scale is 10000 so 10 is 0.1% for (let x = 0; x < data.length; x++) { @@ -172,7 +178,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); describe.skip('switch between Y axis scale types', () => { @@ -180,13 +186,13 @@ export default function({ getService, getPageObjects }) { const axisId = 'ValueAxis-1'; it('should show ticks on selecting log scale', async () => { - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions(axisId); - await PageObjects.visualize.selectYAxisScaleType(axisId, 'log'); - await PageObjects.visualize.clickYAxisAdvancedOptions(axisId); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions(axisId); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'log'); + await PageObjects.visEditor.clickYAxisAdvancedOptions(axisId); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -212,9 +218,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting log scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -240,36 +246,36 @@ export default function({ getService, getPageObjects }) { }); it('should show ticks on selecting square root scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'square root'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'square root'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; expect(labels).to.eql(expectedLabels); }); it('should show filtered ticks on selecting square root scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; expect(labels).to.eql(expectedLabels); }); it('should show ticks on selecting linear scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'linear'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'linear'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; expect(labels).to.eql(expectedLabels); }); it('should show filtered ticks on selecting linear scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; expect(labels).to.eql(expectedLabels); }); diff --git a/test/functional/apps/visualize/_linked_saved_searches.js b/test/functional/apps/visualize/_linked_saved_searches.js index 9bbe8c9d2147cc..37ec3f06f2ecd0 100644 --- a/test/functional/apps/visualize/_linked_saved_searches.js +++ b/test/functional/apps/visualize/_linked_saved_searches.js @@ -22,7 +22,14 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const retry = getService('retry'); - const PageObjects = getPageObjects(['common', 'discover', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'discover', + 'visualize', + 'header', + 'timePicker', + 'visChart', + ]); describe('visualize app', function describeIndexTests() { describe('linked saved searched', () => { @@ -43,7 +50,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickSavedSearch(savedSearchName); await PageObjects.timePicker.setDefaultAbsoluteRange(); await retry.waitFor('wait for count to equal 9,109', async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); return data.trim() === '9,109'; }); }); @@ -54,7 +61,7 @@ export default function({ getService, getPageObjects }) { 'Sep 21, 2015 @ 10:00:00.000' ); await retry.waitFor('wait for count to equal 3,950', async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); return data.trim() === '3,950'; }); }); @@ -63,7 +70,7 @@ export default function({ getService, getPageObjects }) { await filterBar.addFilter('bytes', 'is between', '100', '3000'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitFor('wait for count to equal 707', async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); return data.trim() === '707'; }); }); @@ -71,7 +78,7 @@ export default function({ getService, getPageObjects }) { it('should allow unlinking from a linked search', async () => { await PageObjects.visualize.clickUnlinkSavedSearch(); await retry.waitFor('wait for count to equal 707', async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); return data.trim() === '707'; }); // The filter on the saved search should now be in the editor @@ -82,7 +89,7 @@ export default function({ getService, getPageObjects }) { await filterBar.toggleFilterEnabled('extension.raw'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitFor('wait for count to equal 1,293', async () => { - const unfilteredData = await PageObjects.visualize.getTableVisData(); + const unfilteredData = await PageObjects.visChart.getTableVisData(); return unfilteredData.trim() === '1,293'; }); }); @@ -91,7 +98,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccess('Unlinked before saved'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.waitFor('wait for count to equal 1,293', async () => { - const data = await PageObjects.visualize.getTableVisData(); + const data = await PageObjects.visChart.getTableVisData(); return data.trim() === '1,293'; }); }); diff --git a/test/functional/apps/visualize/_markdown_vis.js b/test/functional/apps/visualize/_markdown_vis.js index 870c465f2de37a..51c03c90f507b8 100644 --- a/test/functional/apps/visualize/_markdown_vis.js +++ b/test/functional/apps/visualize/_markdown_vis.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; export default function({ getPageObjects, getService }) { - const PageObjects = getPageObjects(['common', 'visualize', 'header']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'header']); const find = getService('find'); const inspector = getService('inspector'); const markdown = ` @@ -33,8 +33,8 @@ export default function({ getPageObjects, getService }) { before(async function() { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickMarkdownWidget(); - await PageObjects.visualize.setMarkdownTxt(markdown); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setMarkdownTxt(markdown); + await PageObjects.visEditor.clickGo(); }); describe('markdown vis', () => { @@ -43,29 +43,29 @@ export default function({ getPageObjects, getService }) { }); it('should render markdown as html', async function() { - const h1Txt = await PageObjects.visualize.getMarkdownBodyDescendentText('h1'); + const h1Txt = await PageObjects.visChart.getMarkdownBodyDescendentText('h1'); expect(h1Txt).to.equal('Heading 1'); }); it('should not render html in markdown as html', async function() { const expected = 'Heading 1\n

Inline HTML that should not be rendered as html

'; - const actual = await PageObjects.visualize.getMarkdownText(); + const actual = await PageObjects.visChart.getMarkdownText(); expect(actual).to.equal(expected); }); it('should auto apply changes if auto mode is turned on', async function() { const markdown2 = '## Heading 2'; - await PageObjects.visualize.toggleAutoMode(); - await PageObjects.visualize.setMarkdownTxt(markdown2); + await PageObjects.visEditor.toggleAutoMode(); + await PageObjects.visEditor.setMarkdownTxt(markdown2); await PageObjects.header.waitUntilLoadingHasFinished(); - const h1Txt = await PageObjects.visualize.getMarkdownBodyDescendentText('h2'); + const h1Txt = await PageObjects.visChart.getMarkdownBodyDescendentText('h2'); expect(h1Txt).to.equal('Heading 2'); }); it('should resize the editor', async function() { const editorSidebar = await find.byCssSelector('.visEditor__sidebar'); const initialSize = await editorSidebar.getSize(); - await PageObjects.visualize.sizeUpEditor(); + await PageObjects.visEditor.sizeUpEditor(); const afterSize = await editorSidebar.getSize(); expect(afterSize.width).to.be.greaterThan(initialSize.width); }); diff --git a/test/functional/apps/visualize/_metric_chart.js b/test/functional/apps/visualize/_metric_chart.js index 31140a1718cfe1..6a95f7553943c6 100644 --- a/test/functional/apps/visualize/_metric_chart.js +++ b/test/functional/apps/visualize/_metric_chart.js @@ -24,7 +24,7 @@ export default function({ getService, getPageObjects }) { const retry = getService('retry'); const filterBar = getService('filterBar'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); describe('metric chart', function() { before(async function() { @@ -45,21 +45,21 @@ export default function({ getService, getPageObjects }) { // initial metric of "Count" is selected by default await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(expectedCount).to.eql(metricValue); }); }); it('should show Average', async function() { const avgMachineRam = ['13,104,036,080.615', 'Average machine.ram']; - await PageObjects.visualize.clickMetricEditor(); + await PageObjects.visEditor.clickMetricEditor(); log.debug('Aggregation = Average'); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); log.debug('Field = machine.ram'); - await PageObjects.visualize.selectField('machine.ram', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('machine.ram', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(avgMachineRam).to.eql(metricValue); }); }); @@ -67,12 +67,12 @@ export default function({ getService, getPageObjects }) { it('should show Sum', async function() { const sumPhpMemory = ['85,865,880', 'Sum of phpmemory']; log.debug('Aggregation = Sum'); - await PageObjects.visualize.selectAggregation('Sum', 'metrics'); + await PageObjects.visEditor.selectAggregation('Sum', 'metrics'); log.debug('Field = phpmemory'); - await PageObjects.visualize.selectField('phpmemory', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('phpmemory', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(sumPhpMemory).to.eql(metricValue); }); }); @@ -81,12 +81,12 @@ export default function({ getService, getPageObjects }) { const medianBytes = ['5,565.263', '50th percentile of bytes']; // For now, only comparing the text label part of the metric log.debug('Aggregation = Median'); - await PageObjects.visualize.selectAggregation('Median', 'metrics'); + await PageObjects.visEditor.selectAggregation('Median', 'metrics'); log.debug('Field = bytes'); - await PageObjects.visualize.selectField('bytes', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('bytes', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); // only comparing the text label! expect(medianBytes[1]).to.eql(metricValue[1]); }); @@ -95,12 +95,12 @@ export default function({ getService, getPageObjects }) { it('should show Min', async function() { const minTimestamp = ['Sep 20, 2015 @ 00:00:00.000', 'Min @timestamp']; log.debug('Aggregation = Min'); - await PageObjects.visualize.selectAggregation('Min', 'metrics'); + await PageObjects.visEditor.selectAggregation('Min', 'metrics'); log.debug('Field = @timestamp'); - await PageObjects.visualize.selectField('@timestamp', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('@timestamp', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(minTimestamp).to.eql(metricValue); }); }); @@ -111,12 +111,12 @@ export default function({ getService, getPageObjects }) { 'Max relatedContent.article:modified_time', ]; log.debug('Aggregation = Max'); - await PageObjects.visualize.selectAggregation('Max', 'metrics'); + await PageObjects.visEditor.selectAggregation('Max', 'metrics'); log.debug('Field = relatedContent.article:modified_time'); - await PageObjects.visualize.selectField('relatedContent.article:modified_time', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('relatedContent.article:modified_time', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(maxRelatedContentArticleModifiedTime).to.eql(metricValue); }); }); @@ -124,12 +124,12 @@ export default function({ getService, getPageObjects }) { it('should show Unique Count', async function() { const uniqueCountClientip = ['1,000', 'Unique count of clientip']; log.debug('Aggregation = Unique Count'); - await PageObjects.visualize.selectAggregation('Unique Count', 'metrics'); + await PageObjects.visEditor.selectAggregation('Unique Count', 'metrics'); log.debug('Field = clientip'); - await PageObjects.visualize.selectField('clientip', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('clientip', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(uniqueCountClientip).to.eql(metricValue); }); }); @@ -153,12 +153,12 @@ export default function({ getService, getPageObjects }) { ]; log.debug('Aggregation = Percentiles'); - await PageObjects.visualize.selectAggregation('Percentiles', 'metrics'); + await PageObjects.visEditor.selectAggregation('Percentiles', 'metrics'); log.debug('Field = machine.ram'); - await PageObjects.visualize.selectField('machine.ram', 'metrics'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('machine.ram', 'metrics'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(percentileMachineRam).to.eql(metricValue); }); }); @@ -166,14 +166,14 @@ export default function({ getService, getPageObjects }) { it('should show Percentile Ranks', async function() { const percentileRankBytes = ['2.036%', 'Percentile rank 99 of "memory"']; log.debug('Aggregation = Percentile Ranks'); - await PageObjects.visualize.selectAggregation('Percentile Ranks', 'metrics'); + await PageObjects.visEditor.selectAggregation('Percentile Ranks', 'metrics'); log.debug('Field = bytes'); - await PageObjects.visualize.selectField('memory', 'metrics'); + await PageObjects.visEditor.selectField('memory', 'metrics'); log.debug('Values = 99'); - await PageObjects.visualize.setValue('99'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setValue('99'); + await PageObjects.visEditor.clickGo(); await retry.try(async function tryingForTime() { - const metricValue = await PageObjects.visualize.getMetric(); + const metricValue = await PageObjects.visChart.getMetric(); expect(percentileRankBytes).to.eql(metricValue); }); }); @@ -183,7 +183,7 @@ export default function({ getService, getPageObjects }) { let filterCount = 0; await retry.try(async function tryingForTime() { // click first metric bucket - await PageObjects.visualize.clickMetricByIndex(0); + await PageObjects.visEditor.clickMetricByIndex(0); filterCount = await filterBar.getFilterCount(); }); expect(filterCount).to.equal(0); @@ -191,17 +191,17 @@ export default function({ getService, getPageObjects }) { it('should allow filtering with buckets', async function() { log.debug('Bucket = Split Group'); - await PageObjects.visualize.clickBucket('Split group'); + await PageObjects.visEditor.clickBucket('Split group'); log.debug('Aggregation = Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Field = machine.os.raw'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.clickGo(); let filterCount = 0; await retry.try(async function tryingForTime() { // click first metric bucket - await PageObjects.visualize.clickMetricByIndex(0); + await PageObjects.visEditor.clickMetricByIndex(0); filterCount = await filterBar.getFilterCount(); }); await filterBar.removeAllFilters(); diff --git a/test/functional/apps/visualize/_pie_chart.js b/test/functional/apps/visualize/_pie_chart.js index 03067bb2182c5c..313a4e39e50302 100644 --- a/test/functional/apps/visualize/_pie_chart.js +++ b/test/functional/apps/visualize/_pie_chart.js @@ -24,7 +24,14 @@ export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const pieChart = getService('pieChart'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'header', + 'timePicker', + ]); describe('pie chart', function() { const vizName1 = 'Visualization PieChart'; @@ -36,24 +43,24 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Histogram'); - await PageObjects.visualize.selectAggregation('Histogram'); + await PageObjects.visEditor.selectAggregation('Histogram'); log.debug('Click field memory'); - await PageObjects.visualize.selectField('memory'); + await PageObjects.visEditor.selectField('memory'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.sleep(1003); log.debug('setNumericInterval 4000'); - await PageObjects.visualize.setNumericInterval('40000'); + await PageObjects.visEditor.setInterval('40000', { type: 'numeric' }); log.debug('clickGo'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }); it('should save and load', async function() { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -93,15 +100,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Click field machine.os.raw'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.toggleOtherBucket(2); - await PageObjects.visualize.toggleMissingBucket(2); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.toggleOtherBucket(2); + await PageObjects.visEditor.toggleMissingBucket(2); log.debug('clickGo'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await pieChart.expectPieChartLabels(expectedTableData); }); @@ -109,20 +116,20 @@ export default function({ getService, getPageObjects }) { const expectedTableData = ['Missing', 'osx']; await pieChart.filterOnPieSlice('Other'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); await pieChart.expectPieChartLabels(expectedTableData); await filterBar.removeFilter('machine.os.raw'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should apply correct filter on other bucket by clicking on a legend', async () => { const expectedTableData = ['Missing', 'osx']; - await PageObjects.visualize.filterLegend('Other'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.filterLegend('Other'); + await PageObjects.visChart.waitForVisualization(); await pieChart.expectPieChartLabels(expectedTableData); await filterBar.removeFilter('machine.os.raw'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should show two levels of other buckets', async () => { @@ -171,15 +178,15 @@ export default function({ getService, getPageObjects }) { 'Other', ]; - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split slices'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split slices'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Click field geo.src'); - await PageObjects.visualize.selectField('geo.src'); - await PageObjects.visualize.toggleOtherBucket(3); - await PageObjects.visualize.toggleMissingBucket(3); + await PageObjects.visEditor.selectField('geo.src'); + await PageObjects.visEditor.toggleOtherBucket(3); + await PageObjects.visEditor.toggleMissingBucket(3); log.debug('clickGo'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await pieChart.expectPieChartLabels(expectedTableData); }); }); @@ -187,17 +194,17 @@ export default function({ getService, getPageObjects }) { describe('disabled aggs', () => { before(async () => { await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForRenderingCount(); + await PageObjects.visChart.waitForRenderingCount(); }); it('should show correct result with one agg disabled', async () => { const expectedTableData = ['win 8', 'win xp', 'win 7', 'ios', 'osx']; - await PageObjects.visualize.clickBucket('Split slices'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.toggleDisabledAgg(2); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split slices'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.toggleDisabledAgg(2); + await PageObjects.visEditor.clickGo(); await pieChart.expectPieChartLabels(expectedTableData); }); @@ -206,15 +213,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForRenderingCount(); + await PageObjects.visChart.waitForRenderingCount(); const expectedTableData = ['win 8', 'win xp', 'win 7', 'ios', 'osx']; await pieChart.expectPieChartLabels(expectedTableData); }); it('should show correct result when agg is re-enabled', async () => { - await PageObjects.visualize.toggleDisabledAgg(2); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleDisabledAgg(2); + await PageObjects.visEditor.clickGo(); const expectedTableData = [ '0', @@ -291,24 +298,24 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Filters'); - await PageObjects.visualize.selectAggregation('Filters'); + await PageObjects.visEditor.selectAggregation('Filters'); log.debug('Set the 1st filter value'); - await PageObjects.visualize.setFilterAggregationValue('geo.dest:"US"'); + await PageObjects.visEditor.setFilterAggregationValue('geo.dest:"US"'); log.debug('Add new filter'); - await PageObjects.visualize.addNewFilterAggregation(); + await PageObjects.visEditor.addNewFilterAggregation(); log.debug('Set the 2nd filter value'); - await PageObjects.visualize.setFilterAggregationValue('geo.dest:"CN"', 1); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setFilterAggregationValue('geo.dest:"CN"', 1); + await PageObjects.visEditor.clickGo(); const emptyFromTime = 'Sep 19, 2016 @ 06:31:44.000'; const emptyToTime = 'Sep 23, 2016 @ 18:31:44.000'; log.debug( 'Switch to a different time range from "' + emptyFromTime + '" to "' + emptyToTime + '"' ); await PageObjects.timePicker.setAbsoluteRange(emptyFromTime, emptyToTime); - await PageObjects.visualize.waitForVisualization(); - await PageObjects.visualize.expectError(); + await PageObjects.visChart.waitForVisualization(); + await PageObjects.visChart.expectError(); }); }); describe('multi series slice', () => { @@ -327,22 +334,22 @@ export default function({ getService, getPageObjects }) { ); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Histogram'); - await PageObjects.visualize.selectAggregation('Histogram'); + await PageObjects.visEditor.selectAggregation('Histogram'); log.debug('Click field memory'); - await PageObjects.visualize.selectField('memory'); + await PageObjects.visEditor.selectField('memory'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.sleep(1003); log.debug('setNumericInterval 4000'); - await PageObjects.visualize.setNumericInterval('40000'); + await PageObjects.visEditor.setInterval('40000', { type: 'numeric' }); log.debug('Toggle previous editor'); - await PageObjects.visualize.toggleAggregationEditor(2); + await PageObjects.visEditor.toggleAggregationEditor(2); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('geo.dest'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split slices'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('geo.dest'); + await PageObjects.visEditor.clickGo(); }); it('should show correct chart', async () => { @@ -428,11 +435,11 @@ export default function({ getService, getPageObjects }) { '360,000', 'CN', ]; - await PageObjects.visualize.filterLegend('CN'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.filterLegend('CN'); + await PageObjects.visChart.waitForVisualization(); await pieChart.expectPieChartLabels(expectedTableData); await filterBar.removeFilter('geo.dest'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should still showing pie chart when a subseries have zero data', async function() { @@ -442,21 +449,21 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Filters'); - await PageObjects.visualize.selectAggregation('Filters'); + await PageObjects.visEditor.selectAggregation('Filters'); log.debug('Set the 1st filter value'); - await PageObjects.visualize.setFilterAggregationValue('geo.dest:"US"'); + await PageObjects.visEditor.setFilterAggregationValue('geo.dest:"US"'); log.debug('Toggle previous editor'); - await PageObjects.visualize.toggleAggregationEditor(2); + await PageObjects.visEditor.toggleAggregationEditor(2); log.debug('Add a new series, select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); + await PageObjects.visEditor.clickBucket('Split slices'); log.debug('Click aggregation Filters'); - await PageObjects.visualize.selectAggregation('Filters'); + await PageObjects.visEditor.selectAggregation('Filters'); log.debug('Set the 1st filter value of the aggregation id 3'); - await PageObjects.visualize.setFilterAggregationValue('geo.dest:"UX"', 0, 3); - await PageObjects.visualize.clickGo(); - const legends = await PageObjects.visualize.getLegendEntries(); + await PageObjects.visEditor.setFilterAggregationValue('geo.dest:"UX"', 0, 3); + await PageObjects.visEditor.clickGo(); + const legends = await PageObjects.visChart.getLegendEntries(); const expectedLegends = ['geo.dest:"US"', 'geo.dest:"UX"']; expect(legends).to.eql(expectedLegends); }); @@ -469,15 +476,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Split chart'); - await PageObjects.visualize.clickBucket('Split chart'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os.raw'); - await PageObjects.visualize.toggleAggregationEditor(2); + await PageObjects.visEditor.clickBucket('Split chart'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os.raw'); + await PageObjects.visEditor.toggleAggregationEditor(2); log.debug('Add a new series, select bucket Split slices'); - await PageObjects.visualize.clickBucket('Split slices'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('geo.src'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickBucket('Split slices'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('geo.src'); + await PageObjects.visEditor.clickGo(); }); it('shows correct split chart', async () => { @@ -522,7 +529,7 @@ export default function({ getService, getPageObjects }) { ['ios', '478', 'CN', '478'], ['osx', '228', 'CN', '228'], ]; - await PageObjects.visualize.filterLegend('CN'); + await PageObjects.visChart.filterLegend('CN'); await PageObjects.header.waitUntilLoadingHasFinished(); await inspector.open(); await inspector.setTablePageSize(50); diff --git a/test/functional/apps/visualize/_point_series_options.js b/test/functional/apps/visualize/_point_series_options.js index 2d496cb575db79..e7ce5808554b47 100644 --- a/test/functional/apps/visualize/_point_series_options.js +++ b/test/functional/apps/visualize/_point_series_options.js @@ -25,11 +25,12 @@ export default function({ getService, getPageObjects }) { const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); const PageObjects = getPageObjects([ - 'common', 'visualize', 'header', 'pointSeries', 'timePicker', + 'visEditor', + 'visChart', ]); const pointSeriesVis = PageObjects.pointSeries; const inspector = getService('inspector'); @@ -42,18 +43,18 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); log.debug('Field = @timestamp'); - await PageObjects.visualize.selectField('@timestamp'); + await PageObjects.visEditor.selectField('@timestamp'); // add another metrics log.debug('Metric = Value Axis'); - await PageObjects.visualize.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); log.debug('Aggregation = Average'); - await PageObjects.visualize.selectAggregation('Average', 'metrics'); + await PageObjects.visEditor.selectAggregation('Average', 'metrics'); log.debug('Field = memory'); - await PageObjects.visualize.selectField('machine.ram', 'metrics'); + await PageObjects.visEditor.selectField('machine.ram', 'metrics'); // go to options page log.debug('Going to axis options'); await pointSeriesVis.clickAxisOptions(); @@ -61,11 +62,11 @@ export default function({ getService, getPageObjects }) { log.debug('adding axis'); await pointSeriesVis.clickAddAxis(); // set average count to use second value axis - await PageObjects.visualize.toggleAccordion('visEditorSeriesAccordion3'); + await PageObjects.visEditor.toggleAccordion('visEditorSeriesAccordion3'); log.debug('Average memory value axis - ValueAxis-2'); await pointSeriesVis.setSeriesAxis(1, 'ValueAxis-2'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); } describe('point series', function describeIndexTests() { @@ -129,14 +130,14 @@ export default function({ getService, getPageObjects }) { ]; await retry.try(async () => { - const data = await PageObjects.visualize.getLineChartData('Count'); + const data = await PageObjects.visChart.getLineChartData('Count'); log.debug('count data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues[0]); }); await retry.try(async () => { - const avgMemoryData = await PageObjects.visualize.getLineChartData( + const avgMemoryData = await PageObjects.visChart.getLineChartData( 'Average machine.ram', 'ValueAxis-2' ); @@ -158,7 +159,7 @@ export default function({ getService, getPageObjects }) { describe('multiple chart types', function() { it('should change average series type to histogram', async function() { await pointSeriesVis.setSeriesType(1, 'histogram'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const length = await pointSeriesVis.getHistogramSeries(); expect(length).to.be(1); }); @@ -166,12 +167,12 @@ export default function({ getService, getPageObjects }) { describe('grid lines', function() { before(async function() { - await pointSeriesVis.clickOptions(); + await PageObjects.visEditor.clickOptionsTab(); }); it('should show category grid lines', async function() { await pointSeriesVis.toggleGridCategoryLines(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const gridLines = await pointSeriesVis.getGridLines(); expect(gridLines.length).to.be(9); gridLines.forEach(gridLine => { @@ -182,7 +183,7 @@ export default function({ getService, getPageObjects }) { it('should show value axis grid lines', async function() { await pointSeriesVis.setGridValueAxis('ValueAxis-2'); await pointSeriesVis.toggleGridCategoryLines(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const gridLines = await pointSeriesVis.getGridLines(); expect(gridLines.length).to.be(9); gridLines.forEach(gridLine => { @@ -199,36 +200,36 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickLineChart(); await PageObjects.visualize.clickNewSearch(); - await PageObjects.visualize.selectYAxisAggregation('Average', 'bytes', customLabel, 1); - await PageObjects.visualize.clickGo(); - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions('ValueAxis-1'); + await PageObjects.visEditor.selectYAxisAggregation('Average', 'bytes', customLabel, 1); + await PageObjects.visEditor.clickGo(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions('ValueAxis-1'); }); it('should render a custom label when one is set', async function() { - const title = await PageObjects.visualize.getYAxisTitle(); + const title = await PageObjects.visChart.getYAxisTitle(); expect(title).to.be(customLabel); }); it('should render a custom axis title when one is set, overriding the custom label', async function() { await pointSeriesVis.setAxisTitle(axisTitle); - await PageObjects.visualize.clickGo(); - const title = await PageObjects.visualize.getYAxisTitle(); + await PageObjects.visEditor.clickGo(); + const title = await PageObjects.visChart.getYAxisTitle(); expect(title).to.be(axisTitle); }); it('should preserve saved axis titles after a vis is saved and reopened', async function() { await PageObjects.visualize.saveVisualizationExpectSuccess(visName); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); await PageObjects.visualize.loadSavedVisualization(visName); - await PageObjects.visualize.waitForRenderingCount(); - await PageObjects.visualize.clickData(); - await PageObjects.visualize.toggleOpenEditor(1); - await PageObjects.visualize.setCustomLabel('test', 1); - await PageObjects.visualize.clickGo(); - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions('ValueAxis-1'); - const title = await PageObjects.visualize.getYAxisTitle(); + await PageObjects.visChart.waitForRenderingCount(); + await PageObjects.visEditor.clickDataTab(); + await PageObjects.visEditor.toggleOpenEditor(1); + await PageObjects.visEditor.setCustomLabel('test', 1); + await PageObjects.visEditor.clickGo(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions('ValueAxis-1'); + const title = await PageObjects.visChart.getYAxisTitle(); expect(title).to.be(axisTitle); }); }); @@ -238,7 +239,7 @@ export default function({ getService, getPageObjects }) { it('should show round labels in default timezone', async function() { await initChart(); - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); expect(labels.join()).to.contain(expectedLabels.join()); }); @@ -248,7 +249,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.header.awaitKibanaChrome(); await initChart(); - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); expect(labels.join()).to.contain(expectedLabels.join()); }); @@ -258,10 +259,10 @@ export default function({ getService, getPageObjects }) { const toTime = 'Sep 22, 2015 @ 16:08:34.554'; // note that we're setting the absolute time range while we're in 'America/Phoenix' tz await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visualize.waitForRenderingCount(); + await PageObjects.visChart.waitForRenderingCount(); await retry.waitFor('wait for x-axis labels to match expected for Phoenix', async () => { - const labels = await PageObjects.visualize.getXAxisLabels(); + const labels = await PageObjects.visChart.getXAxisLabels(); log.debug(`Labels: ${labels}`); return ( labels.toString() === ['10:00', '11:00', '12:00', '13:00', '14:00', '15:00'].toString() @@ -303,11 +304,11 @@ export default function({ getService, getPageObjects }) { await browser.refresh(); // wait some time before trying to check for rendering count await PageObjects.header.awaitKibanaChrome(); - await PageObjects.visualize.waitForRenderingCount(); + await PageObjects.visChart.waitForRenderingCount(); log.debug('getXAxisLabels'); await retry.waitFor('wait for x-axis labels to match expected for UTC', async () => { - const labels2 = await PageObjects.visualize.getXAxisLabels(); + const labels2 = await PageObjects.visChart.getXAxisLabels(); log.debug(`Labels: ${labels2}`); return ( labels2.toString() === ['17:00', '18:00', '19:00', '20:00', '21:00', '22:00'].toString() diff --git a/test/functional/apps/visualize/_region_map.js b/test/functional/apps/visualize/_region_map.js index f6c81dab5921f5..10cbd9913c70c2 100644 --- a/test/functional/apps/visualize/_region_map.js +++ b/test/functional/apps/visualize/_region_map.js @@ -24,7 +24,7 @@ export default function({ getService, getPageObjects }) { const inspector = getService('inspector'); const log = getService('log'); const find = getService('find'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'settings']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'timePicker']); before(async function() { log.debug('navigateToApp visualize'); @@ -34,12 +34,12 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = Shape field'); - await PageObjects.visualize.clickBucket('Shape field'); + await PageObjects.visEditor.clickBucket('Shape field'); log.debug('Aggregation = Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Field = geo.src'); - await PageObjects.visualize.selectField('geo.src'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectField('geo.src'); + await PageObjects.visEditor.clickGo(); }); describe('vector map', function indexPatternCreation() { @@ -60,26 +60,26 @@ export default function({ getService, getPageObjects }) { }); it('should change results after changing layer to world', async function() { - await PageObjects.visualize.clickOptions(); - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.clickOptionsTab(); + await PageObjects.visEditor.setSelectByOptionText( 'regionMapOptionsSelectLayer', 'World Countries' ); //ensure all fields are there - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.setSelectByOptionText( 'regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-2 code' ); - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.setSelectByOptionText( 'regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-3 code' ); - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.setSelectByOptionText( 'regionMapOptionsSelectJoinField', 'name' ); - await PageObjects.visualize.setSelectByOptionText( + await PageObjects.visEditor.setSelectByOptionText( 'regionMapOptionsSelectJoinField', 'ISO 3166-1 alpha-2 code' ); diff --git a/test/functional/apps/visualize/_tag_cloud.js b/test/functional/apps/visualize/_tag_cloud.js index 97b8036b305031..4f921cec1fdf10 100644 --- a/test/functional/apps/visualize/_tag_cloud.js +++ b/test/functional/apps/visualize/_tag_cloud.js @@ -26,7 +26,16 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const retry = getService('retry'); const find = getService('find'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'settings', 'timePicker']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'header', + 'settings', + 'timePicker', + 'tagCloud', + ]); describe('tag cloud chart', function() { const vizName1 = 'Visualization tagCloud'; @@ -40,15 +49,15 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select Tags'); - await PageObjects.visualize.clickBucket('Tags'); + await PageObjects.visEditor.clickBucket('Tags'); log.debug('Click aggregation Terms'); - await PageObjects.visualize.selectAggregation('Terms'); + await PageObjects.visEditor.selectAggregation('Terms'); log.debug('Click field machine.ram'); await retry.try(async function tryingForTime() { - await PageObjects.visualize.selectField(termsField); + await PageObjects.visEditor.selectField(termsField); }); - await PageObjects.visualize.selectOrderByMetric(2, '_key'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.selectOrderByMetric(2, '_key'); + await PageObjects.visEditor.clickGo(); }); it('should have inspector enabled', async function() { @@ -56,7 +65,7 @@ export default function({ getService, getPageObjects }) { }); it('should show correct tag cloud data', async function() { - const data = await PageObjects.visualize.getTextTag(); + const data = await PageObjects.tagCloud.getTextTag(); log.debug(data); expect(data).to.eql([ '32,212,254,720', @@ -69,22 +78,22 @@ export default function({ getService, getPageObjects }) { it('should collapse the sidebar', async function() { const editorSidebar = await find.byCssSelector('.collapsible-sidebar'); - await PageObjects.visualize.clickEditorSidebarCollapse(); + await PageObjects.visEditor.clickEditorSidebarCollapse(); // Give d3 tag cloud some time to rearrange tags await PageObjects.common.sleep(1000); const afterSize = await editorSidebar.getSize(); expect(afterSize.width).to.be(0); - await PageObjects.visualize.clickEditorSidebarCollapse(); + await PageObjects.visEditor.clickEditorSidebarCollapse(); }); it('should still show all tags after sidebar has been collapsed', async function() { - await PageObjects.visualize.clickEditorSidebarCollapse(); + await PageObjects.visEditor.clickEditorSidebarCollapse(); // Give d3 tag cloud some time to rearrange tags await PageObjects.common.sleep(1000); - await PageObjects.visualize.clickEditorSidebarCollapse(); + await PageObjects.visEditor.clickEditorSidebarCollapse(); // Give d3 tag cloud some time to rearrange tags await PageObjects.common.sleep(1000); - const data = await PageObjects.visualize.getTextTag(); + const data = await PageObjects.tagCloud.getTextTag(); log.debug(data); expect(data).to.eql([ '32,212,254,720', @@ -100,7 +109,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.common.sleep(1000); await browser.setWindowSize(1200, 800); await PageObjects.common.sleep(1000); - const data = await PageObjects.visualize.getTextTag(); + const data = await PageObjects.tagCloud.getTextTag(); expect(data).to.eql([ '32,212,254,720', '21,474,836,480', @@ -114,11 +123,11 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should show the tags and relative size', function() { - return PageObjects.visualize.getTextSizes().then(function(results) { + return PageObjects.tagCloud.getTextSizes().then(function(results) { log.debug('results here ' + results); expect(results).to.eql(['72px', '63px', '25px', '32px', '18px']); }); @@ -153,7 +162,7 @@ export default function({ getService, getPageObjects }) { }); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.timePicker.setDefaultAbsoluteRange(); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); after(async function() { @@ -168,15 +177,15 @@ export default function({ getService, getPageObjects }) { }); it('should format tags with field formatter', async function() { - const data = await PageObjects.visualize.getTextTag(); + const data = await PageObjects.tagCloud.getTextTag(); log.debug(data); expect(data).to.eql(['30GB', '20GB', '19GB', '18GB', '17GB']); }); it('should apply filter with unformatted value', async function() { - await PageObjects.visualize.selectTagCloudTag('30GB'); + await PageObjects.tagCloud.selectTagCloudTag('30GB'); await PageObjects.header.waitUntilLoadingHasFinished(); - const data = await PageObjects.visualize.getTextTag(); + const data = await PageObjects.tagCloud.getTextTag(); expect(data).to.eql(['30GB']); }); }); diff --git a/test/functional/apps/visualize/_tile_map.js b/test/functional/apps/visualize/_tile_map.js index e2946339b1f087..397eaeb0f30137 100644 --- a/test/functional/apps/visualize/_tile_map.js +++ b/test/functional/apps/visualize/_tile_map.js @@ -27,7 +27,14 @@ export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const testSubjects = getService('testSubjects'); const browser = getService('browser'); - const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'settings']); + const PageObjects = getPageObjects([ + 'common', + 'visualize', + 'visEditor', + 'visChart', + 'timePicker', + 'tileMap', + ]); describe('tile map visualize app', function() { describe('incomplete config', function describeIndexTests() { @@ -45,8 +52,8 @@ export default function({ getService, getPageObjects }) { it('should be able to zoom in twice', async () => { //should not throw - await PageObjects.visualize.clickMapZoomIn(); - await PageObjects.visualize.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); }); }); @@ -61,14 +68,14 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('select bucket Geo Coordinates'); - await PageObjects.visualize.clickBucket('Geo coordinates'); + await PageObjects.visEditor.clickBucket('Geo coordinates'); log.debug('Click aggregation Geohash'); - await PageObjects.visualize.selectAggregation('Geohash'); + await PageObjects.visEditor.selectAggregation('Geohash'); log.debug('Click field geo.coordinates'); await retry.try(async function tryingForTime() { - await PageObjects.visualize.selectField('geo.coordinates'); + await PageObjects.visEditor.selectField('geo.coordinates'); }); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }); /** @@ -106,9 +113,9 @@ export default function({ getService, getPageObjects }) { ['-', '8', '108', { lat: 18, lon: -157 }], ]; //level 1 - await PageObjects.visualize.clickMapZoomOut(); + await PageObjects.tileMap.clickMapZoomOut(); //level 0 - await PageObjects.visualize.clickMapZoomOut(); + await PageObjects.tileMap.clickMapZoomOut(); await inspector.open(); await inspector.setTablePageSize(50); @@ -118,8 +125,8 @@ export default function({ getService, getPageObjects }) { }); it('should not be able to zoom out beyond 0', async function() { - await PageObjects.visualize.zoomAllTheWayOut(); - const enabled = await PageObjects.visualize.getMapZoomOutEnabled(); + await PageObjects.tileMap.zoomAllTheWayOut(); + const enabled = await PageObjects.tileMap.getMapZoomOutEnabled(); expect(enabled).to.be(false); }); @@ -148,7 +155,7 @@ export default function({ getService, getPageObjects }) { ['-', 'b7', '167', { lat: 64, lon: -163 }], ]; - await PageObjects.visualize.clickMapFitDataBounds(); + await PageObjects.tileMap.clickMapFitDataBounds(); await inspector.open(); const data = await inspector.getTableData(); await inspector.close(); @@ -164,8 +171,8 @@ export default function({ getService, getPageObjects }) { await filterBar.addFilter('bytes', 'is between', '19980', '19990'); await filterBar.toggleFilterPinned('bytes'); - await PageObjects.visualize.zoomAllTheWayOut(); - await PageObjects.visualize.clickMapFitDataBounds(); + await PageObjects.tileMap.zoomAllTheWayOut(); + await PageObjects.tileMap.clickMapFitDataBounds(); await inspector.open(); const data = await inspector.getTableData(); @@ -178,15 +185,15 @@ export default function({ getService, getPageObjects }) { it('Newly saved visualization retains map bounds', async () => { const vizName1 = 'Visualization TileMap'; - await PageObjects.visualize.clickMapZoomIn(); - await PageObjects.visualize.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); - const mapBounds = await PageObjects.visualize.getMapBounds(); + const mapBounds = await PageObjects.tileMap.getMapBounds(); await inspector.close(); await PageObjects.visualize.saveVisualizationExpectSuccess(vizName1); - const afterSaveMapBounds = await PageObjects.visualize.getMapBounds(); + const afterSaveMapBounds = await PageObjects.tileMap.getMapBounds(); await inspector.close(); // For some reason the values are slightly different, so we can't check that they are equal. But we did @@ -207,17 +214,17 @@ export default function({ getService, getPageObjects }) { }); it('when not checked does not add filters to aggregation', async () => { - await PageObjects.visualize.toggleOpenEditor(2); - await PageObjects.visualize.setIsFilteredByCollarCheckbox(false); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2); + await PageObjects.visEditor.setIsFilteredByCollarCheckbox(false); + await PageObjects.visEditor.clickGo(); await inspector.open(); await inspector.expectTableHeaders(['geohash_grid', 'Count', 'Geo Centroid']); await inspector.close(); }); after(async () => { - await PageObjects.visualize.setIsFilteredByCollarCheckbox(true); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setIsFilteredByCollarCheckbox(true); + await PageObjects.visEditor.clickGo(); }); }); }); @@ -245,18 +252,18 @@ export default function({ getService, getPageObjects }) { const zoomLevel = 9; for (let i = 0; i < zoomLevel; i++) { - await PageObjects.visualize.clickMapZoomIn(); + await PageObjects.tileMap.clickMapZoomIn(); } }); beforeEach(async function() { - await PageObjects.visualize.clickMapZoomIn(waitForLoading); + await PageObjects.tileMap.clickMapZoomIn(waitForLoading); }); afterEach(async function() { if (!last) { await PageObjects.common.sleep(toastDefaultLife); - await PageObjects.visualize.clickMapZoomOut(waitForLoading); + await PageObjects.tileMap.clickMapZoomOut(waitForLoading); } }); @@ -270,11 +277,11 @@ export default function({ getService, getPageObjects }) { it('should suppress zoom warning if suppress warnings button clicked', async () => { last = true; - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); await find.clickByCssSelector('[data-test-subj="suppressZoomWarnings"]'); - await PageObjects.visualize.clickMapZoomOut(waitForLoading); + await PageObjects.tileMap.clickMapZoomOut(waitForLoading); await testSubjects.waitForDeleted('suppressZoomWarnings'); - await PageObjects.visualize.clickMapZoomIn(waitForLoading); + await PageObjects.tileMap.clickMapZoomIn(waitForLoading); await testSubjects.missingOrFail('maxZoomWarning'); }); diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index dc4b9a786eaa46..8dbe3568895684 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -25,7 +25,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const log = getService('log'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['visualize', 'visualBuilder', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visualBuilder', 'timePicker', 'visChart']); describe('visual builder', function describeIndexTests() { this.tags('smoke'); @@ -80,7 +80,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should verify gauge label and count display', async () => { - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); const labelString = await PageObjects.visualBuilder.getGaugeLabel(); expect(labelString).to.be('Count'); const gaugeCount = await PageObjects.visualBuilder.getGaugeCount(); @@ -96,7 +96,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should verify topN label and count display', async () => { - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); const labelString = await PageObjects.visualBuilder.getTopNLabel(); expect(labelString).to.be('Count'); const gaugeCount = await PageObjects.visualBuilder.getTopNCount(); diff --git a/test/functional/apps/visualize/_tsvb_table.ts b/test/functional/apps/visualize/_tsvb_table.ts index a36b6facb30398..5808212559b18d 100644 --- a/test/functional/apps/visualize/_tsvb_table.ts +++ b/test/functional/apps/visualize/_tsvb_table.ts @@ -21,7 +21,11 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function({ getPageObjects }: FtrProviderContext) { - const { visualBuilder, visualize } = getPageObjects(['visualBuilder', 'visualize']); + const { visualBuilder, visualize, visChart } = getPageObjects([ + 'visualBuilder', + 'visualize', + 'visChart', + ]); describe('visual builder', function describeIndexTests() { describe('table', () => { @@ -32,7 +36,7 @@ export default function({ getPageObjects }: FtrProviderContext) { await visualBuilder.checkTableTabIsPresent(); await visualBuilder.selectGroupByField('machine.os.raw'); await visualBuilder.setColumnLabelValue('OS'); - await visualize.waitForVisualizationRenderingStabilized(); + await visChart.waitForVisualizationRenderingStabilized(); }); it('should display correct values on changing group by field and column name', async () => { diff --git a/test/functional/apps/visualize/_vega_chart.js b/test/functional/apps/visualize/_vega_chart.js index 224dec7ef2a71c..df0603c7f95f51 100644 --- a/test/functional/apps/visualize/_vega_chart.js +++ b/test/functional/apps/visualize/_vega_chart.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { - const PageObjects = getPageObjects(['common', 'header', 'timePicker', 'visualize']); + const PageObjects = getPageObjects(['timePicker', 'visualize', 'visChart', 'vegaChart']); const filterBar = getService('filterBar'); const inspector = getService('inspector'); const log = getService('log'); @@ -40,7 +40,7 @@ export default function({ getService, getPageObjects }) { }); it.skip('should have some initial vega spec text', async function() { - const vegaSpec = await PageObjects.visualize.getVegaSpec(); + const vegaSpec = await PageObjects.vegaChart.getSpec(); expect(vegaSpec) .to.contain('{') .and.to.contain('data'); @@ -48,7 +48,7 @@ export default function({ getService, getPageObjects }) { }); it('should have view and control containers', async function() { - const view = await PageObjects.visualize.getVegaViewContainer(); + const view = await PageObjects.vegaChart.getViewContainer(); expect(view).to.be.ok(); const size = await view.getSize(); expect(size) @@ -57,7 +57,7 @@ export default function({ getService, getPageObjects }) { expect(size.width).to.be.above(0); expect(size.height).to.be.above(0); - const controls = await PageObjects.visualize.getVegaControlContainer(); + const controls = await PageObjects.vegaChart.getControlContainer(); expect(controls).to.be.ok(); }); }); @@ -73,9 +73,9 @@ export default function({ getService, getPageObjects }) { }); it.skip('should render different data in response to filter change', async function() { - await PageObjects.visualize.expectVisToMatchScreenshot('vega_chart'); + await PageObjects.vegaChart.expectVisToMatchScreenshot('vega_chart'); await filterBar.addFilter('@tags.raw', 'is', 'error'); - await PageObjects.visualize.expectVisToMatchScreenshot('vega_chart_filtered'); + await PageObjects.vegaChart.expectVisToMatchScreenshot('vega_chart_filtered'); }); }); }); diff --git a/test/functional/apps/visualize/_vertical_bar_chart.js b/test/functional/apps/visualize/_vertical_bar_chart.js index 1153cc12e23ed7..2efa812c7a734c 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart.js +++ b/test/functional/apps/visualize/_vertical_bar_chart.js @@ -23,8 +23,9 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const retry = getService('retry'); const inspector = getService('inspector'); + const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); // FLAKY: https://github.com/elastic/kibana/issues/22322 describe('vertical bar chart', function() { @@ -38,13 +39,13 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.clickNewSearch(); await PageObjects.timePicker.setDefaultAbsoluteRange(); log.debug('Bucket = X-Axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); log.debug('Field = @timestamp'); - await PageObjects.visualize.selectField('@timestamp'); + await PageObjects.visEditor.selectField('@timestamp'); // leaving Interval set to Auto - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }; before(initBarChart); @@ -53,7 +54,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -92,7 +93,7 @@ export default function({ getService, getPageObjects }) { // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function // try sleeping a bit before getting that data await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData(); + const data = await PageObjects.visChart.getBarChartData(); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); @@ -203,15 +204,15 @@ export default function({ getService, getPageObjects }) { // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function // try sleeping a bit before getting that data await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData(); + const data = await PageObjects.visChart.getBarChartData(); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); }); - await PageObjects.visualize.toggleOpenEditor(2); - await PageObjects.visualize.clickDropPartialBuckets(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2); + await PageObjects.visEditor.clickDropPartialBuckets(); + await PageObjects.visEditor.clickGo(); expectedChartValues = [ 218, @@ -279,7 +280,7 @@ export default function({ getService, getPageObjects }) { // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function // try sleeping a bit before getting that data await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData(); + const data = await PageObjects.visChart.getBarChartData(); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); @@ -291,13 +292,13 @@ export default function({ getService, getPageObjects }) { const axisId = 'ValueAxis-1'; it('should show ticks on selecting log scale', async () => { - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions(axisId); - await PageObjects.visualize.selectYAxisScaleType(axisId, 'log'); - await PageObjects.visualize.clickYAxisAdvancedOptions(axisId); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions(axisId); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'log'); + await PageObjects.visEditor.clickYAxisAdvancedOptions(axisId); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -323,9 +324,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting log scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -351,10 +352,10 @@ export default function({ getService, getPageObjects }) { }); it('should show ticks on selecting square root scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'square root'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'square root'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '0', '200', @@ -370,18 +371,18 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting square root scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); it('should show ticks on selecting linear scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'linear'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'linear'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); const expectedLabels = [ '0', @@ -398,9 +399,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting linear scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); @@ -409,11 +410,11 @@ export default function({ getService, getPageObjects }) { describe('vertical bar in percent mode', async () => { it('should show ticks with percentage values', async function() { const axisId = 'ValueAxis-1'; - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions(axisId); - await PageObjects.visualize.selectYAxisMode('percentage'); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions(axisId); + await PageObjects.visEditor.selectYAxisMode('percentage'); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); expect(labels[0]).to.eql('0%'); expect(labels[labels.length - 1]).to.eql('100%'); }); @@ -423,36 +424,36 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('response.raw'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('response.raw'); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); const expectedEntries = ['200', '404', '503']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); it('should allow custom sorting of series', async () => { - await PageObjects.visualize.toggleOpenEditor(1, 'false'); - await PageObjects.visualize.selectCustomSortMetric(3, 'Min', 'bytes'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(1, 'false'); + await PageObjects.visEditor.selectCustomSortMetric(3, 'Min', 'bytes'); + await PageObjects.visEditor.clickGo(); const expectedEntries = ['404', '200', '503']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); it('should correctly filter by legend', async () => { - await PageObjects.visualize.filterLegend('200'); - await PageObjects.visualize.waitForVisualization(); - const legendEntries = await PageObjects.visualize.getLegendEntries(); + await PageObjects.visChart.filterLegend('200'); + await PageObjects.visChart.waitForVisualization(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); const expectedEntries = ['200']; expect(legendEntries).to.eql(expectedEntries); await filterBar.removeFilter('response.raw'); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); }); @@ -460,18 +461,18 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('response.raw'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - - await PageObjects.visualize.toggleOpenEditor(3, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('response.raw'); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + + await PageObjects.visEditor.toggleOpenEditor(3, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os'); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); const expectedEntries = [ '200 - win 8', @@ -490,18 +491,18 @@ export default function({ getService, getPageObjects }) { '404 - win 8', '404 - win xp', ]; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); it('should show correct series when disabling first agg', async function() { // this will avoid issues with the play tooltip covering the disable agg button - await PageObjects.visualize.scrollSubjectIntoView('metricsAggGroup'); - await PageObjects.visualize.toggleDisabledAgg(3); - await PageObjects.visualize.clickGo(); + await testSubjects.scrollIntoView('metricsAggGroup'); + await PageObjects.visEditor.toggleDisabledAgg(3); + await PageObjects.visEditor.clickGo(); const expectedEntries = ['win 8', 'win xp', 'ios', 'osx', 'win 7']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); }); @@ -510,24 +511,24 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.toggleOpenEditor(1); - await PageObjects.visualize.selectAggregation('Derivative', 'metrics'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.toggleOpenEditor(1); + await PageObjects.visEditor.selectAggregation('Derivative', 'metrics'); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.clickGo(); const expectedEntries = ['Derivative of Count']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); it('should show an error if last bucket aggregation is terms', async () => { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('response.raw'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('response.raw'); - const errorMessage = await PageObjects.visualize.getBucketErrorMessage(); + const errorMessage = await PageObjects.visEditor.getBucketErrorMessage(); expect(errorMessage).to.contain('Last bucket aggregation must be "Date Histogram"'); }); }); diff --git a/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.js b/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.js index e796f281b06890..2371df6e92476e 100644 --- a/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.js +++ b/test/functional/apps/visualize/_vertical_bar_chart_nontimeindex.js @@ -23,7 +23,7 @@ export default function({ getService, getPageObjects }) { const log = getService('log'); const retry = getService('retry'); const inspector = getService('inspector'); - const PageObjects = getPageObjects(['common', 'visualize', 'header']); + const PageObjects = getPageObjects(['common', 'visualize', 'header', 'visEditor', 'visChart']); // FLAKY: https://github.com/elastic/kibana/issues/22322 describe.skip('vertical bar chart with index without time filter', function() { @@ -39,13 +39,13 @@ export default function({ getService, getPageObjects }) { ); await PageObjects.common.sleep(500); log.debug('Bucket = X-Axis'); - await PageObjects.visualize.clickBucket('X-axis'); + await PageObjects.visEditor.clickBucket('X-axis'); log.debug('Aggregation = Date Histogram'); - await PageObjects.visualize.selectAggregation('Date Histogram'); + await PageObjects.visEditor.selectAggregation('Date Histogram'); log.debug('Field = @timestamp'); - await PageObjects.visualize.selectField('@timestamp'); - await PageObjects.visualize.setCustomInterval('3h'); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visEditor.selectField('@timestamp'); + await PageObjects.visEditor.setInterval('3h', { type: 'custom' }); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); }; before(initBarChart); @@ -54,7 +54,7 @@ export default function({ getService, getPageObjects }) { await PageObjects.visualize.saveVisualizationExpectSuccessAndBreadcrumb(vizName1); await PageObjects.visualize.loadSavedVisualization(vizName1); - await PageObjects.visualize.waitForVisualization(); + await PageObjects.visChart.waitForVisualization(); }); it('should have inspector enabled', async function() { @@ -93,7 +93,7 @@ export default function({ getService, getPageObjects }) { // return arguments[0].getAttribute(arguments[1]);","args":[{"ELEMENT":"592"},"fill"]}] arguments[0].getAttribute is not a function // try sleeping a bit before getting that data await retry.try(async () => { - const data = await PageObjects.visualize.getBarChartData(); + const data = await PageObjects.visChart.getBarChartData(); log.debug('data=' + data); log.debug('data.length=' + data.length); expect(data).to.eql(expectedChartValues); @@ -134,13 +134,13 @@ export default function({ getService, getPageObjects }) { const axisId = 'ValueAxis-1'; it('should show ticks on selecting log scale', async () => { - await PageObjects.visualize.clickMetricsAndAxes(); - await PageObjects.visualize.clickYAxisOptions(axisId); - await PageObjects.visualize.selectYAxisScaleType(axisId, 'log'); - await PageObjects.visualize.clickYAxisAdvancedOptions(axisId); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.clickMetricsAndAxes(); + await PageObjects.visEditor.clickYAxisOptions(axisId); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'log'); + await PageObjects.visEditor.clickYAxisAdvancedOptions(axisId); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -166,9 +166,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting log scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '2', '3', @@ -194,10 +194,10 @@ export default function({ getService, getPageObjects }) { }); it('should show ticks on selecting square root scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'square root'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'square root'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = [ '0', '200', @@ -213,18 +213,18 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting square root scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); it('should show ticks on selecting linear scale', async () => { - await PageObjects.visualize.selectYAxisScaleType(axisId, 'linear'); - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, false); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.selectYAxisScaleType(axisId, 'linear'); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); const expectedLabels = [ '0', @@ -241,9 +241,9 @@ export default function({ getService, getPageObjects }) { }); it('should show filtered ticks on selecting linear scale', async () => { - await PageObjects.visualize.changeYAxisFilterLabelsCheckbox(axisId, true); - await PageObjects.visualize.clickGo(); - const labels = await PageObjects.visualize.getYAxisLabels(); + await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); + await PageObjects.visEditor.clickGo(); + const labels = await PageObjects.visChart.getYAxisLabels(); const expectedLabels = ['200', '400', '600', '800', '1,000', '1,200', '1,400']; expect(labels).to.eql(expectedLabels); }); @@ -253,18 +253,18 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('response.raw'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('response.raw'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.sleep(1003); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await PageObjects.header.waitUntilLoadingHasFinished(); const expectedEntries = ['200', '404', '503']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); }); @@ -273,20 +273,20 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('response.raw'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('response.raw'); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.visualize.toggleOpenEditor(3, 'false'); - await PageObjects.visualize.clickBucket('Split series'); - await PageObjects.visualize.selectAggregation('Terms'); - await PageObjects.visualize.selectField('machine.os'); + await PageObjects.visEditor.toggleOpenEditor(3, 'false'); + await PageObjects.visEditor.clickBucket('Split series'); + await PageObjects.visEditor.selectAggregation('Terms'); + await PageObjects.visEditor.selectField('machine.os'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.sleep(1003); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await PageObjects.header.waitUntilLoadingHasFinished(); const expectedEntries = [ @@ -306,17 +306,17 @@ export default function({ getService, getPageObjects }) { '404 - win 8', '404 - win xp', ]; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); it('should show correct series when disabling first agg', async function() { - await PageObjects.visualize.toggleDisabledAgg(3); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.toggleDisabledAgg(3); + await PageObjects.visEditor.clickGo(); await PageObjects.header.waitUntilLoadingHasFinished(); const expectedEntries = ['win 8', 'win xp', 'ios', 'osx', 'win 7']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); }); @@ -325,17 +325,17 @@ export default function({ getService, getPageObjects }) { before(initBarChart); it('should show correct series', async function() { - await PageObjects.visualize.toggleOpenEditor(2, 'false'); - await PageObjects.visualize.toggleOpenEditor(1); - await PageObjects.visualize.selectAggregation('Derivative', 'metrics'); + await PageObjects.visEditor.toggleOpenEditor(2, 'false'); + await PageObjects.visEditor.toggleOpenEditor(1); + await PageObjects.visEditor.selectAggregation('Derivative', 'metrics'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.common.sleep(1003); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); await PageObjects.header.waitUntilLoadingHasFinished(); const expectedEntries = ['Derivative of Count']; - const legendEntries = await PageObjects.visualize.getLegendEntries(); + const legendEntries = await PageObjects.visChart.getLegendEntries(); expect(legendEntries).to.eql(expectedEntries); }); }); diff --git a/test/functional/apps/visualize/_visualize_listing.js b/test/functional/apps/visualize/_visualize_listing.js index 63a8a8310d2c11..e277c3c7d104d7 100644 --- a/test/functional/apps/visualize/_visualize_listing.js +++ b/test/functional/apps/visualize/_visualize_listing.js @@ -19,8 +19,9 @@ import expect from '@kbn/expect'; -export default function({ getPageObjects }) { - const PageObjects = getPageObjects(['visualize', 'header', 'common']); +export default function({ getService, getPageObjects }) { + const PageObjects = getPageObjects(['visualize', 'visEditor']); + const listingTable = getService('listingTable'); // FLAKY: https://github.com/elastic/kibana/issues/40912 describe.skip('visualize listing page', function describeIndexTests() { @@ -36,7 +37,7 @@ export default function({ getPageObjects }) { // type markdown is used for simplicity await PageObjects.visualize.createSimpleMarkdownViz(vizName); await PageObjects.visualize.gotoVisualizationLandingPage(); - const visCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + const visCount = await listingTable.getItemsCount('visualize'); expect(visCount).to.equal(1); }); @@ -45,11 +46,11 @@ export default function({ getPageObjects }) { await PageObjects.visualize.createSimpleMarkdownViz(vizName + '2'); await PageObjects.visualize.gotoVisualizationLandingPage(); - let visCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + let visCount = await listingTable.getItemsCount('visualize'); expect(visCount).to.equal(3); await PageObjects.visualize.deleteAllVisualizations(); - visCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + visCount = await listingTable.getItemsCount('visualize'); expect(visCount).to.equal(0); }); }); @@ -60,45 +61,45 @@ export default function({ getPageObjects }) { await PageObjects.visualize.gotoVisualizationLandingPage(); await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickMarkdownWidget(); - await PageObjects.visualize.setMarkdownTxt('HELLO'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setMarkdownTxt('HELLO'); + await PageObjects.visEditor.clickGo(); await PageObjects.visualize.saveVisualization('Hello World'); await PageObjects.visualize.gotoVisualizationLandingPage(); }); it('matches on the first word', async function() { - await PageObjects.visualize.searchForItemWithName('Hello'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('Hello'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(1); }); it('matches the second word', async function() { - await PageObjects.visualize.searchForItemWithName('World'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('World'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(1); }); it('matches the second word prefix', async function() { - await PageObjects.visualize.searchForItemWithName('Wor'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('Wor'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(1); }); it('does not match mid word', async function() { - await PageObjects.visualize.searchForItemWithName('orld'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('orld'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(0); }); it('is case insensitive', async function() { - await PageObjects.visualize.searchForItemWithName('hello world'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('hello world'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(1); }); it('is using AND operator', async function() { - await PageObjects.visualize.searchForItemWithName('hello banana'); - const itemCount = await PageObjects.visualize.getCountOfItemsInListingTable(); + await listingTable.searchForItemWithName('hello banana'); + const itemCount = await listingTable.getItemsCount('visualize'); expect(itemCount).to.equal(0); }); }); diff --git a/test/functional/apps/visualize/input_control_vis/chained_controls.js b/test/functional/apps/visualize/input_control_vis/chained_controls.js index 96d9dae519b51a..b56a37218aba56 100644 --- a/test/functional/apps/visualize/input_control_vis/chained_controls.js +++ b/test/functional/apps/visualize/input_control_vis/chained_controls.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects(['common', 'visualize', 'visEditor', 'header', 'timePicker']); const testSubjects = getService('testSubjects'); const find = getService('find'); const comboBox = getService('comboBox'); @@ -65,7 +65,7 @@ export default function({ getService, getPageObjects }) { it('should create a seperate filter pill for parent control and child control', async () => { await comboBox.set('listControlSelect1', '14.61.182.136'); - await PageObjects.visualize.inputControlSubmit(); + await PageObjects.visEditor.inputControlSubmit(); const hasParentControlFilter = await filterBar.hasFilter('geo.src', 'BR'); expect(hasParentControlFilter).to.equal(true); diff --git a/test/functional/apps/visualize/input_control_vis/dynamic_options.js b/test/functional/apps/visualize/input_control_vis/dynamic_options.js index 2354855f120792..d78c780a728a77 100644 --- a/test/functional/apps/visualize/input_control_vis/dynamic_options.js +++ b/test/functional/apps/visualize/input_control_vis/dynamic_options.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects(['common', 'visualize', 'visEditor', 'header', 'timePicker']); const comboBox = getService('comboBox'); describe('dynamic options', () => { @@ -55,7 +55,7 @@ export default function({ getService, getPageObjects }) { it('should not fetch new options when non-string is filtered', async () => { await comboBox.set('fieldSelect-0', 'clientip'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const initialOptions = await comboBox.getOptionsList('listControlSelect0'); expect( diff --git a/test/functional/apps/visualize/input_control_vis/input_control_options.js b/test/functional/apps/visualize/input_control_vis/input_control_options.js index 1c3f63e94ae75b..8e8891ac585b31 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_options.js +++ b/test/functional/apps/visualize/input_control_vis/input_control_options.js @@ -21,7 +21,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'timePicker']); + const PageObjects = getPageObjects(['common', 'visualize', 'visEditor', 'header', 'timePicker']); const testSubjects = getService('testSubjects'); const inspector = getService('inspector'); const find = getService('find'); @@ -38,11 +38,11 @@ export default function({ getService, getPageObjects }) { 'Jan 1, 2017 @ 00:00:00.000', 'Jan 1, 2017 @ 00:00:00.000' ); - await PageObjects.visualize.clickVisEditorTab('controls'); - await PageObjects.visualize.addInputControl(); + await PageObjects.visEditor.clickVisEditorTab('controls'); + await PageObjects.visEditor.addInputControl(); await comboBox.set('indexPatternSelect-0', 'logstash- '); await comboBox.set('fieldSelect-0', FIELD_NAME); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }); it('should not have inspector enabled', async function() { @@ -89,7 +89,7 @@ export default function({ getService, getPageObjects }) { }); it('should add filter pill when submit button is clicked', async () => { - await PageObjects.visualize.inputControlSubmit(); + await PageObjects.visEditor.inputControlSubmit(); const hasFilter = await filterBar.hasFilter(FIELD_NAME, 'ios'); expect(hasFilter).to.equal(true); @@ -98,7 +98,7 @@ export default function({ getService, getPageObjects }) { it('should replace existing filter pill(s) when new item is selected', async () => { await comboBox.clear('listControlSelect0'); await comboBox.set('listControlSelect0', 'osx'); - await PageObjects.visualize.inputControlSubmit(); + await PageObjects.visEditor.inputControlSubmit(); await PageObjects.common.sleep(1000); const hasOldFilter = await filterBar.hasFilter(FIELD_NAME, 'ios'); @@ -117,11 +117,11 @@ export default function({ getService, getPageObjects }) { it('should clear form when Clear button is clicked but not remove filter pill', async () => { await comboBox.set('listControlSelect0', 'ios'); - await PageObjects.visualize.inputControlSubmit(); + await PageObjects.visEditor.inputControlSubmit(); const hasFilterBeforeClearBtnClicked = await filterBar.hasFilter(FIELD_NAME, 'ios'); expect(hasFilterBeforeClearBtnClicked).to.equal(true); - await PageObjects.visualize.inputControlClear(); + await PageObjects.visEditor.inputControlClear(); const hasValue = await comboBox.doesComboBoxHaveSelectedOptions('listControlSelect0'); expect(hasValue).to.equal(false); @@ -130,7 +130,7 @@ export default function({ getService, getPageObjects }) { }); it('should remove filter pill when cleared form is submitted', async () => { - await PageObjects.visualize.inputControlSubmit(); + await PageObjects.visEditor.inputControlSubmit(); const hasFilter = await filterBar.hasFilter(FIELD_NAME, 'ios'); expect(hasFilter).to.equal(false); }); @@ -138,17 +138,17 @@ export default function({ getService, getPageObjects }) { describe('updateFiltersOnChange is true', () => { before(async () => { - await PageObjects.visualize.clickVisEditorTab('options'); - await PageObjects.visualize.checkSwitch('inputControlEditorUpdateFiltersOnChangeCheckbox'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickVisEditorTab('options'); + await PageObjects.visEditor.checkSwitch('inputControlEditorUpdateFiltersOnChangeCheckbox'); + await PageObjects.visEditor.clickGo(); }); after(async () => { - await PageObjects.visualize.clickVisEditorTab('options'); - await PageObjects.visualize.uncheckSwitch( + await PageObjects.visEditor.clickVisEditorTab('options'); + await PageObjects.visEditor.uncheckSwitch( 'inputControlEditorUpdateFiltersOnChangeCheckbox' ); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); }); it('should not display staging control buttons', async () => { @@ -173,9 +173,9 @@ export default function({ getService, getPageObjects }) { describe('useTimeFilter', () => { it('should use global time filter when getting terms', async () => { - await PageObjects.visualize.clickVisEditorTab('options'); - await PageObjects.visualize.checkCheckbox('inputControlEditorUseTimeFilterCheckbox'); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickVisEditorTab('options'); + await testSubjects.setCheckbox('inputControlEditorUseTimeFilterCheckbox', 'check'); + await PageObjects.visEditor.clickGo(); // Expect control to be disabled because no terms could be gathered with time filter applied const input = await find.byCssSelector('[data-test-subj="inputControl0"] input'); diff --git a/test/functional/apps/visualize/input_control_vis/input_control_range.ts b/test/functional/apps/visualize/input_control_vis/input_control_range.ts index 2d6550de5dec91..f48ba7b54daf16 100644 --- a/test/functional/apps/visualize/input_control_vis/input_control_range.ts +++ b/test/functional/apps/visualize/input_control_vis/input_control_range.ts @@ -25,7 +25,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const find = getService('find'); - const { visualize } = getPageObjects(['visualize']); + const { visualize, visEditor } = getPageObjects(['visualize', 'visEditor']); describe('input control range', () => { before(async () => { @@ -35,36 +35,22 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { }); it('should add filter with scripted field', async () => { - await visualize.addInputControl('range'); - await visualize.setFilterParams({ - indexPattern: 'kibana_sample_data_flights', - field: 'hour_of_day', - }); - await visualize.clickGo(); - await visualize.setFilterRange({ - min: '7', - max: '10', - }); - await visualize.inputControlSubmit(); + await visEditor.addInputControl('range'); + await visEditor.setFilterParams(0, 'kibana_sample_data_flights', 'hour_of_day'); + await visEditor.clickGo(); + await visEditor.setFilterRange(0, '7', '10'); + await visEditor.inputControlSubmit(); const controlFilters = await find.allByCssSelector('[data-test-subj^="filter"]'); expect(controlFilters).to.have.length(1); expect(await controlFilters[0].getVisibleText()).to.equal('hour_of_day: 7 to 10'); }); it('should add filter with price field', async () => { - await visualize.addInputControl('range'); - await visualize.setFilterParams({ - aggNth: 1, - indexPattern: 'kibana_sample_data_flights', - field: 'AvgTicketPrice', - }); - await visualize.clickGo(); - await visualize.setFilterRange({ - aggNth: 1, - min: '400', - max: '999', - }); - await visualize.inputControlSubmit(); + await visEditor.addInputControl('range'); + await visEditor.setFilterParams(1, 'kibana_sample_data_flights', 'AvgTicketPrice'); + await visEditor.clickGo(); + await visEditor.setFilterRange(1, '400', '999'); + await visEditor.inputControlSubmit(); const controlFilters = await find.allByCssSelector('[data-test-subj^="filter"]'); expect(controlFilters).to.have.length(2); expect(await controlFilters[1].getVisibleText()).to.equal('AvgTicketPrice: $400 to $999'); diff --git a/test/functional/page_objects/index.ts b/test/functional/page_objects/index.ts index 5a104c8d17bf2c..5526243ea2bbde 100644 --- a/test/functional/page_objects/index.ts +++ b/test/functional/page_objects/index.ts @@ -39,7 +39,6 @@ import { NewsfeedPageProvider } from './newsfeed_page'; import { PointSeriesPageProvider } from './point_series_page'; // @ts-ignore not TS yet import { SettingsPageProvider } from './settings_page'; -// @ts-ignore not TS yet import { SharePageProvider } from './share_page'; // @ts-ignore not TS yet import { ShieldPageProvider } from './shield_page'; @@ -48,8 +47,12 @@ import { TimePickerPageProvider } from './time_picker'; // @ts-ignore not TS yet import { TimelionPageProvider } from './timelion_page'; import { VisualBuilderPageProvider } from './visual_builder_page'; -// @ts-ignore not TS yet import { VisualizePageProvider } from './visualize_page'; +import { VisualizeEditorPageProvider } from './visualize_editor_page'; +import { VisualizeChartPageProvider } from './visualize_chart_page'; +import { TileMapPageProvider } from './tile_map_page'; +import { TagCloudPageProvider } from './tag_cloud_page'; +import { VegaChartPageProvider } from './vega_chart_page'; export const pageObjects = { common: CommonPageProvider, @@ -70,4 +73,9 @@ export const pageObjects = { timePicker: TimePickerPageProvider, visualBuilder: VisualBuilderPageProvider, visualize: VisualizePageProvider, + visEditor: VisualizeEditorPageProvider, + visChart: VisualizeChartPageProvider, + tileMap: TileMapPageProvider, + tagCloud: TagCloudPageProvider, + vegaChart: VegaChartPageProvider, }; diff --git a/test/functional/page_objects/point_series_page.js b/test/functional/page_objects/point_series_page.js index 9172809eb73e58..74bf07b59bc381 100644 --- a/test/functional/page_objects/point_series_page.js +++ b/test/functional/page_objects/point_series_page.js @@ -23,10 +23,6 @@ export function PointSeriesPageProvider({ getService }) { const find = getService('find'); class PointSeriesVis { - async clickOptions() { - return await testSubjects.click('visEditorTaboptions'); - } - async clickAxisOptions() { return await testSubjects.click('visEditorTabadvanced'); } diff --git a/test/functional/page_objects/share_page.ts b/test/functional/page_objects/share_page.ts index 906effcb54a26f..fc8db9b78a03f7 100644 --- a/test/functional/page_objects/share_page.ts +++ b/test/functional/page_objects/share_page.ts @@ -19,10 +19,9 @@ import { FtrProviderContext } from '../ftr_provider_context'; -export function SharePageProvider({ getService, getPageObjects }: FtrProviderContext) { +export function SharePageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const find = getService('find'); - const PageObjects = getPageObjects(['visualize', 'common']); const log = getService('log'); class SharePage { @@ -78,7 +77,7 @@ export function SharePageProvider({ getService, getPageObjects }: FtrProviderCon async checkShortenUrl() { const shareForm = await testSubjects.find('shareUrlForm'); - await PageObjects.visualize.checkCheckbox('useShortUrl'); + await testSubjects.setCheckbox('useShortUrl', 'check'); await shareForm.waitForDeletedByCssSelector('.euiLoadingSpinner'); } diff --git a/test/functional/page_objects/tag_cloud_page.ts b/test/functional/page_objects/tag_cloud_page.ts new file mode 100644 index 00000000000000..7d87caa39b2fbe --- /dev/null +++ b/test/functional/page_objects/tag_cloud_page.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; +import { WebElementWrapper } from '../services/lib/web_element_wrapper'; + +export function TagCloudPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const testSubjects = getService('testSubjects'); + const { header, visChart } = getPageObjects(['header', 'visChart']); + + class TagCloudPage { + public async selectTagCloudTag(tagDisplayText: string) { + await testSubjects.click(tagDisplayText); + await header.waitUntilLoadingHasFinished(); + } + + public async getTextTag() { + await visChart.waitForVisualization(); + const elements = await find.allByCssSelector('text'); + return await Promise.all(elements.map(async element => await element.getVisibleText())); + } + + public async getTextSizes() { + const tags = await find.allByCssSelector('text'); + async function returnTagSize(tag: WebElementWrapper) { + const style = await tag.getAttribute('style'); + const fontSize = style.match(/font-size: ([^;]*);/); + return fontSize ? fontSize[1] : ''; + } + return await Promise.all(tags.map(returnTagSize)); + } + } + + return new TagCloudPage(); +} diff --git a/test/functional/page_objects/tile_map_page.ts b/test/functional/page_objects/tile_map_page.ts new file mode 100644 index 00000000000000..b41578f782af41 --- /dev/null +++ b/test/functional/page_objects/tile_map_page.ts @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export function TileMapPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const log = getService('log'); + const inspector = getService('inspector'); + const { header } = getPageObjects(['header']); + + class TileMapPage { + public async getZoomSelectors(zoomSelector: string) { + return await find.allByCssSelector(zoomSelector); + } + + public async clickMapButton(zoomSelector: string, waitForLoading?: boolean) { + await retry.try(async () => { + const zooms = await this.getZoomSelectors(zoomSelector); + await Promise.all(zooms.map(async zoom => await zoom.click())); + if (waitForLoading) { + await header.waitUntilLoadingHasFinished(); + } + }); + } + + public async getVisualizationRequest() { + log.debug('getVisualizationRequest'); + await inspector.open(); + await testSubjects.click('inspectorViewChooser'); + await testSubjects.click('inspectorViewChooserRequests'); + await testSubjects.click('inspectorRequestDetailRequest'); + return await testSubjects.getVisibleText('inspectorRequestBody'); + } + + public async getMapBounds(): Promise { + const request = await this.getVisualizationRequest(); + const requestObject = JSON.parse(request); + return requestObject.aggs.filter_agg.filter.geo_bounding_box['geo.coordinates']; + } + + public async clickMapZoomIn(waitForLoading = true) { + await this.clickMapButton('a.leaflet-control-zoom-in', waitForLoading); + } + + public async clickMapZoomOut(waitForLoading = true) { + await this.clickMapButton('a.leaflet-control-zoom-out', waitForLoading); + } + + public async getMapZoomEnabled(zoomSelector: string): Promise { + const zooms = await this.getZoomSelectors(zoomSelector); + const classAttributes = await Promise.all( + zooms.map(async zoom => await zoom.getAttribute('class')) + ); + return !classAttributes.join('').includes('leaflet-disabled'); + } + + public async zoomAllTheWayOut(): Promise { + // we can tell we're at level 1 because zoom out is disabled + return await retry.try(async () => { + await this.clickMapZoomOut(); + const enabled = await this.getMapZoomOutEnabled(); + // should be able to zoom more as current config has 0 as min level. + if (enabled) { + throw new Error('Not fully zoomed out yet'); + } + }); + } + + public async getMapZoomInEnabled() { + return await this.getMapZoomEnabled('a.leaflet-control-zoom-in'); + } + + public async getMapZoomOutEnabled() { + return await this.getMapZoomEnabled('a.leaflet-control-zoom-out'); + } + + public async clickMapFitDataBounds() { + return await this.clickMapButton('a.fa-crop'); + } + } + + return new TileMapPage(); +} diff --git a/test/functional/page_objects/vega_chart_page.ts b/test/functional/page_objects/vega_chart_page.ts new file mode 100644 index 00000000000000..9931ebebef6ef2 --- /dev/null +++ b/test/functional/page_objects/vega_chart_page.ts @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export function VegaChartPageProvider({ + getService, + getPageObjects, + updateBaselines, +}: FtrProviderContext & { updateBaselines: boolean }) { + const find = getService('find'); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const screenshot = getService('screenshots'); + const log = getService('log'); + const { visEditor, visChart } = getPageObjects(['visEditor', 'visChart']); + + class VegaChartPage { + public async getSpec() { + // Adapted from console_page.js:getVisibleTextFromAceEditor(). Is there a common utilities file? + const editor = await testSubjects.find('vega-editor'); + const lines = await editor.findAllByClassName('ace_line_group'); + const linesText = await Promise.all( + lines.map(async line => { + return await line.getVisibleText(); + }) + ); + return linesText.join('\n'); + } + + public async getViewContainer() { + return await find.byCssSelector('div.vgaVis__view'); + } + + public async getControlContainer() { + return await find.byCssSelector('div.vgaVis__controls'); + } + + /** + * Removes chrome and takes a small screenshot of a vis to compare against a baseline. + * @param {string} name The name of the baseline image. + * @param {object} opts Options object. + * @param {number} opts.threshold Threshold for allowed variance when comparing images. + */ + public async expectVisToMatchScreenshot(name: string, opts = { threshold: 0.05 }) { + log.debug(`expectVisToMatchScreenshot(${name})`); + + // Collapse sidebar and inject some CSS to hide the nav so we have a focused screenshot + await visEditor.clickEditorSidebarCollapse(); + await visChart.waitForVisualizationRenderingStabilized(); + await browser.execute(` + var el = document.createElement('style'); + el.id = '__data-test-style'; + el.innerHTML = '[data-test-subj="headerGlobalNav"] { display: none; } '; + el.innerHTML += '[data-test-subj="top-nav"] { display: none; } '; + el.innerHTML += '[data-test-subj="experimentalVisInfo"] { display: none; } '; + document.body.appendChild(el); + `); + + const percentDifference = await screenshot.compareAgainstBaseline(name, updateBaselines); + + // Reset the chart to its original state + await browser.execute(` + var el = document.getElementById('__data-test-style'); + document.body.removeChild(el); + `); + await visEditor.clickEditorSidebarCollapse(); + await visChart.waitForVisualizationRenderingStabilized(); + expect(percentDifference).to.be.lessThan(opts.threshold); + } + } + + return new VegaChartPage(); +} diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 97d57873503762..2fa59d5fd89d83 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -26,7 +26,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro const retry = getService('retry'); const testSubjects = getService('testSubjects'); const comboBox = getService('comboBox'); - const PageObjects = getPageObjects(['common', 'header', 'visualize', 'timePicker']); + const PageObjects = getPageObjects(['common', 'header', 'visualize', 'timePicker', 'visChart']); type Duration = | 'Milliseconds' @@ -101,7 +101,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro } public async getMetricValue() { - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); const metricValue = await find.byCssSelector('.tvbVisMetric__value--primary'); return metricValue.getVisibleText(); } @@ -110,7 +110,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro const input = await find.byCssSelector('.tvbMarkdownEditor__editor textarea'); await this.clearMarkdown(); await input.type(markdown, { charByChar: true }); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); } public async clearMarkdown() { @@ -304,7 +304,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro } public async getRhythmChartLegendValue(nth = 0) { - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); const metricValue = ( await find.allByCssSelector(`.echLegendItem .echLegendItem__displayValue`) )[nth]; @@ -348,7 +348,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro const prevAggs = await testSubjects.findAll('aggSelector'); const elements = await testSubjects.findAll('addMetricAddBtn'); await elements[nth].click(); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); await retry.waitFor('new agg is added', async () => { const currentAggs = await testSubjects.findAll('aggSelector'); return currentAggs.length > prevAggs.length; @@ -485,7 +485,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro await this.checkColorPickerPopUpIsPresent(); await find.setValue('.tvbColorPickerPopUp input', colorHex); await this.clickColorPicker(); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); } public async checkColorPickerPopUpIsPresent(): Promise { @@ -494,10 +494,10 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro } public async changePanelPreview(nth: number = 0): Promise { - const prevRenderingCount = await PageObjects.visualize.getVisualizationRenderingCount(); + const prevRenderingCount = await PageObjects.visChart.getVisualizationRenderingCount(); const changePreviewBtnArray = await testSubjects.findAll('AddActivatePanelBtn'); await changePreviewBtnArray[nth].click(); - await PageObjects.visualize.waitForRenderingCount(prevRenderingCount + 1); + await PageObjects.visChart.waitForRenderingCount(prevRenderingCount + 1); } public async checkPreviewIsDisabled(): Promise { @@ -508,7 +508,7 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro public async cloneSeries(nth: number = 0): Promise { const cloneBtnArray = await testSubjects.findAll('AddCloneBtn'); await cloneBtnArray[nth].click(); - await PageObjects.visualize.waitForVisualizationRenderingStabilized(); + await PageObjects.visChart.waitForVisualizationRenderingStabilized(); } /** @@ -525,10 +525,10 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro } public async deleteSeries(nth: number = 0): Promise { - const prevRenderingCount = await PageObjects.visualize.getVisualizationRenderingCount(); + const prevRenderingCount = await PageObjects.visChart.getVisualizationRenderingCount(); const cloneBtnArray = await testSubjects.findAll('AddDeleteBtn'); await cloneBtnArray[nth].click(); - await PageObjects.visualize.waitForRenderingCount(prevRenderingCount + 1); + await PageObjects.visChart.waitForRenderingCount(prevRenderingCount + 1); } public async getLegendItems(): Promise { diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts new file mode 100644 index 00000000000000..138e5758ede7cd --- /dev/null +++ b/test/functional/page_objects/visualize_chart_page.ts @@ -0,0 +1,386 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export function VisualizeChartPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const config = getService('config'); + const find = getService('find'); + const log = getService('log'); + const retry = getService('retry'); + const table = getService('table'); + const defaultFindTimeout = config.get('timeouts.find'); + const { common } = getPageObjects(['common']); + + class VisualizeChart { + public async getYAxisTitle() { + const title = await find.byCssSelector('.y-axis-div .y-axis-title text'); + return await title.getVisibleText(); + } + + public async getXAxisLabels() { + const xAxis = await find.byCssSelector('.visAxis--x.visAxis__column--bottom'); + const $ = await xAxis.parseDomContent(); + return $('.x > g > text') + .toArray() + .map(tick => + $(tick) + .text() + .trim() + ); + } + + public async getYAxisLabels() { + const yAxis = await find.byCssSelector('.visAxis__column--y.visAxis__column--left'); + const $ = await yAxis.parseDomContent(); + return $('.y > g > text') + .toArray() + .map(tick => + $(tick) + .text() + .trim() + ); + } + + /** + * Gets the chart data and scales it based on chart height and label. + * @param dataLabel data-label value + * @param axis axis value, 'ValueAxis-1' by default + * + * Returns an array of height values + */ + public async getAreaChartData(dataLabel: string, axis = 'ValueAxis-1') { + const yAxisRatio = await this.getChartYAxisRatio(axis); + + const rectangle = await find.byCssSelector('rect.background'); + const yAxisHeight = Number(await rectangle.getAttribute('height')); + log.debug(`height --------- ${yAxisHeight}`); + + const path = await retry.try( + async () => + await find.byCssSelector(`path[data-label="${dataLabel}"]`, defaultFindTimeout * 2) + ); + const data = await path.getAttribute('d'); + log.debug(data); + // This area chart data starts with a 'M'ove to a x,y location, followed + // by a bunch of 'L'ines from that point to the next. Those points are + // the values we're going to use to calculate the data values we're testing. + // So git rid of the one 'M' and split the rest on the 'L's. + const tempArray = data + .replace('M ', '') + .replace('M', '') + .replace(/ L /g, 'L') + .replace(/ /g, ',') + .split('L'); + const chartSections = tempArray.length / 2; + const chartData = []; + for (let i = 0; i < chartSections; i++) { + chartData[i] = Math.round((yAxisHeight - Number(tempArray[i].split(',')[1])) * yAxisRatio); + log.debug('chartData[i] =' + chartData[i]); + } + return chartData; + } + + /** + * Returns the paths that compose an area chart. + * @param dataLabel data-label value + */ + public async getAreaChartPaths(dataLabel: string) { + const path = await retry.try( + async () => + await find.byCssSelector(`path[data-label="${dataLabel}"]`, defaultFindTimeout * 2) + ); + const data = await path.getAttribute('d'); + log.debug(data); + // This area chart data starts with a 'M'ove to a x,y location, followed + // by a bunch of 'L'ines from that point to the next. Those points are + // the values we're going to use to calculate the data values we're testing. + // So git rid of the one 'M' and split the rest on the 'L's. + return data.split('L'); + } + + /** + * Gets the dots and normalizes their height. + * @param dataLabel data-label value + * @param axis axis value, 'ValueAxis-1' by default + */ + public async getLineChartData(dataLabel = 'Count', axis = 'ValueAxis-1') { + // 1). get the range/pixel ratio + const yAxisRatio = await this.getChartYAxisRatio(axis); + // 2). find and save the y-axis pixel size (the chart height) + const rectangle = await find.byCssSelector('clipPath rect'); + const yAxisHeight = Number(await rectangle.getAttribute('height')); + // 3). get the visWrapper__chart elements + const chartTypes = await retry.try( + async () => + await find.allByCssSelector( + `.visWrapper__chart circle[data-label="${dataLabel}"][fill-opacity="1"]`, + defaultFindTimeout * 2 + ) + ); + // 4). for each chart element, find the green circle, then the cy position + const chartData = await Promise.all( + chartTypes.map(async chart => { + const cy = Number(await chart.getAttribute('cy')); + // the point_series_options test has data in the billions range and + // getting 11 digits of precision with these calculations is very hard + return Math.round(Number(((yAxisHeight - cy) * yAxisRatio).toPrecision(6))); + }) + ); + + return chartData; + } + + /** + * Returns bar chart data in pixels + * @param dataLabel data-label value + * @param axis axis value, 'ValueAxis-1' by default + */ + public async getBarChartData(dataLabel = 'Count', axis = 'ValueAxis-1') { + const yAxisRatio = await this.getChartYAxisRatio(axis); + const svg = await find.byCssSelector('div.chart'); + const $ = await svg.parseDomContent(); + const chartData = $(`g > g.series > rect[data-label="${dataLabel}"]`) + .toArray() + .map(chart => { + const barHeight = Number($(chart).attr('height')); + return Math.round(barHeight * yAxisRatio); + }); + + return chartData; + } + + /** + * Returns the range/pixel ratio + * @param axis axis value, 'ValueAxis-1' by default + */ + private async getChartYAxisRatio(axis = 'ValueAxis-1') { + // 1). get the maximum chart Y-Axis marker value and Y position + const maxYAxisChartMarker = await retry.try( + async () => + await find.byCssSelector( + `div.visAxis__splitAxes--y > div > svg > g.${axis} > g:last-of-type.tick` + ) + ); + const maxYLabel = (await maxYAxisChartMarker.getVisibleText()).replace(/,/g, ''); + const maxYLabelYPosition = (await maxYAxisChartMarker.getPosition()).y; + log.debug(`maxYLabel = ${maxYLabel}, maxYLabelYPosition = ${maxYLabelYPosition}`); + + // 2). get the minimum chart Y-Axis marker value and Y position + const minYAxisChartMarker = await find.byCssSelector( + 'div.visAxis__column--y.visAxis__column--left > div > div > svg:nth-child(2) > g > g:nth-child(1).tick' + ); + const minYLabel = (await minYAxisChartMarker.getVisibleText()).replace(',', ''); + const minYLabelYPosition = (await minYAxisChartMarker.getPosition()).y; + return (Number(maxYLabel) - Number(minYLabel)) / (minYLabelYPosition - maxYLabelYPosition); + } + + public async toggleLegend(show = true) { + await retry.try(async () => { + const isVisible = find.byCssSelector('.visLegend'); + if ((show && !isVisible) || (!show && isVisible)) { + await testSubjects.click('vislibToggleLegend'); + } + }); + } + + public async filterLegend(name: string) { + await this.toggleLegend(); + await testSubjects.click(`legend-${name}`); + const filters = await testSubjects.find(`legend-${name}-filters`); + const [filterIn] = await filters.findAllByCssSelector(`input`); + await filterIn.click(); + await this.waitForVisualizationRenderingStabilized(); + } + + public async doesLegendColorChoiceExist(color: string) { + return await testSubjects.exists(`legendSelectColor-${color}`); + } + + public async selectNewLegendColorChoice(color: string) { + await testSubjects.click(`legendSelectColor-${color}`); + } + + public async doesSelectedLegendColorExist(color: string) { + return await testSubjects.exists(`legendSelectedColor-${color}`); + } + + public async expectError() { + await testSubjects.existOrFail('visLibVisualizeError'); + } + + public async getVisualizationRenderingCount() { + const visualizationLoader = await testSubjects.find('visualizationLoader'); + const renderingCount = await visualizationLoader.getAttribute('data-rendering-count'); + return Number(renderingCount); + } + + public async waitForRenderingCount(minimumCount = 1) { + await retry.waitFor( + `rendering count to be greater than or equal to [${minimumCount}]`, + async () => { + const currentRenderingCount = await this.getVisualizationRenderingCount(); + log.debug(`-- currentRenderingCount=${currentRenderingCount}`); + return currentRenderingCount >= minimumCount; + } + ); + } + + public async waitForVisualizationRenderingStabilized() { + // assuming rendering is done when data-rendering-count is constant within 1000 ms + await retry.waitFor('rendering count to stabilize', async () => { + const firstCount = await this.getVisualizationRenderingCount(); + log.debug(`-- firstCount=${firstCount}`); + + await common.sleep(1000); + + const secondCount = await this.getVisualizationRenderingCount(); + log.debug(`-- secondCount=${secondCount}`); + + return firstCount === secondCount; + }); + } + + public async waitForVisualization() { + await this.waitForVisualizationRenderingStabilized(); + await find.byCssSelector('.visualization'); + } + + public async getLegendEntries() { + const legendEntries = await find.allByCssSelector( + '.visLegend__button', + defaultFindTimeout * 2 + ); + return await Promise.all( + legendEntries.map(async chart => await chart.getAttribute('data-label')) + ); + } + + public async openLegendOptionColors(name: string) { + await this.waitForVisualizationRenderingStabilized(); + await retry.try(async () => { + // This click has been flaky in opening the legend, hence the retry. See + // https://github.com/elastic/kibana/issues/17468 + await testSubjects.click(`legend-${name}`); + await this.waitForVisualizationRenderingStabilized(); + // arbitrary color chosen, any available would do + const isOpen = await this.doesLegendColorChoiceExist('#EF843C'); + if (!isOpen) { + throw new Error('legend color selector not open'); + } + }); + } + + public async filterOnTableCell(column: string, row: string) { + await retry.try(async () => { + const tableVis = await testSubjects.find('tableVis'); + const cell = await tableVis.findByCssSelector( + `tbody tr:nth-child(${row}) td:nth-child(${column})` + ); + await cell.moveMouseTo(); + const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell); + await filterBtn.click(); + }); + } + + public async getMarkdownText() { + const markdownContainer = await testSubjects.find('markdownBody'); + return markdownContainer.getVisibleText(); + } + + public async getMarkdownBodyDescendentText(selector: string) { + const markdownContainer = await testSubjects.find('markdownBody'); + const element = await find.descendantDisplayedByCssSelector(selector, markdownContainer); + return element.getVisibleText(); + } + + /** + * If you are writing new tests, you should rather look into getTableVisContent method instead. + */ + public async getTableVisData() { + return await testSubjects.getVisibleText('paginated-table-body'); + } + + /** + * This function is the newer function to retrieve data from within a table visualization. + * It uses a better return format, than the old getTableVisData, by properly splitting + * cell values into arrays. Please use this function for newer tests. + */ + public async getTableVisContent({ stripEmptyRows = true } = {}) { + return await retry.try(async () => { + const container = await testSubjects.find('tableVis'); + const allTables = await testSubjects.findAllDescendant('paginated-table-body', container); + + if (allTables.length === 0) { + return []; + } + + const allData = await Promise.all( + allTables.map(async t => { + let data = await table.getDataFromElement(t); + if (stripEmptyRows) { + data = data.filter(row => row.length > 0 && row.some(cell => cell.trim().length > 0)); + } + return data; + }) + ); + + if (allTables.length === 1) { + // If there was only one table we return only the data for that table + // This prevents an unnecessary array around that single table, which + // is the case we have in most tests. + return allData[0]; + } + + return allData; + }); + } + + public async getMetric() { + const elements = await find.allByCssSelector( + '[data-test-subj="visualizationLoader"] .mtrVis__container' + ); + const values = await Promise.all( + elements.map(async element => { + const text = await element.getVisibleText(); + return text; + }) + ); + return values + .filter(item => item.length > 0) + .reduce((arr: string[], item) => arr.concat(item.split('\n')), []); + } + + public async getGaugeValue() { + const elements = await find.allByCssSelector( + '[data-test-subj="visualizationLoader"] .chart svg text' + ); + const values = await Promise.all( + elements.map(async element => { + const text = await element.getVisibleText(); + return text; + }) + ); + return values.filter(item => item.length > 0); + } + } + + return new VisualizeChart(); +} diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts new file mode 100644 index 00000000000000..7e512975356f3b --- /dev/null +++ b/test/functional/page_objects/visualize_editor_page.ts @@ -0,0 +1,462 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../ftr_provider_context'; + +export function VisualizeEditorPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const log = getService('log'); + const retry = getService('retry'); + const browser = getService('browser'); + const testSubjects = getService('testSubjects'); + const comboBox = getService('comboBox'); + const { common, header, visChart } = getPageObjects(['common', 'header', 'visChart']); + + interface IntervalOptions { + type?: 'default' | 'numeric' | 'custom'; + aggNth?: number; + append?: boolean; + } + + class VisualizeEditorPage { + public async clickDataTab() { + await testSubjects.click('visualizeEditDataLink'); + } + + public async clickOptionsTab() { + await testSubjects.click('visEditorTaboptions'); + } + + public async clickMetricsAndAxes() { + await testSubjects.click('visEditorTabadvanced'); + } + + public async clickVisEditorTab(tabName: string) { + await testSubjects.click('visEditorTab' + tabName); + await header.waitUntilLoadingHasFinished(); + } + + public async addInputControl(type?: string) { + if (type) { + const selectInput = await testSubjects.find('selectControlType'); + await selectInput.type(type); + } + await testSubjects.click('inputControlEditorAddBtn'); + await header.waitUntilLoadingHasFinished(); + } + + public async inputControlClear() { + await testSubjects.click('inputControlClearBtn'); + await header.waitUntilLoadingHasFinished(); + } + + public async inputControlSubmit() { + await testSubjects.clickWhenNotDisabled('inputControlSubmitBtn'); + await visChart.waitForVisualizationRenderingStabilized(); + } + + public async clickGo() { + const prevRenderingCount = await visChart.getVisualizationRenderingCount(); + log.debug(`Before Rendering count ${prevRenderingCount}`); + await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); + await visChart.waitForRenderingCount(prevRenderingCount + 1); + } + + public async removeDimension(aggNth: number) { + await testSubjects.click(`visEditorAggAccordion${aggNth} > removeDimensionBtn`); + } + + public async setFilterParams(aggNth: number, indexPattern: string, field: string) { + await comboBox.set(`indexPatternSelect-${aggNth}`, indexPattern); + await comboBox.set(`fieldSelect-${aggNth}`, field); + } + + public async setFilterRange(aggNth: number, min: string, max: string) { + const control = await testSubjects.find(`inputControl${aggNth}`); + const inputMin = await control.findByCssSelector('[name$="minValue"]'); + await inputMin.type(min); + const inputMax = await control.findByCssSelector('[name$="maxValue"]'); + await inputMax.type(max); + } + + public async clickSplitDirection(direction: string) { + const control = await testSubjects.find('visEditorSplitBy'); + const radioBtn = await control.findByCssSelector(`[title="${direction}"]`); + await radioBtn.click(); + } + + /** + * Adds new bucket + * @param bucketName bucket name, like 'X-axis', 'Split rows', 'Split series' + * @param type aggregation type, like 'buckets', 'metrics' + */ + public async clickBucket(bucketName: string, type = 'buckets') { + await testSubjects.click(`visEditorAdd_${type}`); + await find.clickByCssSelector(`[data-test-subj="visEditorAdd_${type}_${bucketName}"`); + } + + public async clickEnableCustomRanges() { + await testSubjects.click('heatmapUseCustomRanges'); + } + + public async clickAddRange() { + await testSubjects.click(`heatmapColorRange__addRangeButton`); + } + + public async setCustomRangeByIndex(index: string, from: string, to: string) { + await testSubjects.setValue(`heatmapColorRange${index}__from`, from); + await testSubjects.setValue(`heatmapColorRange${index}__to`, to); + } + + public async changeHeatmapColorNumbers(value = 6) { + const input = await testSubjects.find(`heatmapColorsNumber`); + await input.clearValueWithKeyboard(); + await input.type(`${value}`); + } + + public async getBucketErrorMessage() { + const error = await find.byCssSelector( + '[group-name="buckets"] [data-test-subj="defaultEditorAggSelect"] + .euiFormErrorText' + ); + const errorMessage = await error.getAttribute('innerText'); + log.debug(errorMessage); + return errorMessage; + } + + public async addNewFilterAggregation() { + await testSubjects.click('visEditorAddFilterButton'); + } + + public async selectField( + fieldValue: string, + groupName = 'buckets', + childAggregationType = false + ) { + log.debug(`selectField ${fieldValue}`); + const selector = ` + [group-name="${groupName}"] + [data-test-subj^="visEditorAggAccordion"].euiAccordion-isOpen + [data-test-subj="visAggEditorParams"] + ${childAggregationType ? '.visEditorAgg__subAgg' : ''} + [data-test-subj="visDefaultEditorField"] + `; + const fieldEl = await find.byCssSelector(selector); + await comboBox.setElement(fieldEl, fieldValue); + } + + public async selectOrderByMetric(aggNth: number, metric: string) { + const sortSelect = await testSubjects.find(`visEditorOrderBy${aggNth}`); + const sortMetric = await sortSelect.findByCssSelector(`option[value="${metric}"]`); + await sortMetric.click(); + } + + public async selectCustomSortMetric(aggNth: number, metric: string, field: string) { + await this.selectOrderByMetric(aggNth, 'custom'); + await this.selectAggregation(metric, 'buckets', true); + await this.selectField(field, 'buckets', true); + } + + public async selectAggregation( + aggValue: string, + groupName = 'buckets', + childAggregationType = false + ) { + const comboBoxElement = await find.byCssSelector(` + [group-name="${groupName}"] + [data-test-subj^="visEditorAggAccordion"].euiAccordion-isOpen + ${childAggregationType ? '.visEditorAgg__subAgg' : ''} + [data-test-subj="defaultEditorAggSelect"] + `); + + await comboBox.setElement(comboBoxElement, aggValue); + await common.sleep(500); + } + + /** + * Set the test for a filter aggregation. + * @param {*} filterValue the string value of the filter + * @param {*} filterIndex used when multiple filters are configured on the same aggregation + * @param {*} aggregationId the ID if the aggregation. On Tests, it start at from 2 + */ + public async setFilterAggregationValue( + filterValue: string, + filterIndex = 0, + aggregationId = 2 + ) { + await testSubjects.setValue( + `visEditorFilterInput_${aggregationId}_${filterIndex}`, + filterValue + ); + } + + public async setValue(newValue: string) { + const input = await find.byCssSelector('[data-test-subj="visEditorPercentileRanks"] input'); + await input.clearValue(); + await input.type(newValue); + } + + public async clickEditorSidebarCollapse() { + await testSubjects.click('collapseSideBarButton'); + } + + public async clickDropPartialBuckets() { + await testSubjects.click('dropPartialBucketsCheckbox'); + } + + public async setMarkdownTxt(markdownTxt: string) { + const input = await testSubjects.find('markdownTextarea'); + await input.clearValue(); + await input.type(markdownTxt); + } + + public async isSwitchChecked(selector: string) { + const checkbox = await testSubjects.find(selector); + const isChecked = await checkbox.getAttribute('aria-checked'); + return isChecked === 'true'; + } + + public async checkSwitch(selector: string) { + const isChecked = await this.isSwitchChecked(selector); + if (!isChecked) { + log.debug(`checking switch ${selector}`); + await testSubjects.click(selector); + } + } + + public async uncheckSwitch(selector: string) { + const isChecked = await this.isSwitchChecked(selector); + if (isChecked) { + log.debug(`unchecking switch ${selector}`); + await testSubjects.click(selector); + } + } + + public async setIsFilteredByCollarCheckbox(value = true) { + await retry.try(async () => { + const isChecked = await this.isSwitchChecked('isFilteredByCollarCheckbox'); + if (isChecked !== value) { + await testSubjects.click('isFilteredByCollarCheckbox'); + throw new Error('isFilteredByCollar not set correctly'); + } + }); + } + + public async setCustomLabel(label: string, index = 1) { + const customLabel = await testSubjects.find(`visEditorStringInput${index}customLabel`); + customLabel.type(label); + } + + public async selectYAxisAggregation(agg: string, field: string, label: string, index = 1) { + // index starts on the first "count" metric at 1 + // Each new metric or aggregation added to a visualization gets the next index. + // So to modify a metric or aggregation tests need to keep track of the + // order they are added. + await this.toggleOpenEditor(index); + + // select our agg + const aggSelect = await find.byCssSelector( + `#visEditorAggAccordion${index} [data-test-subj="defaultEditorAggSelect"]` + ); + await comboBox.setElement(aggSelect, agg); + + const fieldSelect = await find.byCssSelector( + `#visEditorAggAccordion${index} [data-test-subj="visDefaultEditorField"]` + ); + // select our field + await comboBox.setElement(fieldSelect, field); + // enter custom label + await this.setCustomLabel(label, index); + } + + public async getField() { + return await comboBox.getComboBoxSelectedOptions('visDefaultEditorField'); + } + + public async sizeUpEditor() { + await testSubjects.click('visualizeEditorResizer'); + await browser.pressKeys(browser.keys.ARROW_RIGHT); + } + + public async toggleDisabledAgg(agg: string) { + await testSubjects.click(`visEditorAggAccordion${agg} > ~toggleDisableAggregationBtn`); + await header.waitUntilLoadingHasFinished(); + } + + public async toggleAggregationEditor(agg: string) { + await find.clickByCssSelector( + `[data-test-subj="visEditorAggAccordion${agg}"] .euiAccordion__button` + ); + await header.waitUntilLoadingHasFinished(); + } + + public async toggleOtherBucket(agg = 2) { + await testSubjects.click(`visEditorAggAccordion${agg} > otherBucketSwitch`); + } + + public async toggleMissingBucket(agg = 2) { + await testSubjects.click(`visEditorAggAccordion${agg} > missingBucketSwitch`); + } + + public async toggleScaleMetrics() { + await testSubjects.click('scaleMetricsSwitch'); + } + + public async toggleAutoMode() { + await testSubjects.click('visualizeEditorAutoButton'); + } + + public async isApplyEnabled() { + const applyButton = await testSubjects.find('visualizeEditorRenderButton'); + return await applyButton.isEnabled(); + } + + public async toggleAccordion(id: string, toState = 'true') { + const toggle = await find.byCssSelector(`button[aria-controls="${id}"]`); + const toggleOpen = await toggle.getAttribute('aria-expanded'); + log.debug(`toggle ${id} expand = ${toggleOpen}`); + if (toggleOpen !== toState) { + log.debug(`toggle ${id} click()`); + await toggle.click(); + } + } + + public async toggleOpenEditor(index: number, toState = 'true') { + // index, see selectYAxisAggregation + await this.toggleAccordion(`visEditorAggAccordion${index}`, toState); + } + + public async toggleAdvancedParams(aggId: string) { + const accordion = await testSubjects.find(`advancedParams-${aggId}`); + const accordionButton = await find.descendantDisplayedByCssSelector('button', accordion); + await accordionButton.click(); + } + + public async clickReset() { + await testSubjects.click('visualizeEditorResetButton'); + await visChart.waitForVisualization(); + } + + public async clickYAxisOptions(axisId: string) { + await testSubjects.click(`toggleYAxisOptions-${axisId}`); + } + + public async clickYAxisAdvancedOptions(axisId: string) { + await testSubjects.click(`toggleYAxisAdvancedOptions-${axisId}`); + } + + public async changeYAxisFilterLabelsCheckbox(axisId: string, enabled: boolean) { + const selector = `yAxisFilterLabelsCheckbox-${axisId}`; + await testSubjects.setCheckbox(selector, enabled ? 'check' : 'uncheck'); + } + + public async setSize(newValue: string, aggId: string) { + const dataTestSubj = aggId + ? `visEditorAggAccordion${aggId} > sizeParamEditor` + : 'sizeParamEditor'; + await testSubjects.setValue(dataTestSubj, String(newValue)); + } + + public async selectChartMode(mode: string) { + const selector = await find.byCssSelector(`#seriesMode0 > option[value="${mode}"]`); + await selector.click(); + } + + public async selectYAxisScaleType(axisId: string, scaleType: string) { + const selectElement = await testSubjects.find(`scaleSelectYAxis-${axisId}`); + const selector = await selectElement.findByCssSelector(`option[value="${scaleType}"]`); + await selector.click(); + } + + public async selectYAxisMode(mode: string) { + const selector = await find.byCssSelector(`#valueAxisMode0 > option[value="${mode}"]`); + await selector.click(); + } + + public async setAxisExtents(min: string, max: string, axisId = 'ValueAxis-1') { + await this.toggleAccordion(`yAxisAccordion${axisId}`); + await this.toggleAccordion(`yAxisOptionsAccordion${axisId}`); + + await testSubjects.click('yAxisSetYExtents'); + await testSubjects.setValue('yAxisYExtentsMax', max); + await testSubjects.setValue('yAxisYExtentsMin', min); + } + + public async selectAggregateWith(fieldValue: string) { + await testSubjects.selectValue('visDefaultEditorAggregateWith', fieldValue); + } + + public async setInterval(newValue: string, options: IntervalOptions = {}) { + const { type = 'default', aggNth = 2, append = false } = options; + log.debug(`visEditor.setInterval(${newValue}, {${type}, ${aggNth}, ${append}})`); + if (type === 'default') { + await comboBox.set('visEditorInterval', newValue); + } else if (type === 'custom') { + await comboBox.setCustom('visEditorInterval', newValue); + } else { + if (append) { + await testSubjects.append(`visEditorInterval${aggNth}`, String(newValue)); + } else { + await testSubjects.setValue(`visEditorInterval${aggNth}`, String(newValue)); + } + } + } + + public async getInterval() { + return await comboBox.getComboBoxSelectedOptions('visEditorInterval'); + } + + public async getNumericInterval(agg = 2) { + return await testSubjects.getAttribute(`visEditorInterval${agg}`, 'value'); + } + + public async clickMetricEditor() { + await find.clickByCssSelector('[group-name="metrics"] .euiAccordion__button'); + } + + public async clickMetricByIndex(index: number) { + const metrics = await find.allByCssSelector( + '[data-test-subj="visualizationLoader"] .mtrVis .mtrVis__container' + ); + expect(metrics.length).greaterThan(index); + await metrics[index].click(); + } + + public async setSelectByOptionText(selectId: string, optionText: string) { + const selectField = await find.byCssSelector(`#${selectId}`); + const options = await find.allByCssSelector(`#${selectId} > option`); + const $ = await selectField.parseDomContent(); + const optionsText = $('option') + .toArray() + .map(option => $(option).text()); + const optionIndex = optionsText.indexOf(optionText); + + if (optionIndex === -1) { + throw new Error( + `Unable to find option '${optionText}' in select ${selectId}. Available options: ${optionsText.join( + ',' + )}` + ); + } + await options[optionIndex].click(); + } + } + + return new VisualizeEditorPage(); +} diff --git a/test/functional/page_objects/visualize_page.js b/test/functional/page_objects/visualize_page.js deleted file mode 100644 index c1ea8be9be98b9..00000000000000 --- a/test/functional/page_objects/visualize_page.js +++ /dev/null @@ -1,1402 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { VisualizeConstants } from '../../../src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_constants'; -import Bluebird from 'bluebird'; -import expect from '@kbn/expect'; - -export function VisualizePageProvider({ getService, getPageObjects, updateBaselines }) { - const browser = getService('browser'); - const config = getService('config'); - const testSubjects = getService('testSubjects'); - const retry = getService('retry'); - const find = getService('find'); - const log = getService('log'); - const inspector = getService('inspector'); - const screenshot = getService('screenshots'); - const table = getService('table'); - const globalNav = getService('globalNav'); - const PageObjects = getPageObjects(['common', 'header']); - const defaultFindTimeout = config.get('timeouts.find'); - const comboBox = getService('comboBox'); - - class VisualizePage { - get index() { - return { - LOGSTASH_TIME_BASED: 'logstash-*', - LOGSTASH_NON_TIME_BASED: 'logstash*', - }; - } - - async gotoVisualizationLandingPage() { - log.debug('gotoVisualizationLandingPage'); - await PageObjects.common.navigateToApp('visualize'); - } - - async checkListingSelectAllCheckbox() { - const element = await testSubjects.find('checkboxSelectAll'); - const isSelected = await element.isSelected(); - if (!isSelected) { - log.debug(`checking checkbox "checkboxSelectAll"`); - await testSubjects.click('checkboxSelectAll'); - } - } - - async navigateToNewVisualization() { - log.debug('navigateToApp visualize'); - await PageObjects.common.navigateToApp('visualize'); - await this.clickNewVisualization(); - await this.waitForVisualizationSelectPage(); - } - - async clickNewVisualization() { - // newItemButton button is only visible when there are items in the listing table is displayed. - let exists = await testSubjects.exists('newItemButton'); - if (exists) { - return await testSubjects.click('newItemButton'); - } - - exists = await testSubjects.exists('createVisualizationPromptButton'); - // no viz exist, click createVisualizationPromptButton to create new dashboard - return await this.createVisualizationPromptButton(); - } - - /* - This method should use retry loop to delete visualizations from multiple pages until we find the createVisualizationPromptButton. - Perhaps it *could* set the page size larger than the default 10, but it might still need to loop anyway. - */ - async deleteAllVisualizations() { - await retry.try(async () => { - await this.checkListingSelectAllCheckbox(); - await this.clickDeleteSelected(); - await PageObjects.common.clickConfirmOnModal(); - await testSubjects.find('createVisualizationPromptButton'); - }); - } - - async createSimpleMarkdownViz(vizName) { - await this.gotoVisualizationLandingPage(); - await this.navigateToNewVisualization(); - await this.clickMarkdownWidget(); - await this.setMarkdownTxt(vizName); - await this.clickGo(); - await this.saveVisualization(vizName); - } - - async createVisualizationPromptButton() { - await testSubjects.click('createVisualizationPromptButton'); - } - - async getSearchFilter() { - const searchFilter = await find.allByCssSelector('.euiFieldSearch'); - return searchFilter[0]; - } - - async clearFilter() { - const searchFilter = await this.getSearchFilter(); - await searchFilter.clearValue(); - await searchFilter.click(); - } - - async searchForItemWithName(name) { - log.debug(`searchForItemWithName: ${name}`); - - await retry.try(async () => { - const searchFilter = await this.getSearchFilter(); - await searchFilter.clearValue(); - await searchFilter.click(); - // Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed. - await searchFilter.type(name.replace('-', ' ')); - await PageObjects.common.pressEnterKey(); - }); - - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async clickDeleteSelected() { - await testSubjects.click('deleteSelectedItems'); - } - - async getCreatePromptExists() { - log.debug('getCreatePromptExists'); - return await testSubjects.exists('createVisualizationPromptButton'); - } - - async getCountOfItemsInListingTable() { - const elements = await find.allByCssSelector('[data-test-subj^="visListingTitleLink"]'); - return elements.length; - } - - async waitForVisualizationSelectPage() { - await retry.try(async () => { - const visualizeSelectTypePage = await testSubjects.find('visNewDialogTypes'); - if (!(await visualizeSelectTypePage.isDisplayed())) { - throw new Error('wait for visualization select page'); - } - }); - } - - async clickVisType(type) { - await testSubjects.click(`visType-${type}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async clickAreaChart() { - await this.clickVisType('area'); - } - - async clickDataTable() { - await this.clickVisType('table'); - } - - async clickLineChart() { - await this.clickVisType('line'); - } - - async clickRegionMap() { - await this.clickVisType('region_map'); - } - - async clickMarkdownWidget() { - await this.clickVisType('markdown'); - } - - // clickBucket(bucketName) 'X-axis', 'Split area', 'Split chart' - async clickBucket(bucketName, type = 'buckets') { - await testSubjects.click(`visEditorAdd_${type}`); - await find.clickByCssSelector(`[data-test-subj="visEditorAdd_${type}_${bucketName}"`); - } - - async clickMetric() { - await this.clickVisType('metric'); - } - - async clickGauge() { - await this.clickVisType('gauge'); - } - - async clickPieChart() { - await this.clickVisType('pie'); - } - - async clickTileMap() { - await this.clickVisType('tile_map'); - } - - async clickTagCloud() { - await this.clickVisType('tagcloud'); - } - - async clickVega() { - await this.clickVisType('vega'); - } - - async clickVisualBuilder() { - await this.clickVisType('metrics'); - } - - async clickEditorSidebarCollapse() { - await testSubjects.click('collapseSideBarButton'); - } - - async selectTagCloudTag(tagDisplayText) { - await testSubjects.click(tagDisplayText); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async getTextTag() { - await this.waitForVisualization(); - const elements = await find.allByCssSelector('text'); - return await Promise.all(elements.map(async element => await element.getVisibleText())); - } - - async getTextSizes() { - const tags = await find.allByCssSelector('text'); - async function returnTagSize(tag) { - const style = await tag.getAttribute('style'); - return style.match(/font-size: ([^;]*);/)[1]; - } - return await Promise.all(tags.map(returnTagSize)); - } - - async clickVerticalBarChart() { - await this.clickVisType('histogram'); - } - - async clickHeatmapChart() { - await this.clickVisType('heatmap'); - } - - async clickInputControlVis() { - await this.clickVisType('input_control_vis'); - } - - async getChartTypes() { - const chartTypeField = await testSubjects.find('visNewDialogTypes'); - const chartTypes = await chartTypeField.findAllByTagName('button'); - async function getChartType(chart) { - const label = await testSubjects.findDescendant('visTypeTitle', chart); - return await label.getVisibleText(); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return await Promise.all(getChartTypesPromises); - } - - async selectVisSourceIfRequired() { - log.debug('selectVisSourceIfRequired'); - const selectPage = await testSubjects.findAll('visualizeSelectSearch'); - if (selectPage.length) { - log.debug('a search is required for this visualization'); - await this.clickNewSearch(); - } - } - - async isBetaInfoShown() { - return await testSubjects.exists('betaVisInfo'); - } - - async getBetaTypeLinks() { - return await find.allByCssSelector('[data-vis-stage="beta"]'); - } - - async getExperimentalTypeLinks() { - return await find.allByCssSelector('[data-vis-stage="experimental"]'); - } - - async isExperimentalInfoShown() { - return await testSubjects.exists('experimentalVisInfo'); - } - - async getExperimentalInfo() { - return await testSubjects.find('experimentalVisInfo'); - } - - async clickAbsoluteButton() { - await find.clickByCssSelector( - 'ul.nav.nav-pills.nav-stacked.kbn-timepicker-modes:contains("absolute")', - defaultFindTimeout * 2 - ); - } - - async clickDropPartialBuckets() { - return await testSubjects.click('dropPartialBucketsCheckbox'); - } - - async setMarkdownTxt(markdownTxt) { - const input = await testSubjects.find('markdownTextarea'); - await input.clearValue(); - await input.type(markdownTxt); - } - - async getMarkdownText() { - const markdownContainer = await testSubjects.find('markdownBody'); - return markdownContainer.getVisibleText(); - } - - async getMarkdownBodyDescendentText(selector) { - const markdownContainer = await testSubjects.find('markdownBody'); - const element = await find.descendantDisplayedByCssSelector(selector, markdownContainer); - return element.getVisibleText(); - } - - async getVegaSpec() { - // Adapted from console_page.js:getVisibleTextFromAceEditor(). Is there a common utilities file? - const editor = await testSubjects.find('vega-editor'); - const lines = await editor.findAllByClassName('ace_line_group'); - const linesText = await Bluebird.map(lines, l => l.getVisibleText()); - return linesText.join('\n'); - } - - async getVegaViewContainer() { - return await find.byCssSelector('div.vgaVis__view'); - } - - async getVegaControlContainer() { - return await find.byCssSelector('div.vgaVis__controls'); - } - - async addInputControl(type) { - if (type) { - const selectInput = await testSubjects.find('selectControlType'); - await selectInput.type(type); - } - await testSubjects.click('inputControlEditorAddBtn'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async inputControlSubmit() { - await testSubjects.clickWhenNotDisabled('inputControlSubmitBtn'); - await this.waitForVisualizationRenderingStabilized(); - } - - async inputControlClear() { - await testSubjects.click('inputControlClearBtn'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async isChecked(selector) { - const checkbox = await testSubjects.find(selector); - return await checkbox.isSelected(); - } - - async checkCheckbox(selector) { - const isChecked = await this.isChecked(selector); - if (!isChecked) { - log.debug(`checking checkbox ${selector}`); - await testSubjects.click(selector); - } - } - - async uncheckCheckbox(selector) { - const isChecked = await this.isChecked(selector); - if (isChecked) { - log.debug(`unchecking checkbox ${selector}`); - await testSubjects.click(selector); - } - } - - async isSwitchChecked(selector) { - const checkbox = await testSubjects.find(selector); - const isChecked = await checkbox.getAttribute('aria-checked'); - return isChecked === 'true'; - } - - async checkSwitch(selector) { - const isChecked = await this.isSwitchChecked(selector); - if (!isChecked) { - log.debug(`checking switch ${selector}`); - await testSubjects.click(selector); - } - } - - async uncheckSwitch(selector) { - const isChecked = await this.isSwitchChecked(selector); - if (isChecked) { - log.debug(`unchecking switch ${selector}`); - await testSubjects.click(selector); - } - } - - async setSelectByOptionText(selectId, optionText) { - const selectField = await find.byCssSelector(`#${selectId}`); - const options = await find.allByCssSelector(`#${selectId} > option`); - const $ = await selectField.parseDomContent(); - const optionsText = $('option') - .toArray() - .map(option => $(option).text()); - const optionIndex = optionsText.indexOf(optionText); - - if (optionIndex === -1) { - throw new Error( - `Unable to find option '${optionText}' in select ${selectId}. Available options: ${optionsText.join( - ',' - )}` - ); - } - await options[optionIndex].click(); - } - - async getSideEditorExists() { - return await find.existsByCssSelector('.collapsible-sidebar'); - } - - async setInspectorTablePageSize(size) { - const panel = await testSubjects.find('inspectorPanel'); - await find.clickByButtonText('Rows per page: 20', panel); - // The buttons for setting table page size are in a popover element. This popover - // element appears as if it's part of the inspectorPanel but it's really attached - // to the body element by a portal. - const tableSizesPopover = await find.byCssSelector('.euiPanel'); - await find.clickByButtonText(`${size} rows`, tableSizesPopover); - } - - async getMetric() { - const elements = await find.allByCssSelector( - '[data-test-subj="visualizationLoader"] .mtrVis__container' - ); - const values = await Promise.all( - elements.map(async element => { - const text = await element.getVisibleText(); - return text; - }) - ); - return values - .filter(item => item.length > 0) - .reduce((arr, item) => arr.concat(item.split('\n')), []); - } - - async getGaugeValue() { - const elements = await find.allByCssSelector( - '[data-test-subj="visualizationLoader"] .chart svg text' - ); - const values = await Promise.all( - elements.map(async element => { - const text = await element.getVisibleText(); - return text; - }) - ); - return values.filter(item => item.length > 0); - } - - async clickMetricEditor() { - await find.clickByCssSelector('[group-name="metrics"] .euiAccordion__button'); - } - - async clickMetricByIndex(index) { - log.debug(`clickMetricByIndex(${index})`); - const metrics = await find.allByCssSelector( - '[data-test-subj="visualizationLoader"] .mtrVis .mtrVis__container' - ); - expect(metrics.length).greaterThan(index); - await metrics[index].click(); - } - - async clickNewSearch(indexPattern = this.index.LOGSTASH_TIME_BASED) { - await testSubjects.click(`savedObjectTitle${indexPattern.split(' ').join('-')}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async clickSavedSearch(savedSearchName) { - await testSubjects.click(`savedObjectTitle${savedSearchName.split(' ').join('-')}`); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async clickUnlinkSavedSearch() { - await testSubjects.doubleClick('unlinkSavedSearch'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async setValue(newValue) { - const input = await find.byCssSelector('[data-test-subj="visEditorPercentileRanks"] input'); - await input.clearValue(); - await input.type(newValue); - } - - async selectSearch(searchName) { - await find.clickByLinkText(searchName); - } - - async getErrorMessage() { - const element = await find.byCssSelector('.item>h4'); - return await element.getVisibleText(); - } - - async selectAggregation(myString, groupName = 'buckets', childAggregationType = null) { - const comboBoxElement = await find.byCssSelector(` - [group-name="${groupName}"] - [data-test-subj^="visEditorAggAccordion"].euiAccordion-isOpen - ${childAggregationType ? '.visEditorAgg__subAgg' : ''} - [data-test-subj="defaultEditorAggSelect"] - `); - - await comboBox.setElement(comboBoxElement, myString); - await PageObjects.common.sleep(500); - } - - async applyFilters() { - return await testSubjects.click('filterBarApplyFilters'); - } - /** - * Set the test for a filter aggregation. - * @param {*} filterValue the string value of the filter - * @param {*} filterIndex used when multiple filters are configured on the same aggregation - * @param {*} aggregationId the ID if the aggregation. On Tests, it start at from 2 - */ - async setFilterAggregationValue(filterValue, filterIndex = 0, aggregationId = 2) { - await testSubjects.setValue( - `visEditorFilterInput_${aggregationId}_${filterIndex}`, - filterValue - ); - } - - async addNewFilterAggregation() { - return await testSubjects.click('visEditorAddFilterButton'); - } - - async toggleOpenEditor(index, toState = 'true') { - // index, see selectYAxisAggregation - await this.toggleAccordion(`visEditorAggAccordion${index}`, toState); - } - - async toggleAccordion(id, toState = 'true') { - const toggle = await find.byCssSelector(`button[aria-controls="${id}"]`); - const toggleOpen = await toggle.getAttribute('aria-expanded'); - log.debug(`toggle ${id} expand = ${toggleOpen}`); - if (toggleOpen !== toState) { - log.debug(`toggle ${id} click()`); - await toggle.click(); - } - } - - async toggleAdvancedParams(aggId) { - const accordion = await testSubjects.find(`advancedParams-${aggId}`); - const accordionButton = await find.descendantDisplayedByCssSelector('button', accordion); - await accordionButton.click(); - } - - async selectYAxisAggregation(agg, field, label, index = 1) { - // index starts on the first "count" metric at 1 - // Each new metric or aggregation added to a visualization gets the next index. - // So to modify a metric or aggregation tests need to keep track of the - // order they are added. - await this.toggleOpenEditor(index); - - // select our agg - const aggSelect = await find.byCssSelector( - `#visEditorAggAccordion${index} [data-test-subj="defaultEditorAggSelect"]` - ); - await comboBox.setElement(aggSelect, agg); - - const fieldSelect = await find.byCssSelector( - `#visEditorAggAccordion${index} [data-test-subj="visDefaultEditorField"]` - ); - // select our field - await comboBox.setElement(fieldSelect, field); - // enter custom label - await this.setCustomLabel(label, index); - } - - async setCustomLabel(label, index = 1) { - const customLabel = await testSubjects.find(`visEditorStringInput${index}customLabel`); - customLabel.type(label); - } - - async setAxisExtents(min, max, axisId = 'ValueAxis-1') { - await this.toggleAccordion(`yAxisAccordion${axisId}`); - await this.toggleAccordion(`yAxisOptionsAccordion${axisId}`); - - await testSubjects.click('yAxisSetYExtents'); - await testSubjects.setValue('yAxisYExtentsMax', max); - await testSubjects.setValue('yAxisYExtentsMin', min); - } - - async getField() { - return await comboBox.getComboBoxSelectedOptions('visDefaultEditorField'); - } - - async selectField(fieldValue, groupName = 'buckets', childAggregationType = null) { - log.debug(`selectField ${fieldValue}`); - const selector = ` - [group-name="${groupName}"] - [data-test-subj^="visEditorAggAccordion"].euiAccordion-isOpen - [data-test-subj="visAggEditorParams"] - ${childAggregationType ? '.visEditorAgg__subAgg' : ''} - [data-test-subj="visDefaultEditorField"] - `; - const fieldEl = await find.byCssSelector(selector); - await comboBox.setElement(fieldEl, fieldValue); - } - - async selectAggregateWith(fieldValue) { - await testSubjects.selectValue('visDefaultEditorAggregateWith', fieldValue); - } - - async getInterval() { - return await comboBox.getComboBoxSelectedOptions('visEditorInterval'); - } - - async setInterval(newValue) { - log.debug(`Visualize.setInterval(${newValue})`); - return await comboBox.set('visEditorInterval', newValue); - } - - async setCustomInterval(newValue) { - log.debug(`Visualize.setCustomInterval(${newValue})`); - return await comboBox.setCustom('visEditorInterval', newValue); - } - - async getNumericInterval(agg = 2) { - return await testSubjects.getAttribute(`visEditorInterval${agg}`, 'value'); - } - - async setNumericInterval(newValue, { append } = {}, agg = 2) { - if (append) { - await testSubjects.append(`visEditorInterval${agg}`, String(newValue)); - } else { - await testSubjects.setValue(`visEditorInterval${agg}`, String(newValue)); - } - } - - async setSize(newValue, aggId) { - const dataTestSubj = aggId - ? `visEditorAggAccordion${aggId} > sizeParamEditor` - : 'sizeParamEditor'; - await testSubjects.setValue(dataTestSubj, String(newValue)); - } - - async toggleDisabledAgg(agg) { - await testSubjects.click(`visEditorAggAccordion${agg} > ~toggleDisableAggregationBtn`); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async toggleAggregationEditor(agg) { - await find.clickByCssSelector( - `[data-test-subj="visEditorAggAccordion${agg}"] .euiAccordion__button` - ); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async toggleOtherBucket(agg = 2) { - return await testSubjects.click(`visEditorAggAccordion${agg} > otherBucketSwitch`); - } - - async toggleMissingBucket(agg = 2) { - return await testSubjects.click(`visEditorAggAccordion${agg} > missingBucketSwitch`); - } - - async toggleScaleMetrics() { - return await testSubjects.click('scaleMetricsSwitch'); - } - - async isApplyEnabled() { - const applyButton = await testSubjects.find('visualizeEditorRenderButton'); - return await applyButton.isEnabled(); - } - - async clickGo() { - const prevRenderingCount = await this.getVisualizationRenderingCount(); - log.debug(`Before Rendering count ${prevRenderingCount}`); - await testSubjects.clickWhenNotDisabled('visualizeEditorRenderButton'); - await this.waitForRenderingCount(prevRenderingCount + 1); - } - - async clickReset() { - await testSubjects.click('visualizeEditorResetButton'); - await this.waitForVisualization(); - } - - async toggleAutoMode() { - await testSubjects.click('visualizeEditorAutoButton'); - } - - async sizeUpEditor() { - await testSubjects.click('visualizeEditorResizer'); - await browser.pressKeys(browser.keys.ARROW_RIGHT); - } - - async clickOptions() { - await find.clickByPartialLinkText('Options'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async changeHeatmapColorNumbers(value = 6) { - const input = await testSubjects.find(`heatmapColorsNumber`); - await input.clearValueWithKeyboard(); - await input.type(`${value}`); - } - - async clickMetricsAndAxes() { - await testSubjects.click('visEditorTabadvanced'); - } - - async clickOptionsTab() { - await testSubjects.click('visEditorTaboptions'); - } - - async clickEnableCustomRanges() { - await testSubjects.click('heatmapUseCustomRanges'); - } - - async clickAddRange() { - await testSubjects.click(`heatmapColorRange__addRangeButton`); - } - - async setCustomRangeByIndex(index, from, to) { - await testSubjects.setValue(`heatmapColorRange${index}__from`, from); - await testSubjects.setValue(`heatmapColorRange${index}__to`, to); - } - - async clickYAxisOptions(axisId) { - await testSubjects.click(`toggleYAxisOptions-${axisId}`); - } - - async clickYAxisAdvancedOptions(axisId) { - await testSubjects.click(`toggleYAxisAdvancedOptions-${axisId}`); - } - - async changeYAxisFilterLabelsCheckbox(axisId, enabled) { - const selector = `yAxisFilterLabelsCheckbox-${axisId}`; - enabled ? await this.checkCheckbox(selector) : await this.uncheckCheckbox(selector); - } - - async selectChartMode(mode) { - const selector = await find.byCssSelector(`#seriesMode0 > option[value="${mode}"]`); - await selector.click(); - } - - async selectYAxisScaleType(axisId, scaleType) { - const selectElement = await testSubjects.find(`scaleSelectYAxis-${axisId}`); - const selector = await selectElement.findByCssSelector(`option[value="${scaleType}"]`); - await selector.click(); - } - - async selectYAxisMode(mode) { - const selector = await find.byCssSelector(`#valueAxisMode0 > option[value="${mode}"]`); - await selector.click(); - } - - async clickData() { - await testSubjects.click('visualizeEditDataLink'); - } - - async clickVisEditorTab(tabName) { - await testSubjects.click('visEditorTab' + tabName); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async selectWMS() { - await find.clickByCssSelector('input[name="wms.enabled"]'); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async ensureSavePanelOpen() { - log.debug('ensureSavePanelOpen'); - await PageObjects.header.waitUntilLoadingHasFinished(); - const isOpen = await testSubjects.exists('savedObjectSaveModal', { timeout: 5000 }); - if (!isOpen) { - await testSubjects.click('visualizeSaveButton'); - } - } - - async saveVisualization(vizName, { saveAsNew = false } = {}) { - await this.ensureSavePanelOpen(); - await testSubjects.setValue('savedObjectTitle', vizName); - if (saveAsNew) { - log.debug('Check save as new visualization'); - await testSubjects.click('saveAsNewCheckbox'); - } - log.debug('Click Save Visualization button'); - - await testSubjects.click('confirmSaveSavedObjectButton'); - - // Confirm that the Visualization has actually been saved - await testSubjects.existOrFail('saveVisualizationSuccess'); - const message = await PageObjects.common.closeToast(); - await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.common.waitForSaveModalToClose(); - - return message; - } - - async saveVisualizationExpectSuccess(vizName, { saveAsNew = false } = {}) { - const saveMessage = await this.saveVisualization(vizName, { saveAsNew }); - if (!saveMessage) { - throw new Error( - `Expected saveVisualization to respond with the saveMessage from the toast, got ${saveMessage}` - ); - } - } - - async saveVisualizationExpectSuccessAndBreadcrumb(vizName, { saveAsNew = false } = {}) { - await this.saveVisualizationExpectSuccess(vizName, { saveAsNew }); - await retry.waitFor( - 'last breadcrumb to have new vis name', - async () => (await globalNav.getLastBreadcrumb()) === vizName - ); - } - - async clickLoadSavedVisButton() { - // TODO: Use a test subject selector once we rewrite breadcrumbs to accept each breadcrumb - // element as a child instead of building the breadcrumbs dynamically. - await find.clickByCssSelector('[href="#/visualize"]'); - } - - async filterVisByName(vizName) { - const input = await find.byCssSelector('input[name="filter"]'); - await input.click(); - // can't uses dashes in saved visualizations when filtering - // or extended character sets - // https://github.com/elastic/kibana/issues/6300 - await input.type(vizName.replace('-', ' ')); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async clickVisualizationByName(vizName) { - log.debug('clickVisualizationByLinkText(' + vizName + ')'); - return find.clickByPartialLinkText(vizName); - } - - async loadSavedVisualization(vizName, { navigateToVisualize = true } = {}) { - if (navigateToVisualize) { - await this.clickLoadSavedVisButton(); - } - await this.openSavedVisualization(vizName); - } - - async openSavedVisualization(vizName) { - await this.clickVisualizationByName(vizName); - await PageObjects.header.waitUntilLoadingHasFinished(); - } - - async getXAxisLabels() { - const xAxis = await find.byCssSelector('.visAxis--x.visAxis__column--bottom'); - const $ = await xAxis.parseDomContent(); - return $('.x > g > text') - .toArray() - .map(tick => - $(tick) - .text() - .trim() - ); - } - - async getYAxisLabels() { - const yAxis = await find.byCssSelector('.visAxis__column--y.visAxis__column--left'); - const $ = await yAxis.parseDomContent(); - return $('.y > g > text') - .toArray() - .map(tick => - $(tick) - .text() - .trim() - ); - } - - /** - * Removes chrome and takes a small screenshot of a vis to compare against a baseline. - * @param {string} name The name of the baseline image. - * @param {object} opts Options object. - * @param {number} opts.threshold Threshold for allowed variance when comparing images. - */ - async expectVisToMatchScreenshot(name, opts = { threshold: 0.05 }) { - log.debug(`expectVisToMatchScreenshot(${name})`); - - // Collapse sidebar and inject some CSS to hide the nav so we have a focused screenshot - await this.clickEditorSidebarCollapse(); - await this.waitForVisualizationRenderingStabilized(); - await browser.execute(` - var el = document.createElement('style'); - el.id = '__data-test-style'; - el.innerHTML = '[data-test-subj="headerGlobalNav"] { display: none; } '; - el.innerHTML += '[data-test-subj="top-nav"] { display: none; } '; - el.innerHTML += '[data-test-subj="experimentalVisInfo"] { display: none; } '; - document.body.appendChild(el); - `); - - const percentDifference = await screenshot.compareAgainstBaseline(name, updateBaselines); - - // Reset the chart to its original state - await browser.execute(` - var el = document.getElementById('__data-test-style'); - document.body.removeChild(el); - `); - await this.clickEditorSidebarCollapse(); - await this.waitForVisualizationRenderingStabilized(); - expect(percentDifference).to.be.lessThan(opts.threshold); - } - - /* - ** This method gets the chart data and scales it based on chart height and label. - ** Returns an array of height values - */ - async getAreaChartData(dataLabel, axis = 'ValueAxis-1') { - const yAxisRatio = await this.getChartYAxisRatio(axis); - - const rectangle = await find.byCssSelector('rect.background'); - const yAxisHeight = await rectangle.getAttribute('height'); - log.debug(`height --------- ${yAxisHeight}`); - - const path = await retry.try( - async () => - await find.byCssSelector(`path[data-label="${dataLabel}"]`, defaultFindTimeout * 2) - ); - const data = await path.getAttribute('d'); - log.debug(data); - // This area chart data starts with a 'M'ove to a x,y location, followed - // by a bunch of 'L'ines from that point to the next. Those points are - // the values we're going to use to calculate the data values we're testing. - // So git rid of the one 'M' and split the rest on the 'L's. - const tempArray = data - .replace('M ', '') - .replace('M', '') - .replace(/ L /g, 'L') - .replace(/ /g, ',') - .split('L'); - const chartSections = tempArray.length / 2; - // log.debug('chartSections = ' + chartSections + ' height = ' + yAxisHeight + ' yAxisLabel = ' + yAxisLabel); - const chartData = []; - for (let i = 0; i < chartSections; i++) { - chartData[i] = Math.round((yAxisHeight - tempArray[i].split(',')[1]) * yAxisRatio); - log.debug('chartData[i] =' + chartData[i]); - } - return chartData; - } - - /* - ** This method returns the paths that compose an area chart. - */ - async getAreaChartPaths(dataLabel) { - const path = await retry.try( - async () => - await find.byCssSelector(`path[data-label="${dataLabel}"]`, defaultFindTimeout * 2) - ); - const data = await path.getAttribute('d'); - log.debug(data); - // This area chart data starts with a 'M'ove to a x,y location, followed - // by a bunch of 'L'ines from that point to the next. Those points are - // the values we're going to use to calculate the data values we're testing. - // So git rid of the one 'M' and split the rest on the 'L's. - return data.split('L'); - } - - // The current test shows dots, not a line. This function gets the dots and normalizes their height. - async getLineChartData(dataLabel = 'Count', axis = 'ValueAxis-1') { - // 1). get the range/pixel ratio - const yAxisRatio = await this.getChartYAxisRatio(axis); - // 2). find and save the y-axis pixel size (the chart height) - const rectangle = await find.byCssSelector('clipPath rect'); - const yAxisHeight = await rectangle.getAttribute('height'); - // 3). get the visWrapper__chart elements - const chartTypes = await retry.try( - async () => - await find.allByCssSelector( - `.visWrapper__chart circle[data-label="${dataLabel}"][fill-opacity="1"]`, - defaultFindTimeout * 2 - ) - ); - // 4). for each chart element, find the green circle, then the cy position - const chartData = await Promise.all( - chartTypes.map(async chart => { - const cy = await chart.getAttribute('cy'); - // the point_series_options test has data in the billions range and - // getting 11 digits of precision with these calculations is very hard - return Math.round(((yAxisHeight - cy) * yAxisRatio).toPrecision(6)); - }) - ); - - return chartData; - } - - // this is ALMOST identical to DiscoverPage.getBarChartData - async getBarChartData(dataLabel = 'Count', axis = 'ValueAxis-1') { - // 1). get the range/pixel ratio - const yAxisRatio = await this.getChartYAxisRatio(axis); - // 3). get the visWrapper__chart elements - const svg = await find.byCssSelector('div.chart'); - const $ = await svg.parseDomContent(); - const chartData = $(`g > g.series > rect[data-label="${dataLabel}"]`) - .toArray() - .map(chart => { - const barHeight = $(chart).attr('height'); - return Math.round(barHeight * yAxisRatio); - }); - - return chartData; - } - - // Returns value per pixel - async getChartYAxisRatio(axis = 'ValueAxis-1') { - // 1). get the maximum chart Y-Axis marker value and Y position - const maxYAxisChartMarker = await retry.try( - async () => - await find.byCssSelector( - `div.visAxis__splitAxes--y > div > svg > g.${axis} > g:last-of-type.tick` - ) - ); - const maxYLabel = (await maxYAxisChartMarker.getVisibleText()).replace(/,/g, ''); - const maxYLabelYPosition = (await maxYAxisChartMarker.getPosition()).y; - log.debug(`maxYLabel = ${maxYLabel}, maxYLabelYPosition = ${maxYLabelYPosition}`); - - // 2). get the minimum chart Y-Axis marker value and Y position - const minYAxisChartMarker = await find.byCssSelector( - 'div.visAxis__column--y.visAxis__column--left > div > div > svg:nth-child(2) > g > g:nth-child(1).tick' - ); - const minYLabel = (await minYAxisChartMarker.getVisibleText()).replace(',', ''); - const minYLabelYPosition = (await minYAxisChartMarker.getPosition()).y; - return (maxYLabel - minYLabel) / (minYLabelYPosition - maxYLabelYPosition); - } - - async getHeatmapData() { - const chartTypes = await retry.try( - async () => await find.allByCssSelector('svg > g > g.series rect', defaultFindTimeout * 2) - ); - log.debug('rects=' + chartTypes); - async function getChartType(chart) { - return await chart.getAttribute('data-label'); - } - const getChartTypesPromises = chartTypes.map(getChartType); - return await Promise.all(getChartTypesPromises); - } - - async expectError() { - return await testSubjects.existOrFail('visLibVisualizeError'); - } - - async getChartAreaWidth() { - const rect = await retry.try(async () => find.byCssSelector('clipPath rect')); - return await rect.getAttribute('width'); - } - - async getChartAreaHeight() { - const rect = await retry.try(async () => find.byCssSelector('clipPath rect')); - return await rect.getAttribute('height'); - } - - /** - * If you are writing new tests, you should rather look into getTableVisContent method instead. - */ - async getTableVisData() { - return await testSubjects.getVisibleText('paginated-table-body'); - } - - /** - * This function is the newer function to retrieve data from within a table visualization. - * It uses a better return format, than the old getTableVisData, by properly splitting - * cell values into arrays. Please use this function for newer tests. - */ - async getTableVisContent({ stripEmptyRows = true } = {}) { - return await retry.try(async () => { - const container = await testSubjects.find('tableVis'); - const allTables = await testSubjects.findAllDescendant('paginated-table-body', container); - - if (allTables.length === 0) { - return []; - } - - const allData = await Promise.all( - allTables.map(async t => { - let data = await table.getDataFromElement(t); - if (stripEmptyRows) { - data = data.filter(row => row.length > 0 && row.some(cell => cell.trim().length > 0)); - } - return data; - }) - ); - - if (allTables.length === 1) { - // If there was only one table we return only the data for that table - // This prevents an unnecessary array around that single table, which - // is the case we have in most tests. - return allData[0]; - } - - return allData; - }); - } - - async toggleIsFilteredByCollarCheckbox() { - await testSubjects.click('isFilteredByCollarCheckbox'); - } - - async setIsFilteredByCollarCheckbox(value = true) { - await retry.try(async () => { - const isChecked = await this.isSwitchChecked('isFilteredByCollarCheckbox'); - if (isChecked !== value) { - await testSubjects.click('isFilteredByCollarCheckbox'); - throw new Error('isFilteredByCollar not set correctly'); - } - }); - } - - async getMarkdownData() { - const markdown = await retry.try(async () => find.byCssSelector('visualize')); - return await markdown.getVisibleText(); - } - - async getVisualizationRenderingCount() { - const visualizationLoader = await testSubjects.find('visualizationLoader'); - const renderingCount = await visualizationLoader.getAttribute('data-rendering-count'); - return Number(renderingCount); - } - - async waitForRenderingCount(minimumCount = 1) { - await retry.waitFor( - `rendering count to be greater than or equal to [${minimumCount}]`, - async () => { - const currentRenderingCount = await this.getVisualizationRenderingCount(); - log.debug(`-- currentRenderingCount=${currentRenderingCount}`); - return currentRenderingCount >= minimumCount; - } - ); - } - - async waitForVisualizationRenderingStabilized() { - //assuming rendering is done when data-rendering-count is constant within 1000 ms - await retry.waitFor('rendering count to stabilize', async () => { - const firstCount = await this.getVisualizationRenderingCount(); - log.debug(`-- firstCount=${firstCount}`); - - await PageObjects.common.sleep(1000); - - const secondCount = await this.getVisualizationRenderingCount(); - log.debug(`-- secondCount=${secondCount}`); - - return firstCount === secondCount; - }); - } - - async waitForVisualization() { - await this.waitForVisualizationRenderingStabilized(); - return await find.byCssSelector('.visualization'); - } - - async waitForVisualizationSavedToastGone() { - return await testSubjects.waitForDeleted('saveVisualizationSuccess'); - } - - async getZoomSelectors(zoomSelector) { - return await find.allByCssSelector(zoomSelector); - } - - async clickMapButton(zoomSelector, waitForLoading) { - await retry.try(async () => { - const zooms = await this.getZoomSelectors(zoomSelector); - await Promise.all(zooms.map(async zoom => await zoom.click())); - if (waitForLoading) { - await PageObjects.header.waitUntilLoadingHasFinished(); - } - }); - } - - async getVisualizationRequest() { - log.debug('getVisualizationRequest'); - await inspector.open(); - await testSubjects.click('inspectorViewChooser'); - await testSubjects.click('inspectorViewChooserRequests'); - await testSubjects.click('inspectorRequestDetailRequest'); - return await testSubjects.getVisibleText('inspectorRequestBody'); - } - - async getVisualizationResponse() { - log.debug('getVisualizationResponse'); - await inspector.open(); - await testSubjects.click('inspectorViewChooser'); - await testSubjects.click('inspectorViewChooserRequests'); - await testSubjects.click('inspectorRequestDetailResponse'); - return await testSubjects.getVisibleText('inspectorResponseBody'); - } - - async getMapBounds() { - const request = await this.getVisualizationRequest(); - const requestObject = JSON.parse(request); - return requestObject.aggs.filter_agg.filter.geo_bounding_box['geo.coordinates']; - } - - async clickMapZoomIn(waitForLoading = true) { - await this.clickMapButton('a.leaflet-control-zoom-in', waitForLoading); - } - - async clickMapZoomOut(waitForLoading = true) { - await this.clickMapButton('a.leaflet-control-zoom-out', waitForLoading); - } - - async getMapZoomEnabled(zoomSelector) { - const zooms = await this.getZoomSelectors(zoomSelector); - const classAttributes = await Promise.all( - zooms.map(async zoom => await zoom.getAttribute('class')) - ); - return !classAttributes.join('').includes('leaflet-disabled'); - } - - async zoomAllTheWayOut() { - // we can tell we're at level 1 because zoom out is disabled - return await retry.try(async () => { - await this.clickMapZoomOut(); - const enabled = await this.getMapZoomOutEnabled(); - //should be able to zoom more as current config has 0 as min level. - if (enabled) { - throw new Error('Not fully zoomed out yet'); - } - }); - } - - async getMapZoomInEnabled() { - return await this.getMapZoomEnabled('a.leaflet-control-zoom-in'); - } - - async getMapZoomOutEnabled() { - return await this.getMapZoomEnabled('a.leaflet-control-zoom-out'); - } - - async clickMapFitDataBounds() { - return await this.clickMapButton('a.fa-crop'); - } - - async clickLandingPageBreadcrumbLink() { - log.debug('clickLandingPageBreadcrumbLink'); - await find.clickByCssSelector(`a[href="#${VisualizeConstants.LANDING_PAGE_PATH}"]`); - } - - /** - * Returns true if already on the landing page (that page doesn't have a link to itself). - * @returns {Promise} - */ - async onLandingPage() { - log.debug(`VisualizePage.onLandingPage`); - const exists = await testSubjects.exists('visualizeLandingPage'); - return exists; - } - - async gotoLandingPage() { - log.debug('VisualizePage.gotoLandingPage'); - const onPage = await this.onLandingPage(); - if (!onPage) { - await retry.try(async () => { - await this.clickLandingPageBreadcrumbLink(); - const onLandingPage = await this.onLandingPage(); - if (!onLandingPage) throw new Error('Not on the landing page.'); - }); - } - } - - async getLegendEntries() { - const legendEntries = await find.allByCssSelector( - '.visLegend__button', - defaultFindTimeout * 2 - ); - return await Promise.all( - legendEntries.map(async chart => await chart.getAttribute('data-label')) - ); - } - - async openLegendOptionColors(name) { - await this.waitForVisualizationRenderingStabilized(); - await retry.try(async () => { - // This click has been flaky in opening the legend, hence the retry. See - // https://github.com/elastic/kibana/issues/17468 - await testSubjects.click(`legend-${name}`); - await this.waitForVisualizationRenderingStabilized(); - // arbitrary color chosen, any available would do - const isOpen = await this.doesLegendColorChoiceExist('#EF843C'); - if (!isOpen) { - throw new Error('legend color selector not open'); - } - }); - } - - async filterOnTableCell(column, row) { - await retry.try(async () => { - const table = await testSubjects.find('tableVis'); - const cell = await table.findByCssSelector( - `tbody tr:nth-child(${row}) td:nth-child(${column})` - ); - await cell.moveMouseTo(); - const filterBtn = await testSubjects.findDescendant('filterForCellValue', cell); - await filterBtn.click(); - }); - } - - async toggleLegend(show = true) { - await retry.try(async () => { - const isVisible = find.byCssSelector('.visLegend'); - if ((show && !isVisible) || (!show && isVisible)) { - await testSubjects.click('vislibToggleLegend'); - } - }); - } - - async filterLegend(name) { - await this.toggleLegend(); - await testSubjects.click(`legend-${name}`); - const filters = await testSubjects.find(`legend-${name}-filters`); - const [filterIn] = await filters.findAllByCssSelector(`input`); - await filterIn.click(); - await this.waitForVisualizationRenderingStabilized(); - } - - async doesLegendColorChoiceExist(color) { - return await testSubjects.exists(`legendSelectColor-${color}`); - } - - async selectNewLegendColorChoice(color) { - await testSubjects.click(`legendSelectColor-${color}`); - } - - async doesSelectedLegendColorExist(color) { - return await testSubjects.exists(`legendSelectedColor-${color}`); - } - - async getYAxisTitle() { - const title = await find.byCssSelector('.y-axis-div .y-axis-title text'); - return await title.getVisibleText(); - } - - async selectBucketType(type) { - const bucketType = await find.byCssSelector(`[data-test-subj="${type}"]`); - return await bucketType.click(); - } - - async getBucketErrorMessage() { - const error = await find.byCssSelector( - '[group-name="buckets"] [data-test-subj="defaultEditorAggSelect"] + .euiFormErrorText' - ); - const errorMessage = await error.getAttribute('innerText'); - log.debug(errorMessage); - return errorMessage; - } - - async selectOrderByMetric(agg, metric) { - const sortSelect = await testSubjects.find(`visEditorOrderBy${agg}`); - const sortMetric = await sortSelect.findByCssSelector(`option[value="${metric}"]`); - await sortMetric.click(); - } - - async selectCustomSortMetric(agg, metric, field) { - await this.selectOrderByMetric(agg, 'custom'); - await this.selectAggregation(metric, 'buckets', true); - await this.selectField(field, 'buckets', true); - } - - async clickSplitDirection(direction) { - const control = await testSubjects.find('visEditorSplitBy'); - const radioBtn = await control.findByCssSelector(`[title="${direction}"]`); - await radioBtn.click(); - } - - async countNestedTables() { - const vis = await testSubjects.find('tableVis'); - const result = []; - - for (let i = 1; true; i++) { - const selector = new Array(i).fill('.kbnAggTable__group').join(' '); - const tables = await vis.findAllByCssSelector(selector); - if (tables.length === 0) { - break; - } - result.push(tables.length); - } - - return result; - } - - async removeDimension(agg) { - await testSubjects.click(`visEditorAggAccordion${agg} > removeDimensionBtn`); - } - - async setFilterParams({ aggNth = 0, indexPattern, field }) { - await comboBox.set(`indexPatternSelect-${aggNth}`, indexPattern); - await comboBox.set(`fieldSelect-${aggNth}`, field); - } - - async setFilterRange({ aggNth = 0, min, max }) { - const control = await testSubjects.find(`inputControl${aggNth}`); - const inputMin = await control.findByCssSelector('[name$="minValue"]'); - await inputMin.type(min); - const inputMax = await control.findByCssSelector('[name$="maxValue"]'); - await inputMax.type(max); - } - - async scrollSubjectIntoView(subject) { - const element = await testSubjects.find(subject); - await element.scrollIntoViewIfNecessary(); - } - } - - return new VisualizePage(); -} diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts new file mode 100644 index 00000000000000..1562cf9745f2de --- /dev/null +++ b/test/functional/page_objects/visualize_page.ts @@ -0,0 +1,328 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; +import { VisualizeConstants } from '../../../src/legacy/core_plugins/kibana/public/visualize/np_ready/visualize_constants'; + +export function VisualizePageProvider({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const find = getService('find'); + const log = getService('log'); + const globalNav = getService('globalNav'); + const listingTable = getService('listingTable'); + const { common, header, visEditor } = getPageObjects(['common', 'header', 'visEditor']); + + /** + * This page object contains the visualization type selection, the landing page, + * and the open/save dialog functions + */ + class VisualizePage { + index = { + LOGSTASH_TIME_BASED: 'logstash-*', + LOGSTASH_NON_TIME_BASED: 'logstash*', + }; + + public async gotoVisualizationLandingPage() { + await common.navigateToApp('visualize'); + } + + public async clickNewVisualization() { + // newItemButton button is only visible when there are items in the listing table is displayed. + let exists = await testSubjects.exists('newItemButton'); + if (exists) { + return await testSubjects.click('newItemButton'); + } + + exists = await testSubjects.exists('createVisualizationPromptButton'); + // no viz exist, click createVisualizationPromptButton to create new dashboard + return await this.createVisualizationPromptButton(); + } + + public async createVisualizationPromptButton() { + await testSubjects.click('createVisualizationPromptButton'); + } + + public async getChartTypes() { + const chartTypeField = await testSubjects.find('visNewDialogTypes'); + const $ = await chartTypeField.parseDomContent(); + return $('button') + .toArray() + .map(chart => + $(chart) + .findTestSubject('visTypeTitle') + .text() + .trim() + ); + } + + public async waitForVisualizationSelectPage() { + await retry.try(async () => { + const visualizeSelectTypePage = await testSubjects.find('visNewDialogTypes'); + if (!(await visualizeSelectTypePage.isDisplayed())) { + throw new Error('wait for visualization select page'); + } + }); + } + + public async navigateToNewVisualization() { + await common.navigateToApp('visualize'); + await this.clickNewVisualization(); + await this.waitForVisualizationSelectPage(); + } + + public async clickVisType(type: string) { + await testSubjects.click(`visType-${type}`); + await header.waitUntilLoadingHasFinished(); + } + + public async clickAreaChart() { + await this.clickVisType('area'); + } + + public async clickDataTable() { + await this.clickVisType('table'); + } + + public async clickLineChart() { + await this.clickVisType('line'); + } + + public async clickRegionMap() { + await this.clickVisType('region_map'); + } + + public async clickMarkdownWidget() { + await this.clickVisType('markdown'); + } + + public async clickMetric() { + await this.clickVisType('metric'); + } + + public async clickGauge() { + await this.clickVisType('gauge'); + } + + public async clickPieChart() { + await this.clickVisType('pie'); + } + + public async clickTileMap() { + await this.clickVisType('tile_map'); + } + + public async clickTagCloud() { + await this.clickVisType('tagcloud'); + } + + public async clickVega() { + await this.clickVisType('vega'); + } + + public async clickVisualBuilder() { + await this.clickVisType('metrics'); + } + + public async clickVerticalBarChart() { + await this.clickVisType('histogram'); + } + + public async clickHeatmapChart() { + await this.clickVisType('heatmap'); + } + + public async clickInputControlVis() { + await this.clickVisType('input_control_vis'); + } + + public async createSimpleMarkdownViz(vizName: string) { + await this.gotoVisualizationLandingPage(); + await this.navigateToNewVisualization(); + await this.clickMarkdownWidget(); + await visEditor.setMarkdownTxt(vizName); + await visEditor.clickGo(); + await this.saveVisualization(vizName); + } + + public async clickNewSearch(indexPattern = this.index.LOGSTASH_TIME_BASED) { + await testSubjects.click(`savedObjectTitle${indexPattern.split(' ').join('-')}`); + await header.waitUntilLoadingHasFinished(); + } + + public async selectVisSourceIfRequired() { + log.debug('selectVisSourceIfRequired'); + const selectPage = await testSubjects.findAll('visualizeSelectSearch'); + if (selectPage.length) { + log.debug('a search is required for this visualization'); + await this.clickNewSearch(); + } + } + + /** + * Deletes all existing visualizations + */ + public async deleteAllVisualizations() { + await retry.try(async () => { + await listingTable.checkListingSelectAllCheckbox(); + await listingTable.clickDeleteSelected(); + await common.clickConfirmOnModal(); + await testSubjects.find('createVisualizationPromptButton'); + }); + } + + public async isBetaInfoShown() { + return await testSubjects.exists('betaVisInfo'); + } + + public async getBetaTypeLinks() { + return await find.allByCssSelector('[data-vis-stage="beta"]'); + } + + public async getExperimentalTypeLinks() { + return await find.allByCssSelector('[data-vis-stage="experimental"]'); + } + + public async isExperimentalInfoShown() { + return await testSubjects.exists('experimentalVisInfo'); + } + + public async getExperimentalInfo() { + return await testSubjects.find('experimentalVisInfo'); + } + + public async getSideEditorExists() { + return await find.existsByCssSelector('.collapsible-sidebar'); + } + + public async clickSavedSearch(savedSearchName: string) { + await testSubjects.click(`savedObjectTitle${savedSearchName.split(' ').join('-')}`); + await header.waitUntilLoadingHasFinished(); + } + + public async clickUnlinkSavedSearch() { + await testSubjects.doubleClick('unlinkSavedSearch'); + await header.waitUntilLoadingHasFinished(); + } + + public async ensureSavePanelOpen() { + log.debug('ensureSavePanelOpen'); + await header.waitUntilLoadingHasFinished(); + const isOpen = await testSubjects.exists('savedObjectSaveModal', { timeout: 5000 }); + if (!isOpen) { + await testSubjects.click('visualizeSaveButton'); + } + } + + public async clickLoadSavedVisButton() { + // TODO: Use a test subject selector once we rewrite breadcrumbs to accept each breadcrumb + // element as a child instead of building the breadcrumbs dynamically. + await find.clickByCssSelector('[href="#/visualize"]'); + } + + public async clickVisualizationByName(vizName: string) { + log.debug('clickVisualizationByLinkText(' + vizName + ')'); + await find.clickByPartialLinkText(vizName); + } + + public async loadSavedVisualization(vizName: string, { navigateToVisualize = true } = {}) { + if (navigateToVisualize) { + await this.clickLoadSavedVisButton(); + } + await this.openSavedVisualization(vizName); + } + + public async openSavedVisualization(vizName: string) { + await this.clickVisualizationByName(vizName); + await header.waitUntilLoadingHasFinished(); + } + + public async waitForVisualizationSavedToastGone() { + await testSubjects.waitForDeleted('saveVisualizationSuccess'); + } + + public async clickLandingPageBreadcrumbLink() { + log.debug('clickLandingPageBreadcrumbLink'); + await find.clickByCssSelector(`a[href="#${VisualizeConstants.LANDING_PAGE_PATH}"]`); + } + + /** + * Returns true if already on the landing page (that page doesn't have a link to itself). + * @returns {Promise} + */ + public async onLandingPage() { + log.debug(`VisualizePage.onLandingPage`); + return await testSubjects.exists('visualizeLandingPage'); + } + + public async gotoLandingPage() { + log.debug('VisualizePage.gotoLandingPage'); + const onPage = await this.onLandingPage(); + if (!onPage) { + await retry.try(async () => { + await this.clickLandingPageBreadcrumbLink(); + const onLandingPage = await this.onLandingPage(); + if (!onLandingPage) throw new Error('Not on the landing page.'); + }); + } + } + + public async saveVisualization(vizName: string, { saveAsNew = false } = {}) { + await this.ensureSavePanelOpen(); + await testSubjects.setValue('savedObjectTitle', vizName); + if (saveAsNew) { + log.debug('Check save as new visualization'); + await testSubjects.click('saveAsNewCheckbox'); + } + log.debug('Click Save Visualization button'); + + await testSubjects.click('confirmSaveSavedObjectButton'); + + // Confirm that the Visualization has actually been saved + await testSubjects.existOrFail('saveVisualizationSuccess'); + const message = await common.closeToast(); + await header.waitUntilLoadingHasFinished(); + await common.waitForSaveModalToClose(); + + return message; + } + + public async saveVisualizationExpectSuccess(vizName: string, { saveAsNew = false } = {}) { + const saveMessage = await this.saveVisualization(vizName, { saveAsNew }); + if (!saveMessage) { + throw new Error( + `Expected saveVisualization to respond with the saveMessage from the toast, got ${saveMessage}` + ); + } + } + + public async saveVisualizationExpectSuccessAndBreadcrumb( + vizName: string, + { saveAsNew = false } = {} + ) { + await this.saveVisualizationExpectSuccess(vizName, { saveAsNew }); + await retry.waitFor( + 'last breadcrumb to have new vis name', + async () => (await globalNav.getLastBreadcrumb()) === vizName + ); + } + } + + return new VisualizePage(); +} diff --git a/test/functional/services/dashboard/visualizations.js b/test/functional/services/dashboard/visualizations.js index c4e7fe6ad3bd9f..5e722ccce89701 100644 --- a/test/functional/services/dashboard/visualizations.js +++ b/test/functional/services/dashboard/visualizations.js @@ -24,7 +24,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { const queryBar = getService('queryBar'); const testSubjects = getService('testSubjects'); const dashboardAddPanel = getService('dashboardAddPanel'); - const PageObjects = getPageObjects(['dashboard', 'visualize', 'header', 'discover']); + const PageObjects = getPageObjects(['dashboard', 'visualize', 'visEditor', 'header', 'discover']); return new (class DashboardVisualizations { async createAndAddTSVBVisualization(name) { @@ -107,8 +107,8 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }) { } await this.ensureNewVisualizationDialogIsShowing(); await PageObjects.visualize.clickMarkdownWidget(); - await PageObjects.visualize.setMarkdownTxt(markdown); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.setMarkdownTxt(markdown); + await PageObjects.visEditor.clickGo(); await PageObjects.visualize.saveVisualizationExpectSuccess(name); } })(); diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index ea47ccf1d2704c..a10bb013b3af48 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -49,7 +49,7 @@ import { TestSubjectsProvider } from './test_subjects'; import { ToastsProvider } from './toasts'; // @ts-ignore not TS yet import { PieChartProvider } from './visualizations'; -import { VisualizeListingTableProvider } from './visualize_listing_table'; +import { ListingTableProvider } from './listing_table'; import { SavedQueryManagementComponentProvider } from './saved_query_management_component'; export const services = { @@ -66,7 +66,7 @@ export const services = { dashboardVisualizations: DashboardVisualizationProvider, dashboardExpect: DashboardExpectProvider, failureDebugging: FailureDebuggingProvider, - visualizeListingTable: VisualizeListingTableProvider, + listingTable: ListingTableProvider, dashboardAddPanel: DashboardAddPanelProvider, dashboardReplacePanel: DashboardReplacePanelProvider, dashboardPanelActions: DashboardPanelActionsProvider, diff --git a/test/functional/services/visualize_listing_table.ts b/test/functional/services/listing_table.ts similarity index 50% rename from test/functional/services/visualize_listing_table.ts rename to test/functional/services/listing_table.ts index 8c4640ada1c059..ec886cf694f2e4 100644 --- a/test/functional/services/visualize_listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -19,13 +19,25 @@ import { FtrProviderContext } from '../ftr_provider_context'; -export function VisualizeListingTableProvider({ getService, getPageObjects }: FtrProviderContext) { +export function ListingTableProvider({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const find = getService('find'); const log = getService('log'); - const { header } = getPageObjects(['header']); + const retry = getService('retry'); + const { common, header } = getPageObjects(['common', 'header']); + + class ListingTable { + public async getSearchFilter() { + const searchFilter = await find.allByCssSelector('.euiFieldSearch'); + return searchFilter[0]; + } + + public async clearFilter() { + const searchFilter = await this.getSearchFilter(); + await searchFilter.clearValue(); + await searchFilter.click(); + } - class VisualizeListingTable { public async getAllVisualizationNamesOnCurrentPage(): Promise { const visualizationNames = []; const links = await find.allByCssSelector('.kuiLink'); @@ -36,8 +48,44 @@ export function VisualizeListingTableProvider({ getService, getPageObjects }: Ft return visualizationNames; } + public async getItemsCount(appName: 'visualize' | 'dashboard'): Promise { + const prefixMap = { visualize: 'vis', dashboard: 'dashboard' }; + const elements = await find.allByCssSelector( + `[data-test-subj^="${prefixMap[appName]}ListingTitleLink"]` + ); + return elements.length; + } + + public async searchForItemWithName(name: string) { + log.debug(`searchForItemWithName: ${name}`); + + await retry.try(async () => { + const searchFilter = await this.getSearchFilter(); + await searchFilter.clearValue(); + await searchFilter.click(); + // Note: this replacement of - to space is to preserve original logic but I'm not sure why or if it's needed. + await searchFilter.type(name.replace('-', ' ')); + await common.pressEnterKey(); + }); + + await header.waitUntilLoadingHasFinished(); + } + + public async clickDeleteSelected() { + await testSubjects.click('deleteSelectedItems'); + } + + public async checkListingSelectAllCheckbox() { + const element = await testSubjects.find('checkboxSelectAll'); + const isSelected = await element.isSelected(); + if (!isSelected) { + log.debug(`checking checkbox "checkboxSelectAll"`); + await testSubjects.click('checkboxSelectAll'); + } + } + public async getAllVisualizationNames(): Promise { - log.debug('VisualizeListingTable.getAllVisualizationNames'); + log.debug('ListingTable.getAllVisualizationNames'); let morePages = true; let visualizationNames: string[] = []; while (morePages) { @@ -54,5 +102,5 @@ export function VisualizeListingTableProvider({ getService, getPageObjects }: Ft } } - return new VisualizeListingTable(); + return new ListingTable(); } diff --git a/test/functional/services/remote/remote.ts b/test/functional/services/remote/remote.ts index 69c27936210951..afe8499a1c2ea3 100644 --- a/test/functional/services/remote/remote.ts +++ b/test/functional/services/remote/remote.ts @@ -100,7 +100,9 @@ export async function RemoteProvider({ getService }: FtrProviderContext) { .subscribe({ next({ message, level }) { const msg = message.replace(/\\n/g, '\n'); - log[level === 'SEVERE' ? 'error' : 'debug'](`browser[${level}] ${msg}`); + log[level === 'SEVERE' || level === 'error' ? 'error' : 'debug']( + `browser[${level}] ${msg}` + ); }, }); diff --git a/test/functional/services/screenshots.ts b/test/functional/services/screenshots.ts index 9e673fe919a74c..4c5728174cf992 100644 --- a/test/functional/services/screenshots.ts +++ b/test/functional/services/screenshots.ts @@ -51,7 +51,7 @@ export async function ScreenshotsProvider({ getService }: FtrProviderContext) { * @param updateBaselines {boolean} optional, pass true to update the baseline snapshot. * @return {Promise.} Percentage difference between the baseline and the current snapshot. */ - async compareAgainstBaseline(name: string, updateBaselines: boolean, el: WebElementWrapper) { + async compareAgainstBaseline(name: string, updateBaselines: boolean, el?: WebElementWrapper) { log.debug('compareAgainstBaseline'); const sessionPath = resolve(SESSION_DIRECTORY, `${name}.png`); await this._take(sessionPath, el); diff --git a/test/functional/services/test_subjects.ts b/test/functional/services/test_subjects.ts index a3f64e6f96cc84..8ef008d5dee50e 100644 --- a/test/functional/services/test_subjects.ts +++ b/test/functional/services/test_subjects.ts @@ -295,6 +295,25 @@ export function TestSubjectsProvider({ getService }: FtrProviderContext) { public getCssSelector(selector: string): string { return testSubjSelector(selector); } + + public async scrollIntoView(selector: string) { + const element = await this.find(selector); + await element.scrollIntoViewIfNecessary(); + } + + public async isChecked(selector: string) { + const checkbox = await this.find(selector); + return await checkbox.isSelected(); + } + + public async setCheckbox(selector: string, state: 'check' | 'uncheck') { + const isChecked = await this.isChecked(selector); + const states = { check: true, uncheck: false }; + if (isChecked !== states[state]) { + log.debug(`updating checkbox ${selector}`); + await this.click(selector); + } + } } return new TestSubjects(); diff --git a/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js b/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js index 30b9dbe0fe80ae..ef6f0a626bd159 100644 --- a/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js +++ b/test/plugin_functional/test_suites/custom_visualizations/self_changing_vis.js @@ -22,7 +22,7 @@ import expect from '@kbn/expect'; export default function({ getService, getPageObjects }) { const testSubjects = getService('testSubjects'); const renderable = getService('renderable'); - const PageObjects = getPageObjects(['common', 'visualize']); + const PageObjects = getPageObjects(['common', 'visualize', 'visEditor']); async function getCounterValue() { return await testSubjects.getVisibleText('counter'); @@ -42,9 +42,9 @@ export default function({ getService, getPageObjects }) { const editor = await testSubjects.find('counterEditor'); await editor.clearValue(); await editor.type('10'); - const isApplyEnabled = await PageObjects.visualize.isApplyEnabled(); + const isApplyEnabled = await PageObjects.visEditor.isApplyEnabled(); expect(isApplyEnabled).to.be(true); - await PageObjects.visualize.clickGo(); + await PageObjects.visEditor.clickGo(); const counter = await getCounterValue(); expect(counter).to.be('10'); }); @@ -57,7 +57,7 @@ export default function({ getService, getPageObjects }) { const editorValue = await getEditorValue(); expect(editorValue).to.be('11'); // If changing a param from within the vis it should immediately apply and not bring editor in an unchanged state - const isApplyEnabled = await PageObjects.visualize.isApplyEnabled(); + const isApplyEnabled = await PageObjects.visEditor.isApplyEnabled(); expect(isApplyEnabled).to.be(false); }); }); diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh index f79fe98e07bef4..2605655ed7e7ad 100755 --- a/test/scripts/jenkins_build_kibana.sh +++ b/test/scripts/jenkins_build_kibana.sh @@ -8,5 +8,8 @@ node scripts/es snapshot --license=oss --download-only; echo " -> Ensuring all functional tests are in a ciGroup" yarn run grunt functionalTests:ensureAllTestsInCiGroup; -echo " -> building and extracting OSS Kibana distributable for use in functional tests" -node scripts/build --debug --oss +# Do not build kibana for code coverage run +if [[ -z "$CODE_COVERAGE" ]] ; then + echo " -> building and extracting OSS Kibana distributable for use in functional tests" + node scripts/build --debug --oss +fi diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh index 1cb566c908dbfb..fccdb29ff512bd 100755 --- a/test/scripts/jenkins_ci_group.sh +++ b/test/scripts/jenkins_ci_group.sh @@ -2,22 +2,30 @@ source test/scripts/jenkins_test_setup.sh -if [[ -z "$IS_PIPELINE_JOB" ]] ; then - yarn run grunt functionalTests:ensureAllTestsInCiGroup; - node scripts/build --debug --oss; -else - installDir="$(realpath $PARENT_DIR/kibana/build/oss/kibana-*-SNAPSHOT-linux-x86_64)" - destDir=${installDir}-${CI_WORKER_NUMBER} - cp -R "$installDir" "$destDir" +if [[ -z "$CODE_COVERAGE" ]] ; then + if [[ -z "$IS_PIPELINE_JOB" ]] ; then + yarn run grunt functionalTests:ensureAllTestsInCiGroup; + node scripts/build --debug --oss; + else + installDir="$(realpath $PARENT_DIR/kibana/build/oss/kibana-*-SNAPSHOT-linux-x86_64)" + destDir=${installDir}-${CI_WORKER_NUMBER} + cp -R "$installDir" "$destDir" - export KIBANA_INSTALL_DIR="$destDir" -fi + export KIBANA_INSTALL_DIR="$destDir" + fi + + checks-reporter-with-killswitch "Functional tests / Group ${CI_GROUP}" yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}"; + + if [ "$CI_GROUP" == "1" ]; then + source test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh + yarn run grunt run:pluginFunctionalTestsRelease --from=source; + yarn run grunt run:exampleFunctionalTestsRelease --from=source; + yarn run grunt run:interpreterFunctionalTestsRelease; + fi +else + echo " -> Running Functional tests with code coverage" -checks-reporter-with-killswitch "Functional tests / Group ${CI_GROUP}" yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}"; + export NODE_OPTIONS=--max_old_space_size=8192 -if [ "$CI_GROUP" == "1" ]; then - source test/scripts/jenkins_build_kbn_tp_sample_panel_action.sh - yarn run grunt run:pluginFunctionalTestsRelease --from=source; - yarn run grunt run:exampleFunctionalTestsRelease --from=source; - yarn run grunt run:interpreterFunctionalTestsRelease; + yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}"; fi diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh index 75610884b542fd..a8b5e8e4fdf976 100755 --- a/test/scripts/jenkins_unit.sh +++ b/test/scripts/jenkins_unit.sh @@ -4,4 +4,16 @@ set -e export TEST_BROWSER_HEADLESS=1 -"$(FORCE_COLOR=0 yarn bin)/grunt" jenkins:unit --dev; +if [[ -z "$CODE_COVERAGE" ]] ; then + "$(FORCE_COLOR=0 yarn bin)/grunt" jenkins:unit --dev; +else + echo "NODE_ENV=$NODE_ENV" + echo " -> Running jest tests with coverage" + node scripts/jest --ci --verbose --coverage + echo "" + echo "" + echo " -> Running mocha tests with coverage" + yarn run grunt "test:mochaCoverage"; + echo "" + echo "" +fi diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh index 27f73c0b6e20d2..e0055085d9b372 100755 --- a/test/scripts/jenkins_xpack.sh +++ b/test/scripts/jenkins_xpack.sh @@ -4,33 +4,48 @@ set -e export TEST_BROWSER_HEADLESS=1 -echo " -> Running mocha tests" -cd "$XPACK_DIR" -checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:browser -echo "" -echo "" - -echo " -> Running jest tests" -cd "$XPACK_DIR" -checks-reporter-with-killswitch "X-Pack Jest" node scripts/jest --ci --verbose -echo "" -echo "" - -echo " -> Running SIEM cyclic dependency test" -cd "$XPACK_DIR" -checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node legacy/plugins/siem/scripts/check_circular_deps -echo "" -echo "" - -# FAILING: https://github.com/elastic/kibana/issues/44250 -# echo " -> Running jest contracts tests" -# cd "$XPACK_DIR" -# SLAPSHOT_ONLINE=true CONTRACT_ONLINE=true node scripts/jest_contract.js --ci --verbose -# echo "" -# echo "" - -# echo " -> Running jest integration tests" -# cd "$XPACK_DIR" -# node scripts/jest_integration --ci --verbose -# echo "" -# echo "" +if [[ -z "$CODE_COVERAGE" ]] ; then + echo " -> Running mocha tests" + cd "$XPACK_DIR" + checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:browser + echo "" + echo "" + + echo " -> Running jest tests" + cd "$XPACK_DIR" + checks-reporter-with-killswitch "X-Pack Jest" node scripts/jest --ci --verbose + echo "" + echo "" + + echo " -> Running SIEM cyclic dependency test" + cd "$XPACK_DIR" + checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node legacy/plugins/siem/scripts/check_circular_deps + echo "" + echo "" + + # FAILING: https://github.com/elastic/kibana/issues/44250 + # echo " -> Running jest contracts tests" + # cd "$XPACK_DIR" + # SLAPSHOT_ONLINE=true CONTRACT_ONLINE=true node scripts/jest_contract.js --ci --verbose + # echo "" + # echo "" + + # echo " -> Running jest integration tests" + # cd "$XPACK_DIR" + # node scripts/jest_integration --ci --verbose + # echo "" + # echo "" +else + echo " -> Running jest tests with coverage" + cd "$XPACK_DIR" + # build runtime for canvas + echo "NODE_ENV=$NODE_ENV" + node ./legacy/plugins/canvas/scripts/shareable_runtime + node scripts/jest --ci --verbose --coverage + # rename file in order to be unique one + test -f ../target/kibana-coverage/jest/coverage-final.json \ + && mv ../target/kibana-coverage/jest/coverage-final.json \ + ../target/kibana-coverage/jest/xpack-coverage-final.json + echo "" + echo "" +fi \ No newline at end of file diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh index 9f2bafc863f41a..20b12b302cb397 100755 --- a/test/scripts/jenkins_xpack_build_kibana.sh +++ b/test/scripts/jenkins_xpack_build_kibana.sh @@ -20,10 +20,13 @@ node scripts/functional_tests --assert-none-excluded \ --include-tag ciGroup9 \ --include-tag ciGroup10 -echo " -> building and extracting default Kibana distributable for use in functional tests" -cd "$KIBANA_DIR" -node scripts/build --debug --no-oss -linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" -installDir="$PARENT_DIR/install/kibana" -mkdir -p "$installDir" -tar -xzf "$linuxBuild" -C "$installDir" --strip=1 +# Do not build kibana for code coverage run +if [[ -z "$CODE_COVERAGE" ]] ; then + echo " -> building and extracting default Kibana distributable for use in functional tests" + cd "$KIBANA_DIR" + node scripts/build --debug --no-oss + linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" + installDir="$PARENT_DIR/install/kibana" + mkdir -p "$installDir" + tar -xzf "$linuxBuild" -C "$installDir" --strip=1 +fi diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh index fba05f8f252d76..58c407a848ae3d 100755 --- a/test/scripts/jenkins_xpack_ci_group.sh +++ b/test/scripts/jenkins_xpack_ci_group.sh @@ -2,59 +2,60 @@ source test/scripts/jenkins_test_setup.sh -if [[ -z "$IS_PIPELINE_JOB" ]] ; then - echo " -> Ensuring all functional tests are in a ciGroup" +if [[ -z "$CODE_COVERAGE" ]] ; then + if [[ -z "$IS_PIPELINE_JOB" ]] ; then + echo " -> Ensuring all functional tests are in a ciGroup" + cd "$XPACK_DIR" + node scripts/functional_tests --assert-none-excluded \ + --include-tag ciGroup1 \ + --include-tag ciGroup2 \ + --include-tag ciGroup3 \ + --include-tag ciGroup4 \ + --include-tag ciGroup5 \ + --include-tag ciGroup6 \ + --include-tag ciGroup7 \ + --include-tag ciGroup8 \ + --include-tag ciGroup9 \ + --include-tag ciGroup10 + fi + + cd "$KIBANA_DIR" + + if [[ -z "$IS_PIPELINE_JOB" ]] ; then + echo " -> building and extracting default Kibana distributable for use in functional tests" + node scripts/build --debug --no-oss + + linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" + installDir="$PARENT_DIR/install/kibana" + + mkdir -p "$installDir" + tar -xzf "$linuxBuild" -C "$installDir" --strip=1 + + export KIBANA_INSTALL_DIR="$installDir" + else + installDir="$PARENT_DIR/install/kibana" + destDir="${installDir}-${CI_WORKER_NUMBER}" + cp -R "$installDir" "$destDir" + + export KIBANA_INSTALL_DIR="$destDir" + fi + + echo " -> Running functional and api tests" cd "$XPACK_DIR" - node scripts/functional_tests --assert-none-excluded \ - --include-tag ciGroup1 \ - --include-tag ciGroup2 \ - --include-tag ciGroup3 \ - --include-tag ciGroup4 \ - --include-tag ciGroup5 \ - --include-tag ciGroup6 \ - --include-tag ciGroup7 \ - --include-tag ciGroup8 \ - --include-tag ciGroup9 \ - --include-tag ciGroup10 -fi - -cd "$KIBANA_DIR" - -if [[ -z "$IS_PIPELINE_JOB" ]] ; then - echo " -> building and extracting default Kibana distributable for use in functional tests" - node scripts/build --debug --no-oss - - linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" - installDir="$PARENT_DIR/install/kibana" - mkdir -p "$installDir" - tar -xzf "$linuxBuild" -C "$installDir" --strip=1 + checks-reporter-with-killswitch "X-Pack Chrome Functional tests / Group ${CI_GROUP}" \ + node scripts/functional_tests \ + --debug --bail \ + --kibana-install-dir "$KIBANA_INSTALL_DIR" \ + --include-tag "ciGroup$CI_GROUP" - export KIBANA_INSTALL_DIR="$installDir" + echo "" + echo "" else - installDir="$PARENT_DIR/install/kibana" - destDir="${installDir}-${CI_WORKER_NUMBER}" - cp -R "$installDir" "$destDir" + echo " -> Running X-Pack functional tests with code coverage" + cd "$XPACK_DIR" - export KIBANA_INSTALL_DIR="$destDir" -fi + export NODE_OPTIONS=--max_old_space_size=8192 -echo " -> Running functional and api tests" -cd "$XPACK_DIR" - -checks-reporter-with-killswitch "X-Pack Chrome Functional tests / Group ${CI_GROUP}" \ - node scripts/functional_tests \ - --debug --bail \ - --kibana-install-dir "$KIBANA_INSTALL_DIR" \ - --include-tag "ciGroup$CI_GROUP" - -echo "" -echo "" - -# checks-reporter-with-killswitch "X-Pack Firefox Functional tests / Group ${CI_GROUP}" \ -# node scripts/functional_tests --debug --bail \ -# --kibana-install-dir "$installDir" \ -# --include-tag "ciGroup$CI_GROUP" \ -# --config "test/functional/config.firefox.js" -# echo "" -# echo "" + node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" +fi diff --git a/vars/esSnapshots.groovy b/vars/esSnapshots.groovy new file mode 100644 index 00000000000000..884fbcdb17aebb --- /dev/null +++ b/vars/esSnapshots.groovy @@ -0,0 +1,50 @@ +def promote(snapshotVersion, snapshotId) { + def snapshotDestination = "${snapshotVersion}/archives/${snapshotId}" + def MANIFEST_URL = "https://storage.googleapis.com/kibana-ci-es-snapshots-daily/${snapshotDestination}/manifest.json" + + dir('verified-manifest') { + def verifiedSnapshotFilename = 'manifest-latest-verified.json' + + sh """ + curl -O '${MANIFEST_URL}' + mv manifest.json ${verifiedSnapshotFilename} + """ + + googleStorageUpload( + credentialsId: 'kibana-ci-gcs-plugin', + bucket: "gs://kibana-ci-es-snapshots-daily/${snapshotVersion}", + pattern: verifiedSnapshotFilename, + sharedPublicly: false, + showInline: false, + ) + } + + // This would probably be more efficient if we could just copy using gsutil and specifying buckets for src and dest + // But we don't currently have access to the GCS credentials in a way that can be consumed easily from here... + dir('transfer-to-permanent') { + googleStorageDownload( + credentialsId: 'kibana-ci-gcs-plugin', + bucketUri: "gs://kibana-ci-es-snapshots-daily/${snapshotDestination}/*", + localDirectory: '.', + pathPrefix: snapshotDestination, + ) + + def manifestJson = readFile file: 'manifest.json' + writeFile( + file: 'manifest.json', + text: manifestJson.replace("kibana-ci-es-snapshots-daily/${snapshotDestination}", "kibana-ci-es-snapshots-permanent/${snapshotVersion}") + ) + + // Ideally we would have some delete logic here before uploading, + // But we don't currently have access to the GCS credentials in a way that can be consumed easily from here... + googleStorageUpload( + credentialsId: 'kibana-ci-gcs-plugin', + bucket: "gs://kibana-ci-es-snapshots-permanent/${snapshotVersion}", + pattern: '*.*', + sharedPublicly: false, + showInline: false, + ) + } +} + +return this diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 18f214554b444d..5c6be70514c610 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -137,13 +137,8 @@ def jobRunner(label, useRamDisk, closure) { def scmVars // Try to clone from Github up to 8 times, waiting 15 secs between attempts - retry(8) { - try { - scmVars = checkout scm - } catch (ex) { - sleep 15 - throw ex - } + retryWithDelay(8, 15) { + scmVars = checkout scm } withEnv([ @@ -181,6 +176,18 @@ def uploadGcsArtifact(uploadPrefix, pattern) { ) } +def downloadCoverageArtifacts() { + def storageLocation = "gs://kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/coverage/" + def targetLocation = "/tmp/downloaded_coverage" + + sh "mkdir -p '${targetLocation}' && gsutil -m cp -r '${storageLocation}' '${targetLocation}'" +} + +def uploadCoverageArtifacts(prefix, pattern) { + def uploadPrefix = "kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/coverage/${prefix}" + uploadGcsArtifact(uploadPrefix, pattern) +} + def withGcsArtifactUpload(workerName, closure) { def uploadPrefix = "kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" def ARTIFACT_PATTERNS = [ @@ -206,6 +213,11 @@ def withGcsArtifactUpload(workerName, closure) { } } }) + + if (env.CODE_COVERAGE) { + sh 'tar -czf kibana-coverage.tar.gz target/kibana-coverage/**/*' + uploadGcsArtifact("kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/coverage/${workerName}", 'kibana-coverage.tar.gz') + } } def publishJunit() { diff --git a/vars/retryWithDelay.groovy b/vars/retryWithDelay.groovy new file mode 100644 index 00000000000000..70d6f86a63ab2d --- /dev/null +++ b/vars/retryWithDelay.groovy @@ -0,0 +1,16 @@ +def call(retryTimes, delaySecs, closure) { + retry(retryTimes) { + try { + closure() + } catch (ex) { + sleep delaySecs + throw ex + } + } +} + +def call(retryTimes, Closure closure) { + call(retryTimes, 15, closure) +} + +return this diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index cd4414b5fdebe6..02904cc48e0306 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -30,7 +30,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) { '^test_utils/find_test_subject': `${xPackKibanaDirectory}/test_utils/find_test_subject.ts`, }, coverageDirectory: '/../target/kibana-coverage/jest', - coverageReporters: ['html'], + coverageReporters: !!process.env.CODE_COVERAGE ? ['json'] : ['html'], setupFiles: [ `${kibanaDirectory}/src/dev/jest/setup/babel_polyfill.js`, `/dev-tools/jest/setup/polyfills.js`, diff --git a/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts index 35d81ba74fa72d..1da8b06e1587a9 100644 --- a/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts +++ b/x-pack/legacy/plugins/actions/server/builtin_action_types/es_index.test.ts @@ -142,7 +142,7 @@ describe('params validation', () => { ); expect(() => { - validateParams(actionType, { refresh: 'true' }); + validateParams(actionType, { refresh: 'foo' }); }).toThrowErrorMatchingInlineSnapshot( `"error validating action params: [refresh]: expected value of type [boolean] but got [string]"` ); diff --git a/x-pack/legacy/plugins/actions/server/plugin.ts b/x-pack/legacy/plugins/actions/server/plugin.ts index e8bc5d60a697b9..48f99ba5135b78 100644 --- a/x-pack/legacy/plugins/actions/server/plugin.ts +++ b/x-pack/legacy/plugins/actions/server/plugin.ts @@ -69,7 +69,7 @@ export class Plugin { plugins: ActionsPluginsSetup ): Promise { const config = await this.config$.pipe(first()).toPromise(); - this.adminClient = await core.elasticsearch.adminClient$.pipe(first()).toPromise(); + this.adminClient = core.elasticsearch.adminClient; this.defaultKibanaIndex = (await this.kibana$.pipe(first()).toPromise()).index; this.licenseState = new LicenseState(plugins.licensing.license$); diff --git a/x-pack/legacy/plugins/alerting/server/plugin.ts b/x-pack/legacy/plugins/alerting/server/plugin.ts index ede95f76bf8113..fb16f579d4c701 100644 --- a/x-pack/legacy/plugins/alerting/server/plugin.ts +++ b/x-pack/legacy/plugins/alerting/server/plugin.ts @@ -5,7 +5,7 @@ */ import Hapi from 'hapi'; -import { first } from 'rxjs/operators'; + import { Services } from './types'; import { AlertsClient } from './alerts_client'; import { AlertTypeRegistry } from './alert_type_registry'; @@ -62,7 +62,7 @@ export class Plugin { core: AlertingCoreSetup, plugins: AlertingPluginsSetup ): Promise { - this.adminClient = await core.elasticsearch.adminClient$.pipe(first()).toPromise(); + this.adminClient = core.elasticsearch.adminClient; this.licenseState = new LicenseState(plugins.licensing.license$); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx new file mode 100644 index 00000000000000..ff2cb69d011fa4 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.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 { EuiText, EuiTextColor } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +interface Props { + count: number; +} + +export const ErrorCount = ({ count }: Props) => ( + +

+ { + e.stopPropagation(); + }} + > + {i18n.translate('xpack.apm.transactionDetails.errorCount', { + defaultMessage: + '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}', + values: { errorCount: count } + })} + +

+
+); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge.tsx deleted file mode 100644 index 4c3ec3ca9f3080..00000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiBadge } from '@elastic/eui'; -import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json'; -import React from 'react'; - -type Props = React.ComponentProps; - -export const ErrorCountBadge = ({ children, ...rest }: Props) => ( - - {children} - -); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx index 39e52be34a415a..322ec7c422571a 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx @@ -25,8 +25,9 @@ export const MaybeViewTraceLink = ({ } ); + const { rootTransaction } = waterfall; // the traceroot cannot be found, so we cannot link to it - if (!waterfall.traceRoot) { + if (!rootTransaction) { return ( {viewFullTraceButtonLabel} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx index e5be12509e3c91..f8318b9ae97e6d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx @@ -77,7 +77,6 @@ export function TransactionTabs({ {currentTab.key === timelineTab.key ? ( { it('should sort the marks by time', () => { @@ -21,9 +21,24 @@ describe('getAgentMarks', () => { } } as any; expect(getAgentMarks(transaction)).toEqual([ - { name: 'timeToFirstByte', us: 10000 }, - { name: 'domInteractive', us: 117000 }, - { name: 'domComplete', us: 118000 } + { + id: 'timeToFirstByte', + offset: 10000, + type: 'agentMark', + verticalLine: true + }, + { + id: 'domInteractive', + offset: 117000, + type: 'agentMark', + verticalLine: true + }, + { + id: 'domComplete', + offset: 118000, + type: 'agentMark', + verticalLine: true + } ]); }); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts new file mode 100644 index 00000000000000..8fd8edd7f8a728 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IWaterfallItem } from '../../Waterfall/waterfall_helpers/waterfall_helpers'; +import { getErrorMarks } from '../get_error_marks'; + +describe('getErrorMarks', () => { + describe('returns empty array', () => { + it('when items are missing', () => { + expect(getErrorMarks([], {})).toEqual([]); + }); + it('when any error is available', () => { + const items = [ + { docType: 'span' }, + { docType: 'transaction' } + ] as IWaterfallItem[]; + expect(getErrorMarks(items, {})).toEqual([]); + }); + }); + + it('returns error marks', () => { + const items = [ + { + docType: 'error', + offset: 10, + skew: 5, + doc: { error: { id: 1 }, service: { name: 'opbeans-java' } } + } as unknown, + { docType: 'transaction' }, + { + docType: 'error', + offset: 50, + skew: 0, + doc: { error: { id: 2 }, service: { name: 'opbeans-node' } } + } as unknown + ] as IWaterfallItem[]; + expect( + getErrorMarks(items, { 'opbeans-java': 'red', 'opbeans-node': 'blue' }) + ).toEqual([ + { + type: 'errorMark', + offset: 15, + verticalLine: false, + id: 1, + error: { error: { id: 1 }, service: { name: 'opbeans-java' } }, + serviceColor: 'red' + }, + { + type: 'errorMark', + offset: 50, + verticalLine: false, + id: 2, + error: { error: { id: 2 }, service: { name: 'opbeans-node' } }, + serviceColor: 'blue' + } + ]); + }); + + it('returns error marks without service color', () => { + const items = [ + { + docType: 'error', + offset: 10, + skew: 5, + doc: { error: { id: 1 }, service: { name: 'opbeans-java' } } + } as unknown, + { docType: 'transaction' }, + { + docType: 'error', + offset: 50, + skew: 0, + doc: { error: { id: 2 }, service: { name: 'opbeans-node' } } + } as unknown + ] as IWaterfallItem[]; + expect(getErrorMarks(items, {})).toEqual([ + { + type: 'errorMark', + offset: 15, + verticalLine: false, + id: 1, + error: { error: { id: 1 }, service: { name: 'opbeans-java' } }, + serviceColor: undefined + }, + { + type: 'errorMark', + offset: 50, + verticalLine: false, + id: 2, + error: { error: { id: 2 }, service: { name: 'opbeans-node' } }, + serviceColor: undefined + } + ]); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts new file mode 100644 index 00000000000000..7798d716cb2194 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { sortBy } from 'lodash'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/Transaction'; +import { Mark } from '.'; + +// Extends Mark without adding new properties to it. +export interface AgentMark extends Mark { + type: 'agentMark'; +} + +export function getAgentMarks(transaction?: Transaction): AgentMark[] { + const agent = transaction?.transaction.marks?.agent; + if (!agent) { + return []; + } + + return sortBy( + Object.entries(agent).map(([name, ms]) => ({ + type: 'agentMark', + id: name, + offset: ms * 1000, + verticalLine: true + })), + 'offset' + ); +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts new file mode 100644 index 00000000000000..f1f0163a49d105 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.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 { isEmpty } from 'lodash'; +import { ErrorRaw } from '../../../../../../../typings/es_schemas/raw/ErrorRaw'; +import { + IWaterfallItem, + IWaterfallError, + IServiceColors +} from '../Waterfall/waterfall_helpers/waterfall_helpers'; +import { Mark } from '.'; + +export interface ErrorMark extends Mark { + type: 'errorMark'; + error: ErrorRaw; + serviceColor?: string; +} + +export const getErrorMarks = ( + items: IWaterfallItem[], + serviceColors: IServiceColors +): ErrorMark[] => { + if (isEmpty(items)) { + return []; + } + + return (items.filter( + item => item.docType === 'error' + ) as IWaterfallError[]).map(error => ({ + type: 'errorMark', + offset: error.offset + error.skew, + verticalLine: false, + id: error.doc.error.id, + error: error.doc, + serviceColor: serviceColors[error.doc.service.name] + })); +}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts new file mode 100644 index 00000000000000..52f811f5c3969d --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface Mark { + type: string; + offset: number; + verticalLine: boolean; + id: string; +} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx index e4cb4ff62b36c6..4e6a0eaf455852 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx @@ -10,7 +10,7 @@ import React from 'react'; import styled from 'styled-components'; import { px, unit } from '../../../../../style/variables'; // @ts-ignore -import Legend from '../../../../shared/charts/Legend'; +import { Legend } from '../../../../shared/charts/Legend'; import { IServiceColors } from './Waterfall/waterfall_helpers/waterfall_helpers'; const Legends = styled.div` diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx index cc1f9dd529bce3..4863d6519de070 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx @@ -101,7 +101,7 @@ export function SpanFlyout({ const dbContext = span.span.db; const httpContext = span.span.http; const spanTypes = getSpanTypes(span); - const spanHttpStatusCode = httpContext?.response.status_code; + const spanHttpStatusCode = httpContext?.response?.status_code; const spanHttpUrl = httpContext?.url?.original; const spanHttpMethod = httpContext?.method; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx index 2020b8252035b7..df95577c81eff7 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx @@ -27,8 +27,8 @@ import { DroppedSpansWarning } from './DroppedSpansWarning'; interface Props { onClose: () => void; transaction?: Transaction; - errorCount: number; - traceRootDuration?: number; + errorCount?: number; + rootTransactionDuration?: number; } function TransactionPropertiesTable({ @@ -49,8 +49,8 @@ function TransactionPropertiesTable({ export function TransactionFlyout({ transaction: transactionDoc, onClose, - errorCount, - traceRootDuration + errorCount = 0, + rootTransactionDuration }: Props) { if (!transactionDoc) { return null; @@ -84,7 +84,7 @@ export function TransactionFlyout({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx new file mode 100644 index 00000000000000..426088f0bb36a9 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Location } from 'history'; +import React from 'react'; +import { SpanFlyout } from './SpanFlyout'; +import { TransactionFlyout } from './TransactionFlyout'; +import { IWaterfall } from './waterfall_helpers/waterfall_helpers'; + +interface Props { + waterfallItemId?: string; + waterfall: IWaterfall; + location: Location; + toggleFlyout: ({ location }: { location: Location }) => void; +} +export const WaterfallFlyout: React.FC = ({ + waterfallItemId, + waterfall, + location, + toggleFlyout +}) => { + const currentItem = waterfall.items.find(item => item.id === waterfallItemId); + + if (!currentItem) { + return null; + } + + switch (currentItem.docType) { + case 'span': + const parentTransaction = + currentItem.parent?.docType === 'transaction' + ? currentItem.parent?.doc + : undefined; + + return ( + toggleFlyout({ location })} + /> + ); + case 'transaction': + return ( + toggleFlyout({ location })} + rootTransactionDuration={ + waterfall.rootTransaction?.transaction.duration.us + } + errorCount={waterfall.errorsPerTransaction[currentItem.id]} + /> + ); + default: + return null; + } +}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx index 8d4fab4aa8dd91..8a82547d717db2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx @@ -13,12 +13,12 @@ import { i18n } from '@kbn/i18n'; import { isRumAgentName } from '../../../../../../../common/agent_name'; import { px, unit, units } from '../../../../../../style/variables'; import { asDuration } from '../../../../../../utils/formatters'; -import { ErrorCountBadge } from '../../ErrorCountBadge'; +import { ErrorCount } from '../../ErrorCount'; import { IWaterfallItem } from './waterfall_helpers/waterfall_helpers'; import { ErrorOverviewLink } from '../../../../../shared/Links/apm/ErrorOverviewLink'; import { TRACE_ID } from '../../../../../../../common/elasticsearch_fieldnames'; -type ItemType = 'transaction' | 'span'; +type ItemType = 'transaction' | 'span' | 'error'; interface IContainerStyleProps { type: ItemType; @@ -89,24 +89,29 @@ interface IWaterfallItemProps { } function PrefixIcon({ item }: { item: IWaterfallItem }) { - if (item.docType === 'span') { - // icon for database spans - const isDbType = item.span.span.type.startsWith('db'); - if (isDbType) { - return ; + switch (item.docType) { + case 'span': { + // icon for database spans + const isDbType = item.doc.span.type.startsWith('db'); + if (isDbType) { + return ; + } + + // omit icon for other spans + return null; } - - // omit icon for other spans - return null; - } - - // icon for RUM agent transactions - if (isRumAgentName(item.transaction.agent.name)) { - return ; + case 'transaction': { + // icon for RUM agent transactions + if (isRumAgentName(item.doc.agent.name)) { + return ; + } + + // icon for other transactions + return ; + } + default: + return null; } - - // icon for other transactions - return ; } interface SpanActionToolTipProps { @@ -117,11 +122,9 @@ const SpanActionToolTip: React.FC = ({ item, children }) => { - if (item && item.docType === 'span') { + if (item?.docType === 'span') { return ( - + <>{children} ); @@ -140,9 +143,8 @@ function Duration({ item }: { item: IWaterfallItem }) { function HttpStatusCode({ item }: { item: IWaterfallItem }) { // http status code for transactions of type 'request' const httpStatusCode = - item.docType === 'transaction' && - item.transaction.transaction.type === 'request' - ? item.transaction.transaction.result + item.docType === 'transaction' && item.doc.transaction.type === 'request' + ? item.doc.transaction.result : undefined; if (!httpStatusCode) { @@ -153,14 +155,18 @@ function HttpStatusCode({ item }: { item: IWaterfallItem }) { } function NameLabel({ item }: { item: IWaterfallItem }) { - if (item.docType === 'span') { - return {item.name}; + switch (item.docType) { + case 'span': + return {item.doc.span.name}; + case 'transaction': + return ( + +
{item.doc.transaction.name}
+
+ ); + default: + return null; } - return ( - -
{item.name}
-
- ); } export function WaterfallItem({ @@ -210,24 +216,17 @@ export function WaterfallItem({ {errorCount > 0 && item.docType === 'transaction' ? ( - { - event.stopPropagation(); - }} - onClickAriaLabel={tooltipContent} - > - {errorCount} - + ) : null} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx index d53b4077d9759e..b48fc1cf7ca27e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx @@ -4,31 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Location } from 'history'; -import React, { Component } from 'react'; +import React from 'react'; // @ts-ignore import { StickyContainer } from 'react-sticky'; import styled from 'styled-components'; -import { EuiCallOut } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { IUrlParams } from '../../../../../../context/UrlParamsContext/types'; +import { px } from '../../../../../../style/variables'; +import { history } from '../../../../../../utils/history'; // @ts-ignore import Timeline from '../../../../../shared/charts/Timeline'; +import { fromQuery, toQuery } from '../../../../../shared/Links/url_helpers'; +import { getAgentMarks } from '../Marks/get_agent_marks'; +import { getErrorMarks } from '../Marks/get_error_marks'; +import { WaterfallFlyout } from './WaterfallFlyout'; +import { WaterfallItem } from './WaterfallItem'; import { - APMQueryParams, - fromQuery, - toQuery -} from '../../../../../shared/Links/url_helpers'; -import { history } from '../../../../../../utils/history'; -import { AgentMark } from '../get_agent_marks'; -import { SpanFlyout } from './SpanFlyout'; -import { TransactionFlyout } from './TransactionFlyout'; -import { - IServiceColors, IWaterfall, IWaterfallItem } from './waterfall_helpers/waterfall_helpers'; -import { WaterfallItem } from './WaterfallItem'; const Container = styled.div` transition: 0.1s padding ease; @@ -43,138 +38,105 @@ const TIMELINE_MARGINS = { bottom: 0 }; +const toggleFlyout = ({ + item, + location +}: { + item?: IWaterfallItem; + location: Location; +}) => { + history.replace({ + ...location, + search: fromQuery({ + ...toQuery(location.search), + flyoutDetailTab: undefined, + waterfallItemId: item?.id + }) + }); +}; + +const WaterfallItemsContainer = styled.div<{ + paddingTop: number; +}>` + padding-top: ${props => px(props.paddingTop)}; +`; + interface Props { - agentMarks: AgentMark[]; - urlParams: IUrlParams; + waterfallItemId?: string; waterfall: IWaterfall; location: Location; - serviceColors: IServiceColors; exceedsMax: boolean; } -export class Waterfall extends Component { - public onOpenFlyout = (item: IWaterfallItem) => { - this.setQueryParams({ - flyoutDetailTab: undefined, - waterfallItemId: String(item.id) - }); - }; +export const Waterfall: React.FC = ({ + waterfall, + exceedsMax, + waterfallItemId, + location +}) => { + const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found + const waterfallHeight = itemContainerHeight * waterfall.items.length; - public onCloseFlyout = () => { - this.setQueryParams({ - flyoutDetailTab: undefined, - waterfallItemId: undefined - }); - }; + const { serviceColors, duration } = waterfall; - public renderWaterfallItem = (item: IWaterfallItem) => { - const { serviceColors, waterfall, urlParams }: Props = this.props; + const agentMarks = getAgentMarks(waterfall.entryTransaction); + const errorMarks = getErrorMarks(waterfall.items, serviceColors); + + const renderWaterfallItem = (item: IWaterfallItem) => { + if (item.docType === 'error') { + return null; + } const errorCount = item.docType === 'transaction' - ? waterfall.errorCountByTransactionId[item.transaction.transaction.id] + ? waterfall.errorsPerTransaction[item.doc.transaction.id] : 0; return ( this.onOpenFlyout(item)} + onClick={() => toggleFlyout({ item, location })} /> ); }; - public getFlyOut = () => { - const { waterfall, urlParams } = this.props; - - const currentItem = - urlParams.waterfallItemId && - waterfall.itemsById[urlParams.waterfallItemId]; - - if (!currentItem) { - return null; - } - - switch (currentItem.docType) { - case 'span': - const parentTransaction = waterfall.getTransactionById( - currentItem.parentId - ); - - return ( - - ); - case 'transaction': - return ( - - ); - default: - return null; - } - }; - - public render() { - const { waterfall, exceedsMax } = this.props; - const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found - const waterfallHeight = itemContainerHeight * waterfall.orderedItems.length; - - return ( - - {exceedsMax ? ( - - ) : null} - - -
- {waterfall.orderedItems.map(this.renderWaterfallItem)} -
-
- - {this.getFlyOut()} -
- ); - } - - private setQueryParams(params: APMQueryParams) { - const { location } = this.props; - history.replace({ - ...location, - search: fromQuery({ - ...toQuery(location.search), - ...params - }) - }); - } -} + return ( + + {exceedsMax && ( + + )} + + + + {waterfall.items.map(renderWaterfallItem)} + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap index 6f61f621676386..ece396bc4cfc4f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap @@ -24,145 +24,44 @@ Object { "name": "GET /api", }, }, - "errorCountByTransactionId": Object { + "errorsCount": 1, + "errorsPerTransaction": Object { "myTransactionId1": 2, "myTransactionId2": 3, }, - "getTransactionById": [Function], - "itemsById": Object { - "mySpanIdA": Object { - "childIds": Array [ - "mySpanIdB", - "mySpanIdC", - ], - "docType": "span", - "duration": 6161, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - "offset": 40498, - "parentId": "myTransactionId2", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "myTransactionId2", - }, + "items": Array [ + Object { + "doc": Object { "processor": Object { - "event": "span", + "event": "transaction", }, "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 6161, - }, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", + "name": "opbeans-node", }, "timestamp": Object { - "us": 1549324795824504, + "us": 1549324795784006, }, "trace": Object { "id": "myTraceId", }, "transaction": Object { - "id": "myTransactionId2", - }, - }, - "timestamp": 1549324795824504, - }, - "mySpanIdB": Object { - "childIds": Array [], - "docType": "span", - "duration": 481, - "id": "mySpanIdB", - "name": "SELECT FROM products", - "offset": 41627, - "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "mySpanIdA", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { "duration": Object { - "us": 481, + "us": 49660, }, - "id": "mySpanIdB", - "name": "SELECT FROM products", - }, - "timestamp": Object { - "us": 1549324795825633, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId2", + "id": "myTransactionId1", + "name": "GET /api", }, }, - "timestamp": 1549324795825633, - }, - "mySpanIdC": Object { - "childIds": Array [], - "docType": "span", - "duration": 532, - "id": "mySpanIdC", - "name": "SELECT FROM product", - "offset": 43899, - "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, "skew": 0, - "span": Object { - "parent": Object { - "id": "mySpanIdA", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 532, - }, - "id": "mySpanIdC", - "name": "SELECT FROM product", - }, - "timestamp": Object { - "us": 1549324795827905, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId2", - }, - }, - "timestamp": 1549324795827905, }, - "mySpanIdD": Object { - "childIds": Array [ - "myTransactionId2", - ], - "docType": "span", - "duration": 47557, - "id": "mySpanIdD", - "name": "GET opbeans-ruby:3000/api/products", - "offset": 1754, - "parentId": "myTransactionId1", - "serviceName": "opbeans-node", - "skew": 0, - "span": Object { + Object { + "doc": Object { "parent": Object { "id": "myTransactionId1", }, @@ -189,59 +88,45 @@ Object { "id": "myTransactionId1", }, }, - "timestamp": 1549324795785760, - }, - "myTransactionId1": Object { - "childIds": Array [ - "mySpanIdD", - ], - "docType": "transaction", - "duration": 49660, - "errorCount": 2, - "id": "myTransactionId1", - "name": "GET /api", - "offset": 0, - "parentId": undefined, - "serviceName": "opbeans-node", - "skew": 0, - "timestamp": 1549324795784006, - "transaction": Object { - "processor": Object { - "event": "transaction", - }, - "service": Object { - "name": "opbeans-node", - }, - "timestamp": Object { - "us": 1549324795784006, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "duration": Object { - "us": 49660, + "docType": "span", + "duration": 47557, + "id": "mySpanIdD", + "offset": 1754, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", }, - "id": "myTransactionId1", - "name": "GET /api", }, + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, }, - }, - "myTransactionId2": Object { - "childIds": Array [ - "mySpanIdA", - ], - "docType": "transaction", - "duration": 8634, - "errorCount": 3, - "id": "myTransactionId2", - "name": "Api::ProductsController#index", - "offset": 39298, - "parentId": "mySpanIdD", - "serviceName": "opbeans-ruby", + "parentId": "myTransactionId1", "skew": 0, - "timestamp": 1549324795823304, - "transaction": Object { + }, + Object { + "doc": Object { "parent": Object { "id": "mySpanIdD", }, @@ -262,181 +147,403 @@ Object { "us": 8634, }, "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, "name": "Api::ProductsController#index", }, }, - }, - }, - "orderedItems": Array [ - Object { - "childIds": Array [ - "mySpanIdD", - ], "docType": "transaction", - "duration": 49660, - "errorCount": 2, - "id": "myTransactionId1", - "name": "GET /api", - "offset": 0, - "parentId": undefined, - "serviceName": "opbeans-node", - "skew": 0, - "timestamp": 1549324795784006, - "transaction": Object { - "processor": Object { - "event": "transaction", - }, - "service": Object { - "name": "opbeans-node", - }, - "timestamp": Object { - "us": 1549324795784006, - }, - "trace": Object { - "id": "myTraceId", + "duration": 8634, + "id": "myTransactionId2", + "offset": 39298, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId1", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-node", + }, + "span": Object { + "duration": Object { + "us": 47557, + }, + "id": "mySpanIdD", + "name": "GET opbeans-ruby:3000/api/products", + }, + "timestamp": Object { + "us": 1549324795785760, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId1", + }, }, - "transaction": Object { - "duration": Object { - "us": 49660, + "docType": "span", + "duration": 47557, + "id": "mySpanIdD", + "offset": 1754, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", + }, }, + "docType": "transaction", + "duration": 49660, "id": "myTransactionId1", - "name": "GET /api", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, }, + "parentId": "myTransactionId1", + "skew": 0, }, + "parentId": "mySpanIdD", + "skew": 0, }, Object { - "childIds": Array [ - "myTransactionId2", - ], - "docType": "span", - "duration": 47557, - "id": "mySpanIdD", - "name": "GET opbeans-ruby:3000/api/products", - "offset": 1754, - "parentId": "myTransactionId1", - "serviceName": "opbeans-node", - "skew": 0, - "span": Object { + "doc": Object { "parent": Object { - "id": "myTransactionId1", + "id": "myTransactionId2", }, "processor": Object { "event": "span", }, "service": Object { - "name": "opbeans-node", + "name": "opbeans-ruby", }, "span": Object { "duration": Object { - "us": 47557, + "us": 6161, }, - "id": "mySpanIdD", - "name": "GET opbeans-ruby:3000/api/products", + "id": "mySpanIdA", + "name": "Api::ProductsController#index", }, "timestamp": Object { - "us": 1549324795785760, + "us": 1549324795824504, }, "trace": Object { "id": "myTraceId", }, "transaction": Object { - "id": "myTransactionId1", + "id": "myTransactionId2", + }, + }, + "docType": "span", + "duration": 6161, + "id": "mySpanIdA", + "offset": 40498, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, }, + "docType": "transaction", + "duration": 8634, + "id": "myTransactionId2", + "offset": 39298, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId1", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-node", + }, + "span": Object { + "duration": Object { + "us": 47557, + }, + "id": "mySpanIdD", + "name": "GET opbeans-ruby:3000/api/products", + }, + "timestamp": Object { + "us": 1549324795785760, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId1", + }, + }, + "docType": "span", + "duration": 47557, + "id": "mySpanIdD", + "offset": 1754, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", + }, + }, + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, + }, + "parentId": "myTransactionId1", + "skew": 0, + }, + "parentId": "mySpanIdD", + "skew": 0, }, - "timestamp": 1549324795785760, + "parentId": "myTransactionId2", + "skew": 0, }, Object { - "childIds": Array [ - "mySpanIdA", - ], - "docType": "transaction", - "duration": 8634, - "errorCount": 3, - "id": "myTransactionId2", - "name": "Api::ProductsController#index", - "offset": 39298, - "parentId": "mySpanIdD", - "serviceName": "opbeans-ruby", - "skew": 0, - "timestamp": 1549324795823304, - "transaction": Object { + "doc": Object { "parent": Object { - "id": "mySpanIdD", + "id": "mySpanIdA", }, "processor": Object { - "event": "transaction", + "event": "span", }, "service": Object { "name": "opbeans-ruby", }, + "span": Object { + "duration": Object { + "us": 481, + }, + "id": "mySpanIdB", + "name": "SELECT FROM products", + }, "timestamp": Object { - "us": 1549324795823304, + "us": 1549324795825633, }, "trace": Object { "id": "myTraceId", }, "transaction": Object { - "duration": Object { - "us": 8634, - }, "id": "myTransactionId2", - "name": "Api::ProductsController#index", }, }, - }, - Object { - "childIds": Array [ - "mySpanIdB", - "mySpanIdC", - ], "docType": "span", - "duration": 6161, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - "offset": 40498, - "parentId": "myTransactionId2", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "myTransactionId2", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 6161, + "duration": 481, + "id": "mySpanIdB", + "offset": 41627, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId2", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "span": Object { + "duration": Object { + "us": 6161, + }, + "id": "mySpanIdA", + "name": "Api::ProductsController#index", + }, + "timestamp": Object { + "us": 1549324795824504, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId2", }, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - }, - "timestamp": Object { - "us": 1549324795824504, - }, - "trace": Object { - "id": "myTraceId", }, - "transaction": Object { + "docType": "span", + "duration": 6161, + "id": "mySpanIdA", + "offset": 40498, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, + }, + "docType": "transaction", + "duration": 8634, "id": "myTransactionId2", + "offset": 39298, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId1", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-node", + }, + "span": Object { + "duration": Object { + "us": 47557, + }, + "id": "mySpanIdD", + "name": "GET opbeans-ruby:3000/api/products", + }, + "timestamp": Object { + "us": 1549324795785760, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId1", + }, + }, + "docType": "span", + "duration": 47557, + "id": "mySpanIdD", + "offset": 1754, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", + }, + }, + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, + }, + "parentId": "myTransactionId1", + "skew": 0, + }, + "parentId": "mySpanIdD", + "skew": 0, }, + "parentId": "myTransactionId2", + "skew": 0, }, - "timestamp": 1549324795824504, - }, - Object { - "childIds": Array [], - "docType": "span", - "duration": 481, - "id": "mySpanIdB", - "name": "SELECT FROM products", - "offset": 41627, "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { "parent": Object { "id": "mySpanIdA", }, @@ -448,13 +555,13 @@ Object { }, "span": Object { "duration": Object { - "us": 481, + "us": 532, }, - "id": "mySpanIdB", - "name": "SELECT FROM products", + "id": "mySpanIdC", + "name": "SELECT FROM product", }, "timestamp": Object { - "us": 1549324795825633, + "us": 1549324795827905, }, "trace": Object { "id": "myTraceId", @@ -463,57 +570,223 @@ Object { "id": "myTransactionId2", }, }, - "timestamp": 1549324795825633, - }, - Object { - "childIds": Array [], "docType": "span", "duration": 532, "id": "mySpanIdC", - "name": "SELECT FROM product", "offset": 43899, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId2", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "span": Object { + "duration": Object { + "us": 6161, + }, + "id": "mySpanIdA", + "name": "Api::ProductsController#index", + }, + "timestamp": Object { + "us": 1549324795824504, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId2", + }, + }, + "docType": "span", + "duration": 6161, + "id": "mySpanIdA", + "offset": 40498, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, + }, + "docType": "transaction", + "duration": 8634, + "id": "myTransactionId2", + "offset": 39298, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId1", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-node", + }, + "span": Object { + "duration": Object { + "us": 47557, + }, + "id": "mySpanIdD", + "name": "GET opbeans-ruby:3000/api/products", + }, + "timestamp": Object { + "us": 1549324795785760, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId1", + }, + }, + "docType": "span", + "duration": 47557, + "id": "mySpanIdD", + "offset": 1754, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", + }, + }, + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, + }, + "parentId": "myTransactionId1", + "skew": 0, + }, + "parentId": "mySpanIdD", + "skew": 0, + }, + "parentId": "myTransactionId2", + "skew": 0, + }, "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { + "agent": Object { + "name": "ruby", + "version": "2", + }, + "error": Object { + "grouping_key": "errorGroupingKey1", + "id": "error1", + "log": Object { + "message": "error message", + }, + }, "parent": Object { - "id": "mySpanIdA", + "id": "myTransactionId1", }, "processor": Object { - "event": "span", + "event": "error", }, "service": Object { "name": "opbeans-ruby", }, - "span": Object { - "duration": Object { - "us": 532, - }, - "id": "mySpanIdC", - "name": "SELECT FROM product", - }, "timestamp": Object { - "us": 1549324795827905, + "us": 1549324795810000, }, "trace": Object { "id": "myTraceId", }, "transaction": Object { - "id": "myTransactionId2", + "id": "myTransactionId1", + }, + }, + "docType": "error", + "duration": 0, + "id": "error1", + "offset": 25994, + "parent": Object { + "doc": Object { + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-node", + }, + "timestamp": Object { + "us": 1549324795784006, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 49660, + }, + "id": "myTransactionId1", + "name": "GET /api", + }, }, + "docType": "transaction", + "duration": 49660, + "id": "myTransactionId1", + "offset": 0, + "parent": undefined, + "parentId": undefined, + "skew": 0, }, - "timestamp": 1549324795827905, + "parentId": "myTransactionId1", + "skew": 0, }, ], - "serviceColors": Object { - "opbeans-node": "#3185fc", - "opbeans-ruby": "#00b3a4", - }, - "services": Array [ - "opbeans-node", - "opbeans-ruby", - ], - "traceRoot": Object { + "rootTransaction": Object { "processor": Object { "event": "transaction", }, @@ -534,7 +807,10 @@ Object { "name": "GET /api", }, }, - "traceRootDuration": 49660, + "serviceColors": Object { + "opbeans-node": "#3185fc", + "opbeans-ruby": "#00b3a4", + }, } `; @@ -562,221 +838,24 @@ Object { "us": 8634, }, "id": "myTransactionId2", - "name": "Api::ProductsController#index", - }, - }, - "errorCountByTransactionId": Object { - "myTransactionId1": 2, - "myTransactionId2": 3, - }, - "getTransactionById": [Function], - "itemsById": Object { - "mySpanIdA": Object { - "childIds": Array [ - "mySpanIdB", - "mySpanIdC", - ], - "docType": "span", - "duration": 6161, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - "offset": 1200, - "parentId": "myTransactionId2", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "myTransactionId2", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 6161, - }, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - }, - "timestamp": Object { - "us": 1549324795824504, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId2", - }, - }, - "timestamp": 1549324795824504, - }, - "mySpanIdB": Object { - "childIds": Array [], - "docType": "span", - "duration": 481, - "id": "mySpanIdB", - "name": "SELECT FROM products", - "offset": 2329, - "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "mySpanIdA", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 481, - }, - "id": "mySpanIdB", - "name": "SELECT FROM products", - }, - "timestamp": Object { - "us": 1549324795825633, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId2", - }, - }, - "timestamp": 1549324795825633, - }, - "mySpanIdC": Object { - "childIds": Array [], - "docType": "span", - "duration": 532, - "id": "mySpanIdC", - "name": "SELECT FROM product", - "offset": 4601, - "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { - "parent": Object { - "id": "mySpanIdA", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "span": Object { - "duration": Object { - "us": 532, - }, - "id": "mySpanIdC", - "name": "SELECT FROM product", - }, - "timestamp": Object { - "us": 1549324795827905, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId2", - }, - }, - "timestamp": 1549324795827905, - }, - "mySpanIdD": Object { - "docType": "span", - "duration": 47557, - "id": "mySpanIdD", - "name": "GET opbeans-ruby:3000/api/products", - "offset": 0, - "parentId": "myTransactionId1", - "serviceName": "opbeans-node", - "skew": 0, - "span": Object { - "parent": Object { - "id": "myTransactionId1", - }, - "processor": Object { - "event": "span", - }, - "service": Object { - "name": "opbeans-node", - }, - "span": Object { - "duration": Object { - "us": 47557, - }, - "id": "mySpanIdD", - "name": "GET opbeans-ruby:3000/api/products", - }, - "timestamp": Object { - "us": 1549324795785760, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "id": "myTransactionId1", - }, - }, - "timestamp": 1549324795785760, - }, - "myTransactionId1": Object { - "docType": "transaction", - "duration": 49660, - "errorCount": 2, - "id": "myTransactionId1", - "name": "GET /api", - "offset": 0, - "parentId": undefined, - "serviceName": "opbeans-node", - "skew": 0, - "timestamp": 1549324795784006, - "transaction": Object { - "processor": Object { - "event": "transaction", - }, - "service": Object { - "name": "opbeans-node", - }, - "timestamp": Object { - "us": 1549324795784006, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "duration": Object { - "us": 49660, - }, - "id": "myTransactionId1", - "name": "GET /api", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, }, }, - }, - "myTransactionId2": Object { - "childIds": Array [ - "mySpanIdA", - ], - "docType": "transaction", - "duration": 8634, - "errorCount": 3, - "id": "myTransactionId2", "name": "Api::ProductsController#index", - "offset": 0, - "parentId": "mySpanIdD", - "serviceName": "opbeans-ruby", - "skew": 0, - "timestamp": 1549324795823304, - "transaction": Object { + }, + }, + "errorsCount": 0, + "errorsPerTransaction": Object { + "myTransactionId1": 2, + "myTransactionId2": 3, + }, + "items": Array [ + Object { + "doc": Object { "parent": Object { "id": "mySpanIdD", }, @@ -797,65 +876,26 @@ Object { "us": 8634, }, "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, "name": "Api::ProductsController#index", }, }, - }, - }, - "orderedItems": Array [ - Object { - "childIds": Array [ - "mySpanIdA", - ], "docType": "transaction", "duration": 8634, - "errorCount": 3, "id": "myTransactionId2", - "name": "Api::ProductsController#index", "offset": 0, + "parent": undefined, "parentId": "mySpanIdD", - "serviceName": "opbeans-ruby", "skew": 0, - "timestamp": 1549324795823304, - "transaction": Object { - "parent": Object { - "id": "mySpanIdD", - }, - "processor": Object { - "event": "transaction", - }, - "service": Object { - "name": "opbeans-ruby", - }, - "timestamp": Object { - "us": 1549324795823304, - }, - "trace": Object { - "id": "myTraceId", - }, - "transaction": Object { - "duration": Object { - "us": 8634, - }, - "id": "myTransactionId2", - "name": "Api::ProductsController#index", - }, - }, }, Object { - "childIds": Array [ - "mySpanIdB", - "mySpanIdC", - ], - "docType": "span", - "duration": 6161, - "id": "mySpanIdA", - "name": "Api::ProductsController#index", - "offset": 1200, - "parentId": "myTransactionId2", - "serviceName": "opbeans-ruby", - "skew": 0, - "span": Object { + "doc": Object { "parent": Object { "id": "myTransactionId2", }, @@ -882,19 +922,55 @@ Object { "id": "myTransactionId2", }, }, - "timestamp": 1549324795824504, - }, - Object { - "childIds": Array [], "docType": "span", - "duration": 481, - "id": "mySpanIdB", - "name": "SELECT FROM products", - "offset": 2329, - "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", + "duration": 6161, + "id": "mySpanIdA", + "offset": 1200, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, + }, + "docType": "transaction", + "duration": 8634, + "id": "myTransactionId2", + "offset": 0, + "parent": undefined, + "parentId": "mySpanIdD", + "skew": 0, + }, + "parentId": "myTransactionId2", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { "parent": Object { "id": "mySpanIdA", }, @@ -921,19 +997,90 @@ Object { "id": "myTransactionId2", }, }, - "timestamp": 1549324795825633, - }, - Object { - "childIds": Array [], "docType": "span", - "duration": 532, - "id": "mySpanIdC", - "name": "SELECT FROM product", - "offset": 4601, + "duration": 481, + "id": "mySpanIdB", + "offset": 2329, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId2", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "span": Object { + "duration": Object { + "us": 6161, + }, + "id": "mySpanIdA", + "name": "Api::ProductsController#index", + }, + "timestamp": Object { + "us": 1549324795824504, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId2", + }, + }, + "docType": "span", + "duration": 6161, + "id": "mySpanIdA", + "offset": 1200, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, + }, + "docType": "transaction", + "duration": 8634, + "id": "myTransactionId2", + "offset": 0, + "parent": undefined, + "parentId": "mySpanIdD", + "skew": 0, + }, + "parentId": "myTransactionId2", + "skew": 0, + }, "parentId": "mySpanIdA", - "serviceName": "opbeans-ruby", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { "parent": Object { "id": "mySpanIdA", }, @@ -960,16 +1107,90 @@ Object { "id": "myTransactionId2", }, }, - "timestamp": 1549324795827905, + "docType": "span", + "duration": 532, + "id": "mySpanIdC", + "offset": 4601, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "myTransactionId2", + }, + "processor": Object { + "event": "span", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "span": Object { + "duration": Object { + "us": 6161, + }, + "id": "mySpanIdA", + "name": "Api::ProductsController#index", + }, + "timestamp": Object { + "us": 1549324795824504, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "id": "myTransactionId2", + }, + }, + "docType": "span", + "duration": 6161, + "id": "mySpanIdA", + "offset": 1200, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "mySpanIdD", + }, + "processor": Object { + "event": "transaction", + }, + "service": Object { + "name": "opbeans-ruby", + }, + "timestamp": Object { + "us": 1549324795823304, + }, + "trace": Object { + "id": "myTraceId", + }, + "transaction": Object { + "duration": Object { + "us": 8634, + }, + "id": "myTransactionId2", + "marks": Object { + "agent": Object { + "domComplete": 383, + "domInteractive": 382, + "timeToFirstByte": 14, + }, + }, + "name": "Api::ProductsController#index", + }, + }, + "docType": "transaction", + "duration": 8634, + "id": "myTransactionId2", + "offset": 0, + "parent": undefined, + "parentId": "mySpanIdD", + "skew": 0, + }, + "parentId": "myTransactionId2", + "skew": 0, + }, + "parentId": "mySpanIdA", + "skew": 0, }, ], - "serviceColors": Object { - "opbeans-ruby": "#3185fc", - }, - "services": Array [ - "opbeans-ruby", - ], - "traceRoot": Object { + "rootTransaction": Object { "processor": Object { "event": "transaction", }, @@ -990,30 +1211,61 @@ Object { "name": "GET /api", }, }, - "traceRootDuration": 49660, + "serviceColors": Object { + "opbeans-ruby": "#3185fc", + }, } `; exports[`waterfall_helpers getWaterfallItems should handle cyclic references 1`] = ` Array [ Object { - "childIds": Array [ - "a", - ], + "doc": Object { + "timestamp": Object { + "us": 10, + }, + "transaction": Object { + "id": "a", + }, + }, + "docType": "transaction", "id": "a", "offset": 0, + "parent": undefined, "skew": 0, - "timestamp": 10, }, Object { - "childIds": Array [ - "a", - ], - "id": "a", + "doc": Object { + "parent": Object { + "id": "a", + }, + "span": Object { + "id": "b", + }, + "timestamp": Object { + "us": 20, + }, + }, + "docType": "span", + "id": "b", "offset": 10, + "parent": Object { + "doc": Object { + "timestamp": Object { + "us": 10, + }, + "transaction": Object { + "id": "a", + }, + }, + "docType": "transaction", + "id": "a", + "offset": 0, + "parent": undefined, + "skew": 0, + }, "parentId": "a", - "skew": undefined, - "timestamp": 20, + "skew": 0, }, ] `; @@ -1021,89 +1273,280 @@ Array [ exports[`waterfall_helpers getWaterfallItems should order items correctly 1`] = ` Array [ Object { - "childIds": Array [ - "b2", - "b", - ], + "doc": Object { + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736366000, + }, + "transaction": Object { + "id": "a", + "name": "APIRestController#products", + }, + }, "docType": "transaction", "duration": 9480, - "errorCount": 0, "id": "a", - "name": "APIRestController#products", "offset": 0, - "serviceName": "opbeans-java", + "parent": undefined, "skew": 0, - "timestamp": 1536763736366000, - "transaction": Object {}, }, Object { - "childIds": Array [], + "doc": Object { + "parent": Object { + "id": "a", + }, + "service": Object { + "name": "opbeans-java", + }, + "span": Object { + "id": "b2", + "name": "GET [0:0:0:0:0:0:0:1]", + }, + "timestamp": Object { + "us": 1536763736367000, + }, + "transaction": Object { + "id": "a", + }, + }, "docType": "span", "duration": 4694, "id": "b2", - "name": "GET [0:0:0:0:0:0:0:1]", "offset": 1000, + "parent": Object { + "doc": Object { + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736366000, + }, + "transaction": Object { + "id": "a", + "name": "APIRestController#products", + }, + }, + "docType": "transaction", + "duration": 9480, + "id": "a", + "offset": 0, + "parent": undefined, + "skew": 0, + }, "parentId": "a", - "serviceName": "opbeans-java", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { + "parent": Object { + "id": "a", + }, + "service": Object { + "name": "opbeans-java", + }, + "span": Object { + "id": "b", + "name": "GET [0:0:0:0:0:0:0:1]", + }, + "timestamp": Object { + "us": 1536763736368000, + }, "transaction": Object { "id": "a", }, }, - "timestamp": 1536763736367000, - }, - Object { - "childIds": Array [ - "c", - ], "docType": "span", "duration": 4694, "id": "b", - "name": "GET [0:0:0:0:0:0:0:1]", "offset": 2000, + "parent": Object { + "doc": Object { + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736366000, + }, + "transaction": Object { + "id": "a", + "name": "APIRestController#products", + }, + }, + "docType": "transaction", + "duration": 9480, + "id": "a", + "offset": 0, + "parent": undefined, + "skew": 0, + }, "parentId": "a", - "serviceName": "opbeans-java", "skew": 0, - "span": Object { + }, + Object { + "doc": Object { + "parent": Object { + "id": "b", + }, + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736369000, + }, "transaction": Object { - "id": "a", + "id": "c", + "name": "APIRestController#productsRemote", }, }, - "timestamp": 1536763736368000, - }, - Object { - "childIds": Array [ - "d", - ], "docType": "transaction", "duration": 3581, - "errorCount": 0, "id": "c", - "name": "APIRestController#productsRemote", "offset": 3000, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "a", + }, + "service": Object { + "name": "opbeans-java", + }, + "span": Object { + "id": "b", + "name": "GET [0:0:0:0:0:0:0:1]", + }, + "timestamp": Object { + "us": 1536763736368000, + }, + "transaction": Object { + "id": "a", + }, + }, + "docType": "span", + "duration": 4694, + "id": "b", + "offset": 2000, + "parent": Object { + "doc": Object { + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736366000, + }, + "transaction": Object { + "id": "a", + "name": "APIRestController#products", + }, + }, + "docType": "transaction", + "duration": 9480, + "id": "a", + "offset": 0, + "parent": undefined, + "skew": 0, + }, + "parentId": "a", + "skew": 0, + }, "parentId": "b", - "serviceName": "opbeans-java", "skew": 0, - "timestamp": 1536763736369000, - "transaction": Object {}, }, Object { - "childIds": Array [], + "doc": Object { + "parent": Object { + "id": "c", + }, + "service": Object { + "name": "opbeans-java", + }, + "span": Object { + "id": "d", + "name": "SELECT", + }, + "timestamp": Object { + "us": 1536763736371000, + }, + "transaction": Object { + "id": "c", + }, + }, "docType": "span", "duration": 210, "id": "d", - "name": "SELECT", "offset": 5000, - "parentId": "c", - "serviceName": "opbeans-java", - "skew": 0, - "span": Object { - "transaction": Object { - "id": "c", + "parent": Object { + "doc": Object { + "parent": Object { + "id": "b", + }, + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736369000, + }, + "transaction": Object { + "id": "c", + "name": "APIRestController#productsRemote", + }, + }, + "docType": "transaction", + "duration": 3581, + "id": "c", + "offset": 3000, + "parent": Object { + "doc": Object { + "parent": Object { + "id": "a", + }, + "service": Object { + "name": "opbeans-java", + }, + "span": Object { + "id": "b", + "name": "GET [0:0:0:0:0:0:0:1]", + }, + "timestamp": Object { + "us": 1536763736368000, + }, + "transaction": Object { + "id": "a", + }, + }, + "docType": "span", + "duration": 4694, + "id": "b", + "offset": 2000, + "parent": Object { + "doc": Object { + "service": Object { + "name": "opbeans-java", + }, + "timestamp": Object { + "us": 1536763736366000, + }, + "transaction": Object { + "id": "a", + "name": "APIRestController#products", + }, + }, + "docType": "transaction", + "duration": 9480, + "id": "a", + "offset": 0, + "parent": undefined, + "skew": 0, + }, + "parentId": "a", + "skew": 0, }, + "parentId": "b", + "skew": 0, }, - "timestamp": 1536763736371000, + "parentId": "c", + "skew": 0, }, ] `; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts index 6166515fd9d38d..426842bc02f510 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts @@ -11,8 +11,10 @@ import { getClockSkew, getOrderedWaterfallItems, getWaterfall, - IWaterfallItem + IWaterfallItem, + IWaterfallTransaction } from './waterfall_helpers'; +import { APMError } from '../../../../../../../../typings/es_schemas/ui/APMError'; describe('waterfall_helpers', () => { describe('getWaterfall', () => { @@ -80,7 +82,7 @@ describe('waterfall_helpers', () => { }, timestamp: { us: 1549324795785760 } } as Span, - { + ({ parent: { id: 'mySpanIdD' }, processor: { event: 'transaction' }, trace: { id: 'myTraceId' }, @@ -88,10 +90,36 @@ describe('waterfall_helpers', () => { transaction: { duration: { us: 8634 }, name: 'Api::ProductsController#index', - id: 'myTransactionId2' + id: 'myTransactionId2', + marks: { + agent: { + domInteractive: 382, + domComplete: 383, + timeToFirstByte: 14 + } + } }, timestamp: { us: 1549324795823304 } - } as Transaction + } as unknown) as Transaction, + ({ + processor: { event: 'error' }, + parent: { id: 'myTransactionId1' }, + timestamp: { us: 1549324795810000 }, + trace: { id: 'myTraceId' }, + transaction: { id: 'myTransactionId1' }, + error: { + id: 'error1', + grouping_key: 'errorGroupingKey1', + log: { + message: 'error message' + } + }, + service: { name: 'opbeans-ruby' }, + agent: { + name: 'ruby', + version: '2' + } + } as unknown) as APMError ]; it('should return full waterfall', () => { @@ -107,8 +135,10 @@ describe('waterfall_helpers', () => { }, entryTransactionId ); - expect(waterfall.orderedItems.length).toBe(6); - expect(waterfall.orderedItems[0].id).toBe('myTransactionId1'); + + expect(waterfall.items.length).toBe(7); + expect(waterfall.items[0].id).toBe('myTransactionId1'); + expect(waterfall.errorsCount).toEqual(1); expect(waterfall).toMatchSnapshot(); }); @@ -125,26 +155,11 @@ describe('waterfall_helpers', () => { }, entryTransactionId ); - expect(waterfall.orderedItems.length).toBe(4); - expect(waterfall.orderedItems[0].id).toBe('myTransactionId2'); - expect(waterfall).toMatchSnapshot(); - }); - it('getTransactionById', () => { - const entryTransactionId = 'myTransactionId1'; - const errorsPerTransaction = { - myTransactionId1: 2, - myTransactionId2: 3 - }; - const waterfall = getWaterfall( - { - trace: { items: hits, exceedsMax: false }, - errorsPerTransaction - }, - entryTransactionId - ); - const transaction = waterfall.getTransactionById('myTransactionId2'); - expect(transaction!.transaction.id).toBe('myTransactionId2'); + expect(waterfall.items.length).toBe(4); + expect(waterfall.items[0].id).toBe('myTransactionId2'); + expect(waterfall.errorsCount).toEqual(0); + expect(waterfall).toMatchSnapshot(); }); }); @@ -152,84 +167,102 @@ describe('waterfall_helpers', () => { it('should order items correctly', () => { const items: IWaterfallItem[] = [ { + docType: 'span', + doc: { + parent: { id: 'c' }, + service: { name: 'opbeans-java' }, + transaction: { + id: 'c' + }, + timestamp: { us: 1536763736371000 }, + span: { + id: 'd', + name: 'SELECT' + } + } as Span, id: 'd', parentId: 'c', - serviceName: 'opbeans-java', - name: 'SELECT', duration: 210, - timestamp: 1536763736371000, offset: 0, - skew: 0, + skew: 0 + }, + { docType: 'span', - span: { + doc: { + parent: { id: 'a' }, + service: { name: 'opbeans-java' }, transaction: { - id: 'c' + id: 'a' + }, + timestamp: { us: 1536763736368000 }, + span: { + id: 'b', + name: 'GET [0:0:0:0:0:0:0:1]' } - } as Span - }, - { + } as Span, id: 'b', parentId: 'a', - serviceName: 'opbeans-java', - name: 'GET [0:0:0:0:0:0:0:1]', duration: 4694, - timestamp: 1536763736368000, offset: 0, - skew: 0, + skew: 0 + }, + { docType: 'span', - span: { + doc: { + parent: { id: 'a' }, + service: { name: 'opbeans-java' }, transaction: { id: 'a' + }, + timestamp: { us: 1536763736367000 }, + span: { + id: 'b2', + name: 'GET [0:0:0:0:0:0:0:1]' } - } as Span - }, - { + } as Span, id: 'b2', parentId: 'a', - serviceName: 'opbeans-java', - name: 'GET [0:0:0:0:0:0:0:1]', duration: 4694, - timestamp: 1536763736367000, offset: 0, - skew: 0, - docType: 'span', - span: { - transaction: { - id: 'a' - } - } as Span + skew: 0 }, { + docType: 'transaction', + doc: { + parent: { id: 'b' }, + service: { name: 'opbeans-java' }, + timestamp: { us: 1536763736369000 }, + transaction: { id: 'c', name: 'APIRestController#productsRemote' } + } as Transaction, id: 'c', parentId: 'b', - serviceName: 'opbeans-java', - name: 'APIRestController#productsRemote', duration: 3581, - timestamp: 1536763736369000, offset: 0, - skew: 0, - docType: 'transaction', - transaction: {} as Transaction, - errorCount: 0 + skew: 0 }, { + docType: 'transaction', + doc: { + service: { name: 'opbeans-java' }, + timestamp: { us: 1536763736366000 }, + transaction: { + id: 'a', + name: 'APIRestController#products' + } + } as Transaction, id: 'a', - serviceName: 'opbeans-java', - name: 'APIRestController#products', duration: 9480, - timestamp: 1536763736366000, offset: 0, - skew: 0, - docType: 'transaction', - transaction: {} as Transaction, - errorCount: 0 + skew: 0 } ]; const childrenByParentId = groupBy(items, hit => hit.parentId ? hit.parentId : 'root' ); - const entryTransactionItem = childrenByParentId.root[0]; + const entryTransactionItem = childrenByParentId + .root[0] as IWaterfallTransaction; + expect( getOrderedWaterfallItems(childrenByParentId, entryTransactionItem) ).toMatchSnapshot(); @@ -237,13 +270,32 @@ describe('waterfall_helpers', () => { it('should handle cyclic references', () => { const items = [ - { id: 'a', timestamp: 10 } as IWaterfallItem, - { id: 'a', parentId: 'a', timestamp: 20 } as IWaterfallItem + { + docType: 'transaction', + id: 'a', + doc: ({ + transaction: { id: 'a' }, + timestamp: { us: 10 } + } as unknown) as Transaction + } as IWaterfallItem, + { + docType: 'span', + id: 'b', + parentId: 'a', + doc: ({ + span: { + id: 'b' + }, + parent: { id: 'a' }, + timestamp: { us: 20 } + } as unknown) as Span + } as IWaterfallItem ]; const childrenByParentId = groupBy(items, hit => hit.parentId ? hit.parentId : 'root' ); - const entryTransactionItem = childrenByParentId.root[0]; + const entryTransactionItem = childrenByParentId + .root[0] as IWaterfallTransaction; expect( getOrderedWaterfallItems(childrenByParentId, entryTransactionItem) ).toMatchSnapshot(); @@ -254,12 +306,17 @@ describe('waterfall_helpers', () => { it('should adjust when child starts before parent', () => { const child = { docType: 'transaction', - timestamp: 0, + doc: { + timestamp: { us: 0 } + }, duration: 50 } as IWaterfallItem; const parent = { - timestamp: 100, + docType: 'transaction', + doc: { + timestamp: { us: 100 } + }, duration: 100, skew: 5 } as IWaterfallItem; @@ -270,12 +327,17 @@ describe('waterfall_helpers', () => { it('should not adjust when child starts after parent has ended', () => { const child = { docType: 'transaction', - timestamp: 250, + doc: { + timestamp: { us: 250 } + }, duration: 50 } as IWaterfallItem; const parent = { - timestamp: 100, + docType: 'transaction', + doc: { + timestamp: { us: 100 } + }, duration: 100, skew: 5 } as IWaterfallItem; @@ -286,12 +348,17 @@ describe('waterfall_helpers', () => { it('should not adjust when child starts within parent duration', () => { const child = { docType: 'transaction', - timestamp: 150, + doc: { + timestamp: { us: 150 } + }, duration: 50 } as IWaterfallItem; const parent = { - timestamp: 100, + docType: 'transaction', + doc: { + timestamp: { us: 100 } + }, duration: 100, skew: 5 } as IWaterfallItem; @@ -305,7 +372,27 @@ describe('waterfall_helpers', () => { } as IWaterfallItem; const parent = { - timestamp: 100, + docType: 'span', + doc: { + timestamp: { us: 100 } + }, + duration: 100, + skew: 5 + } as IWaterfallItem; + + expect(getClockSkew(child, parent)).toBe(5); + }); + + it('should return parent skew for errors', () => { + const child = { + docType: 'error' + } as IWaterfallItem; + + const parent = { + docType: 'transaction', + doc: { + timestamp: { us: 100 } + }, duration: 100, skew: 5 } as IWaterfallItem; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts index 2a69c5f51173d5..1af6cddb3ba4a7 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -6,60 +6,52 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { + first, flatten, groupBy, - indexBy, + isEmpty, sortBy, + sum, uniq, - zipObject, - isEmpty, - first + zipObject } from 'lodash'; import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace'; +import { APMError } from '../../../../../../../../typings/es_schemas/ui/APMError'; import { Span } from '../../../../../../../../typings/es_schemas/ui/Span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction'; -interface IWaterfallIndex { - [key: string]: IWaterfallItem | undefined; -} - interface IWaterfallGroup { [key: string]: IWaterfallItem[]; } export interface IWaterfall { entryTransaction?: Transaction; - traceRoot?: Transaction; - traceRootDuration?: number; + rootTransaction?: Transaction; /** * Duration in us */ duration: number; - services: string[]; - orderedItems: IWaterfallItem[]; - itemsById: IWaterfallIndex; - getTransactionById: (id?: IWaterfallItem['id']) => Transaction | undefined; - errorCountByTransactionId: TraceAPIResponse['errorsPerTransaction']; + items: IWaterfallItem[]; + errorsPerTransaction: TraceAPIResponse['errorsPerTransaction']; + errorsCount: number; serviceColors: IServiceColors; } -interface IWaterfallItemBase { - id: string | number; +interface IWaterfallItemBase { + docType: U; + doc: T; + + id: string; + + parent?: IWaterfallItem; parentId?: string; - serviceName: string; - name: string; /** * Duration in us */ duration: number; - /** - * start timestamp in us - */ - timestamp: number; - /** * offset from first item in us */ @@ -69,53 +61,53 @@ interface IWaterfallItemBase { * skew from timestamp in us */ skew: number; - childIds?: Array; -} - -interface IWaterfallItemTransaction extends IWaterfallItemBase { - transaction: Transaction; - docType: 'transaction'; - errorCount: number; } -interface IWaterfallItemSpan extends IWaterfallItemBase { - span: Span; - docType: 'span'; -} +export type IWaterfallTransaction = IWaterfallItemBase< + Transaction, + 'transaction' +>; +export type IWaterfallSpan = IWaterfallItemBase; +export type IWaterfallError = IWaterfallItemBase; -export type IWaterfallItem = IWaterfallItemSpan | IWaterfallItemTransaction; +export type IWaterfallItem = + | IWaterfallTransaction + | IWaterfallSpan + | IWaterfallError; -function getTransactionItem( - transaction: Transaction, - errorsPerTransaction: TraceAPIResponse['errorsPerTransaction'] -): IWaterfallItemTransaction { +function getTransactionItem(transaction: Transaction): IWaterfallTransaction { return { + docType: 'transaction', + doc: transaction, id: transaction.transaction.id, - parentId: transaction.parent && transaction.parent.id, - serviceName: transaction.service.name, - name: transaction.transaction.name, + parentId: transaction.parent?.id, duration: transaction.transaction.duration.us, - timestamp: transaction.timestamp.us, offset: 0, - skew: 0, - docType: 'transaction', - transaction, - errorCount: errorsPerTransaction[transaction.transaction.id] || 0 + skew: 0 }; } -function getSpanItem(span: Span): IWaterfallItemSpan { +function getSpanItem(span: Span): IWaterfallSpan { return { + docType: 'span', + doc: span, id: span.span.id, - parentId: span.parent && span.parent.id, - serviceName: span.service.name, - name: span.span.name, + parentId: span.parent?.id, duration: span.span.duration.us, - timestamp: span.timestamp.us, + offset: 0, + skew: 0 + }; +} + +function getErrorItem(error: APMError): IWaterfallError { + return { + docType: 'error', + doc: error, + id: error.error.id, + parentId: error.parent?.id, offset: 0, skew: 0, - docType: 'span', - span + duration: 0 }; } @@ -126,18 +118,17 @@ export function getClockSkew( if (!parentItem) { return 0; } - switch (item.docType) { - // don't calculate skew for spans. Just use parent's skew + // don't calculate skew for spans and errors. Just use parent's skew + case 'error': case 'span': return parentItem.skew; - // transaction is the inital entry in a service. Calculate skew for this, and it will be propogated to all child spans case 'transaction': { - const parentStart = parentItem.timestamp + parentItem.skew; + const parentStart = parentItem.doc.timestamp.us + parentItem.skew; // determine if child starts before the parent - const offsetStart = parentStart - item.timestamp; + const offsetStart = parentStart - item.doc.timestamp.us; if (offsetStart > 0) { const latency = Math.max(parentItem.duration - item.duration, 0) / 2; return offsetStart + latency; @@ -151,9 +142,14 @@ export function getClockSkew( export function getOrderedWaterfallItems( childrenByParentId: IWaterfallGroup, - entryTransactionItem: IWaterfallItem + entryWaterfallTransaction?: IWaterfallTransaction ) { + if (!entryWaterfallTransaction) { + return []; + } + const entryTimestamp = entryWaterfallTransaction.doc.timestamp.us; const visitedWaterfallItemSet = new Set(); + function getSortedChildren( item: IWaterfallItem, parentItem?: IWaterfallItem @@ -162,10 +158,16 @@ export function getOrderedWaterfallItems( return []; } visitedWaterfallItemSet.add(item); - const children = sortBy(childrenByParentId[item.id] || [], 'timestamp'); - item.childIds = children.map(child => child.id); - item.offset = item.timestamp - entryTransactionItem.timestamp; + const children = sortBy( + childrenByParentId[item.id] || [], + 'doc.timestamp.us' + ); + + item.parent = parentItem; + // get offset from the beginning of trace + item.offset = item.doc.timestamp.us - entryTimestamp; + // move the item to the right if it starts before its parent item.skew = getClockSkew(item, parentItem); const deepChildren = flatten( @@ -174,24 +176,21 @@ export function getOrderedWaterfallItems( return [item, ...deepChildren]; } - return getSortedChildren(entryTransactionItem); + return getSortedChildren(entryWaterfallTransaction); } -function getTraceRoot(childrenByParentId: IWaterfallGroup) { +function getRootTransaction(childrenByParentId: IWaterfallGroup) { const item = first(childrenByParentId.root); if (item && item.docType === 'transaction') { - return item.transaction; + return item.doc; } } -function getServices(items: IWaterfallItem[]) { - const serviceNames = items.map(item => item.serviceName); - return uniq(serviceNames); -} - export type IServiceColors = Record; -function getServiceColors(services: string[]) { +function getServiceColors(waterfallItems: IWaterfallItem[]) { + const services = uniq(waterfallItems.map(item => item.doc.service.name)); + const assignedColors = [ theme.euiColorVis1, theme.euiColorVis0, @@ -205,30 +204,35 @@ function getServiceColors(services: string[]) { return zipObject(services, assignedColors) as IServiceColors; } -function getDuration(items: IWaterfallItem[]) { - if (items.length === 0) { - return 0; - } - const timestampStart = items[0].timestamp; - const timestampEnd = Math.max( - ...items.map(item => item.timestamp + item.duration + item.skew) +const getWaterfallDuration = (waterfallItems: IWaterfallItem[]) => + Math.max( + ...waterfallItems.map(item => item.offset + item.skew + item.duration), + 0 ); - return timestampEnd - timestampStart; -} -function createGetTransactionById(itemsById: IWaterfallIndex) { - return (id?: IWaterfallItem['id']) => { - if (!id) { - return undefined; +const getWaterfallItems = (items: TraceAPIResponse['trace']['items']) => + items.map(item => { + const docType = item.processor.event; + switch (docType) { + case 'span': + return getSpanItem(item as Span); + case 'transaction': + return getTransactionItem(item as Transaction); + case 'error': + return getErrorItem(item as APMError); } + }); - const item = itemsById[id]; - const isTransaction = item?.docType === 'transaction'; - if (isTransaction) { - return (item as IWaterfallItemTransaction).transaction; - } - }; -} +const getChildrenGroupedByParentId = (waterfallItems: IWaterfallItem[]) => + groupBy(waterfallItems, item => (item.parentId ? item.parentId : 'root')); + +const getEntryWaterfallTransaction = ( + entryTransactionId: string, + waterfallItems: IWaterfallItem[] +): IWaterfallTransaction | undefined => + waterfallItems.find( + item => item.docType === 'transaction' && item.id === entryTransactionId + ) as IWaterfallTransaction; export function getWaterfall( { trace, errorsPerTransaction }: TraceAPIResponse, @@ -236,59 +240,41 @@ export function getWaterfall( ): IWaterfall { if (isEmpty(trace.items) || !entryTransactionId) { return { - services: [], duration: 0, - orderedItems: [], - itemsById: {}, - getTransactionById: () => undefined, - errorCountByTransactionId: errorsPerTransaction, + items: [], + errorsPerTransaction, + errorsCount: sum(Object.values(errorsPerTransaction)), serviceColors: {} }; } - const waterfallItems = trace.items.map(traceItem => { - const docType = traceItem.processor.event; - switch (docType) { - case 'span': - return getSpanItem(traceItem as Span); - case 'transaction': - return getTransactionItem( - traceItem as Transaction, - errorsPerTransaction - ); - } - }); + const waterfallItems: IWaterfallItem[] = getWaterfallItems(trace.items); + + const childrenByParentId = getChildrenGroupedByParentId(waterfallItems); - const childrenByParentId = groupBy(waterfallItems, item => - item.parentId ? item.parentId : 'root' + const entryWaterfallTransaction = getEntryWaterfallTransaction( + entryTransactionId, + waterfallItems ); - const entryTransactionItem = waterfallItems.find( - waterfallItem => - waterfallItem.docType === 'transaction' && - waterfallItem.id === entryTransactionId + + const items = getOrderedWaterfallItems( + childrenByParentId, + entryWaterfallTransaction ); - const itemsById: IWaterfallIndex = indexBy(waterfallItems, 'id'); - const orderedItems = entryTransactionItem - ? getOrderedWaterfallItems(childrenByParentId, entryTransactionItem) - : []; - const traceRoot = getTraceRoot(childrenByParentId); - const duration = getDuration(orderedItems); - const traceRootDuration = traceRoot && traceRoot.transaction.duration.us; - const services = getServices(orderedItems); - const getTransactionById = createGetTransactionById(itemsById); - const serviceColors = getServiceColors(services); - const entryTransaction = getTransactionById(entryTransactionId); + + const rootTransaction = getRootTransaction(childrenByParentId); + const duration = getWaterfallDuration(items); + const serviceColors = getServiceColors(items); + + const entryTransaction = entryWaterfallTransaction?.doc; return { entryTransaction, - traceRoot, - traceRootDuration, + rootTransaction, duration, - services, - orderedItems, - itemsById, - getTransactionById, - errorCountByTransactionId: errorsPerTransaction, + items, + errorsPerTransaction, + errorsCount: items.filter(item => item.docType === 'error').length, serviceColors }; } diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/get_agent_marks.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/get_agent_marks.ts deleted file mode 100644 index af76451db68b7b..00000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/get_agent_marks.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { sortBy } from 'lodash'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; - -export interface AgentMark { - name: string; - us: number; -} - -export function getAgentMarks(transaction: Transaction): AgentMark[] { - if (!(transaction.transaction.marks && transaction.transaction.marks.agent)) { - return []; - } - - return sortBy( - Object.entries(transaction.transaction.marks.agent).map(([name, ms]) => ({ - name, - us: ms * 1000 - })), - 'us' - ); -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx index 2f34cc86c5cfc2..77be5c999f7c36 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx @@ -6,16 +6,13 @@ import { Location } from 'history'; import React from 'react'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; import { IUrlParams } from '../../../../../context/UrlParamsContext/types'; -import { getAgentMarks } from './get_agent_marks'; import { ServiceLegends } from './ServiceLegends'; import { Waterfall } from './Waterfall'; import { IWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; interface Props { urlParams: IUrlParams; - transaction: Transaction; location: Location; waterfall: IWaterfall; exceedsMax: boolean; @@ -24,11 +21,9 @@ interface Props { export function WaterfallContainer({ location, urlParams, - transaction, waterfall, exceedsMax }: Props) { - const agentMarks = getAgentMarks(transaction); if (!waterfall) { return null; } @@ -37,10 +32,8 @@ export function WaterfallContainer({
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx new file mode 100644 index 00000000000000..62b5f7834d3a9d --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { expectTextsInDocument } from '../../../../../utils/testHelpers'; +import { ErrorCount } from '../ErrorCount'; + +describe('ErrorCount', () => { + it('shows singular error message', () => { + const component = render(); + expectTextsInDocument(component, ['1 Error']); + }); + it('shows plural error message', () => { + const component = render(); + expectTextsInDocument(component, ['2 Errors']); + }); + it('prevents click propagation', () => { + const mock = jest.fn(); + const { getByText } = render( + + ); + fireEvent( + getByText('1 Error'), + new MouseEvent('click', { + bubbles: true, + cancelable: true + }) + ); + expect(mock).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx index b56370a59c8e22..6dcab6c6b97c16 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx @@ -5,30 +5,29 @@ */ import { + EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, + EuiPagination, EuiPanel, EuiSpacer, - EuiEmptyPrompt, - EuiTitle, - EuiPagination + EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Location } from 'history'; -import React, { useState, useEffect } from 'react'; -import { sum } from 'lodash'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; -import { IUrlParams } from '../../../../context/UrlParamsContext/types'; -import { TransactionActionMenu } from '../../../shared/TransactionActionMenu/TransactionActionMenu'; -import { TransactionTabs } from './TransactionTabs'; -import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers'; -import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; -import { TransactionSummary } from '../../../shared/Summary/TransactionSummary'; import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform'; +import { IUrlParams } from '../../../../context/UrlParamsContext/types'; +import { px, units } from '../../../../style/variables'; import { history } from '../../../../utils/history'; import { fromQuery, toQuery } from '../../../shared/Links/url_helpers'; +import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; +import { TransactionSummary } from '../../../shared/Summary/TransactionSummary'; +import { TransactionActionMenu } from '../../../shared/TransactionActionMenu/TransactionActionMenu'; import { MaybeViewTraceLink } from './MaybeViewTraceLink'; -import { units, px } from '../../../../style/variables'; +import { TransactionTabs } from './TransactionTabs'; +import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers'; const PaginationContainer = styled.div` margin-left: ${px(units.quarter)}; @@ -140,8 +139,8 @@ export const WaterfallWithSummmary: React.FC = ({ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx index 0312e94d7ee192..eba59f6e3ce44f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx @@ -18,7 +18,7 @@ interface Props extends EuiLinkAnchorProps { children?: React.ReactNode; } -export type APMLinkExtendProps = Omit; +export type APMLinkExtendProps = Omit; export const PERSISTENT_APM_PARAMS = [ 'kuery', diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx index 4b6355034f16ae..99d8a0790a816e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx @@ -31,11 +31,15 @@ describe('SpanMetadata', () => { name: 'opbeans-java' }, span: { - id: '7efbc7056b746fcb' + id: '7efbc7056b746fcb', + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } + } } } as unknown) as Span; const output = render(, renderOptions); - expectTextsInDocument(output, ['Service', 'Agent']); + expectTextsInDocument(output, ['Service', 'Agent', 'Message']); }); }); describe('when a span is presented', () => { @@ -55,11 +59,15 @@ describe('SpanMetadata', () => { response: { status_code: 200 } }, subtype: 'http', - type: 'external' + type: 'external', + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } + } } } as unknown) as Span; const output = render(, renderOptions); - expectTextsInDocument(output, ['Service', 'Agent', 'Span']); + expectTextsInDocument(output, ['Service', 'Agent', 'Span', 'Message']); }); }); describe('when there is no id inside span', () => { @@ -83,7 +91,7 @@ describe('SpanMetadata', () => { } as unknown) as Span; const output = render(, renderOptions); expectTextsInDocument(output, ['Service', 'Agent']); - expectTextsNotInDocument(output, ['Span']); + expectTextsNotInDocument(output, ['Span', 'Message']); }); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts index 7012bbcc8fceaf..5a83a9bf4ef9e1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts @@ -11,7 +11,8 @@ import { SPAN, LABELS, TRANSACTION, - TRACE + TRACE, + MESSAGE_SPAN } from '../sections'; export const SPAN_METADATA_SECTIONS: Section[] = [ @@ -20,5 +21,6 @@ export const SPAN_METADATA_SECTIONS: Section[] = [ TRANSACTION, TRACE, SERVICE, + MESSAGE_SPAN, AGENT ]; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx index 1fcb093fa03544..93e87e884ea766 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx @@ -35,6 +35,10 @@ function getTransaction() { notIncluded: 'transaction not included value', custom: { someKey: 'custom value' + }, + message: { + age: { ms: 1577958057123 }, + queue: { name: 'queue name' } } } } as unknown) as Transaction; @@ -59,7 +63,8 @@ describe('TransactionMetadata', () => { 'Agent', 'URL', 'User', - 'Custom' + 'Custom', + 'Message' ]); }); @@ -81,7 +86,9 @@ describe('TransactionMetadata', () => { 'agent.someKey', 'url.someKey', 'user.someKey', - 'transaction.custom.someKey' + 'transaction.custom.someKey', + 'transaction.message.age.ms', + 'transaction.message.queue.name' ]); // excluded keys @@ -109,7 +116,9 @@ describe('TransactionMetadata', () => { 'agent value', 'url value', 'user value', - 'custom value' + 'custom value', + '1577958057123', + 'queue name' ]); // excluded values @@ -138,7 +147,8 @@ describe('TransactionMetadata', () => { 'Process', 'Agent', 'URL', - 'Custom' + 'Custom', + 'Message' ]); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts index 6b30c82bc35a06..18751efc6e1c1f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts @@ -18,7 +18,8 @@ import { PAGE, USER, USER_AGENT, - CUSTOM_TRANSACTION + CUSTOM_TRANSACTION, + MESSAGE_TRANSACTION } from '../sections'; export const TRANSACTION_METADATA_SECTIONS: Section[] = [ @@ -29,6 +30,7 @@ export const TRANSACTION_METADATA_SECTIONS: Section[] = [ CONTAINER, SERVICE, PROCESS, + MESSAGE_TRANSACTION, AGENT, URL, { ...PAGE, key: 'transaction.page' }, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts index 403663ce2095a1..ac8e9559357e3c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts @@ -136,3 +136,20 @@ export const CUSTOM_TRANSACTION: Section = { key: 'transaction.custom', label: customLabel }; + +const messageLabel = i18n.translate( + 'xpack.apm.metadataTable.section.messageLabel', + { + defaultMessage: 'Message' + } +); + +export const MESSAGE_TRANSACTION: Section = { + key: 'transaction.message', + label: messageLabel +}; + +export const MESSAGE_SPAN: Section = { + key: 'span.message', + label: messageLabel +}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx similarity index 51% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx index 964debbedb2e42..7558f002c0afc4 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx @@ -6,28 +6,25 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; +import { EuiBadge } from '@elastic/eui'; +import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json'; import { px } from '../../../../public/style/variables'; -import { ErrorCountBadge } from '../../app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge'; import { units } from '../../../style/variables'; interface Props { count: number; } -const Badge = styled(ErrorCountBadge)` +const Badge = styled(EuiBadge)` margin-top: ${px(units.eighth)}; `; -const ErrorCountSummaryItem = ({ count }: Props) => { - return ( - - {i18n.translate('xpack.apm.transactionDetails.errorCount', { - defaultMessage: - '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}', - values: { errorCount: count } - })} - - ); -}; - -export { ErrorCountSummaryItem }; +export const ErrorCountSummaryItemBadge = ({ count }: Props) => ( + + {i18n.translate('xpack.apm.transactionDetails.errorCount', { + defaultMessage: + '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}', + values: { errorCount: count } + })} + +); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx index 8b7380a18edc39..51da61cd7c1a6e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx @@ -8,7 +8,7 @@ import { Transaction } from '../../../../typings/es_schemas/ui/Transaction'; import { Summary } from './'; import { TimestampTooltip } from '../TimestampTooltip'; import { DurationSummaryItem } from './DurationSummaryItem'; -import { ErrorCountSummaryItem } from './ErrorCountSummaryItem'; +import { ErrorCountSummaryItemBadge } from './ErrorCountSummaryItemBadge'; import { isRumAgentName } from '../../../../common/agent_name'; import { HttpInfoSummaryItem } from './HttpInfoSummaryItem'; import { TransactionResultSummaryItem } from './TransactionResultSummaryItem'; @@ -54,7 +54,7 @@ const TransactionSummary = ({ parentType="trace" />, getTransactionResultSummaryItem(transaction), - errorCount ? : null, + errorCount ? : null, transaction.user_agent ? ( ) : null diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx new file mode 100644 index 00000000000000..33f5752b6389b0 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ErrorCountSummaryItemBadge } from '../ErrorCountSummaryItemBadge'; +import { render } from '@testing-library/react'; +import { expectTextsInDocument } from '../../../../utils/testHelpers'; + +describe('ErrorCountSummaryItemBadge', () => { + it('shows singular error message', () => { + const component = render(); + expectTextsInDocument(component, ['1 Error']); + }); + it('shows plural error message', () => { + const component = render(); + expectTextsInDocument(component, ['2 Errors']); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js index 848c975942ff64..99eb17386f847b 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import styled from 'styled-components'; -import Legend from '../Legend'; +import { Legend } from '../Legend'; import { unit, units, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap index c46cbbbcccc0b8..557751a0f02268 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap @@ -2725,11 +2725,14 @@ Array [ @@ -2763,11 +2766,14 @@ Array [ @@ -2794,11 +2800,14 @@ Array [ @@ -5167,11 +5176,14 @@ Array [ Avg. @@ -5210,11 +5222,14 @@ Array [ 95th @@ -5253,11 +5268,14 @@ Array [ 99th @@ -5886,11 +5904,14 @@ Array [ @@ -5924,11 +5945,14 @@ Array [ @@ -5955,11 +5979,14 @@ Array [ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js deleted file mode 100644 index 601482430b00f5..00000000000000 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent } from 'react'; -import styled from 'styled-components'; -import { units, px, fontSizes } from '../../../../style/variables'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; - -const Container = styled.div` - display: flex; - align-items: center; - font-size: ${props => props.fontSize}; - color: ${theme.euiColorDarkShade}; - cursor: ${props => (props.clickable ? 'pointer' : 'initial')}; - opacity: ${props => (props.disabled ? 0.4 : 1)}; - user-select: none; -`; - -export const Indicator = styled.span` - width: ${props => px(props.radius)}; - height: ${props => px(props.radius)}; - margin-right: ${props => px(props.radius / 2)}; - background: ${props => props.color}; - border-radius: 100%; -`; - -export default class Legend extends PureComponent { - render() { - const { - onClick, - text, - color = theme.euiColorVis1, - fontSize = fontSizes.small, - radius = units.minus - 1, - disabled = false, - clickable = false, - indicator, - ...rest - } = this.props; - return ( - - {indicator ? indicator() : } - {text} - - ); - } -} diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx new file mode 100644 index 00000000000000..436b020bc9eba7 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import React from 'react'; +import styled from 'styled-components'; +import { fontSizes, px, units } from '../../../../style/variables'; + +export enum Shape { + circle = 'circle', + square = 'square' +} + +interface ContainerProps { + onClick: (e: Event) => void; + fontSize?: string; + clickable: boolean; + disabled: boolean; +} +const Container = styled.div` + display: flex; + align-items: center; + font-size: ${props => props.fontSize}; + color: ${theme.euiColorDarkShade}; + cursor: ${props => (props.clickable ? 'pointer' : 'initial')}; + opacity: ${props => (props.disabled ? 0.4 : 1)}; + user-select: none; +`; + +interface IndicatorProps { + radius: number; + color: string; + shape: Shape; + withMargin: boolean; +} +export const Indicator = styled.span` + width: ${props => px(props.radius)}; + height: ${props => px(props.radius)}; + margin-right: ${props => (props.withMargin ? px(props.radius / 2) : 0)}; + background: ${props => props.color}; + border-radius: ${props => { + return props.shape === Shape.circle ? '100%' : '0'; + }}; +`; + +interface Props { + onClick?: any; + text?: string; + color?: string; + fontSize?: string; + radius?: number; + disabled?: boolean; + clickable?: boolean; + shape?: Shape; + indicator?: () => React.ReactNode; +} + +export const Legend: React.FC = ({ + onClick, + text, + color = theme.euiColorVis1, + fontSize = fontSizes.small, + radius = units.minus - 1, + disabled = false, + clickable = false, + shape = Shape.circle, + indicator, + ...rest +}) => { + return ( + + {indicator ? ( + indicator() + ) : ( + + )} + {text} + + ); +}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx similarity index 56% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js rename to x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx index 8ee23d61fe0eb6..ffdbfe6cce7ec8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/AgentMarker.js +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import PropTypes from 'prop-types'; import { EuiToolTip } from '@elastic/eui'; -import Legend from '../Legend'; -import { units, px } from '../../../../style/variables'; -import styled from 'styled-components'; -import { asDuration } from '../../../../utils/formatters'; import theme from '@elastic/eui/dist/eui_theme_light.json'; +import React from 'react'; +import styled from 'styled-components'; +import { px, units } from '../../../../../style/variables'; +import { asDuration } from '../../../../../utils/formatters'; +import { Legend } from '../../Legend'; +import { AgentMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks'; const NameContainer = styled.div` border-bottom: 1px solid ${theme.euiColorMediumShade}; @@ -23,33 +23,25 @@ const TimeContainer = styled.div` padding-top: ${px(units.half)}; `; -export default function AgentMarker({ agentMark, x }) { - const legendWidth = 11; +interface Props { + mark: AgentMark; +} + +export const AgentMarker: React.FC = ({ mark }) => { return ( -
+ <> - {agentMark.name} - {asDuration(agentMark.us)} + {mark.id} + {asDuration(mark.offset)}
} > -
+ ); -} - -AgentMarker.propTypes = { - agentMark: PropTypes.object.isRequired, - x: PropTypes.number.isRequired }; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx new file mode 100644 index 00000000000000..51368a4fb946d5 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiPopover, EuiText } from '@elastic/eui'; +import theme from '@elastic/eui/dist/eui_theme_light.json'; +import React, { useState } from 'react'; +import styled from 'styled-components'; +import { + TRACE_ID, + TRANSACTION_ID +} from '../../../../../../common/elasticsearch_fieldnames'; +import { useUrlParams } from '../../../../../hooks/useUrlParams'; +import { px, unit, units } from '../../../../../style/variables'; +import { asDuration } from '../../../../../utils/formatters'; +import { ErrorMark } from '../../../../app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks'; +import { ErrorDetailLink } from '../../../Links/apm/ErrorDetailLink'; +import { Legend, Shape } from '../../Legend'; + +interface Props { + mark: ErrorMark; +} + +const Popover = styled.div` + max-width: ${px(280)}; +`; + +const TimeLegend = styled(Legend)` + margin-bottom: ${px(unit)}; +`; + +const ErrorLink = styled(ErrorDetailLink)` + display: block; + margin: ${px(units.half)} 0 ${px(units.half)} 0; +`; + +const Button = styled(Legend)` + height: 20px; + display: flex; + align-items: flex-end; +`; + +export const ErrorMarker: React.FC = ({ mark }) => { + const { urlParams } = useUrlParams(); + const [isPopoverOpen, showPopover] = useState(false); + + const togglePopover = () => showPopover(!isPopoverOpen); + + const button = ( +